From bdfd30955d2c19b14cc2db4f45e753d1be3c558b Mon Sep 17 00:00:00 2001
From: Faizan Naeem
Date: Fri, 28 Aug 2026 14:14:55 +0500
Subject: [PATCH 1/9] feat(media): add filename and readiness filters to shared
listing (#126)
* Added Search filters,Added file names,Implemented CLI,MCP and HTTP projections
* Fixed the issue of import in the file named mcp.py
* Fixed the same import issue in the file named ports.py
* Fixes of media_service.py and test_frontend.py as proposed by the Reviewer
* Missed the service variable that was defined at the top of the function
* align workspace actions, cursor scope, and ready-only frontend listing
* Minor changes
* fix(media): preserve filtered pagination and matching
---------
Co-authored-by: Talha
---
src/vidxp/api_routes/media.py | 16 +++-
src/vidxp/api_routes/platform.py | 16 +++-
src/vidxp/application_models.py | 9 ++
src/vidxp/cli_commands/media.py | 16 +++-
src/vidxp/control_plane.py | 5 +-
src/vidxp/frontend.py | 6 +-
src/vidxp/infrastructure/sql_catalog.py | 77 ++++++++++++++---
src/vidxp/mcp.py | 25 +++++-
src/vidxp/media_service.py | 23 +++++-
src/vidxp/ports.py | 10 ++-
tests/test_api.py | 51 ++++++++++++
tests/test_cli.py | 19 +++++
tests/test_control_plane.py | 59 +++++++++++++
tests/test_frontend.py | 8 +-
tests/test_mcp.py | 28 +++++++
tests/test_media_catalog.py | 105 ++++++++++++++++++++++++
tests/test_media_services.py | 62 +++++++++++++-
17 files changed, 508 insertions(+), 27 deletions(-)
diff --git a/src/vidxp/api_routes/media.py b/src/vidxp/api_routes/media.py
index 0122bd22..504af0f7 100644
--- a/src/vidxp/api_routes/media.py
+++ b/src/vidxp/api_routes/media.py
@@ -26,6 +26,7 @@
from vidxp.api_models import UploadIntentResponse
from vidxp.composition import HttpApplicationContext
from vidxp.core.identifiers import MediaId
+from vidxp.core.media import MediaState
router = APIRouter(prefix="/media", tags=["media"])
@@ -176,9 +177,22 @@ def list_media(
str | None,
Query(min_length=1, max_length=512),
] = None,
+ filename: Annotated[
+ str | None,
+ Query(min_length=1),
+ ] = None,
+ state: Annotated[
+ MediaState | None,
+ Query(),
+ ] = None,
) -> MediaPage:
return service.application.list_media(
- ListMediaCommand(page_size=page_size, cursor=cursor)
+ ListMediaCommand(
+ page_size=page_size,
+ cursor=cursor,
+ filename=filename,
+ state=state,
+ )
)
diff --git a/src/vidxp/api_routes/platform.py b/src/vidxp/api_routes/platform.py
index 1e3be995..56b06a8e 100644
--- a/src/vidxp/api_routes/platform.py
+++ b/src/vidxp/api_routes/platform.py
@@ -11,6 +11,7 @@
WorkspaceOverview,
)
from vidxp.composition import HttpApplicationContext
+from vidxp.core.media import MediaState
router = APIRouter(
@@ -32,9 +33,22 @@ def workspace(
str | None,
Query(min_length=1, max_length=512),
] = None,
+ filename: Annotated[
+ str | None,
+ Query(min_length=1),
+ ] = None,
+ state: Annotated[
+ MediaState | None,
+ Query(),
+ ] = None,
) -> WorkspaceOverview:
return service.application.workspace(
- ListMediaCommand(page_size=page_size, cursor=cursor)
+ ListMediaCommand(
+ page_size=page_size,
+ cursor=cursor,
+ filename=filename,
+ state=state,
+ )
)
diff --git a/src/vidxp/application_models.py b/src/vidxp/application_models.py
index 585205d4..e2bb1eb7 100644
--- a/src/vidxp/application_models.py
+++ b/src/vidxp/application_models.py
@@ -398,6 +398,15 @@ class ListMediaCommand(ApplicationModel):
max_length=512,
description="Opaque next_cursor from the previous list_media page.",
)
+ filename: str | None = Field(
+ default=None,
+ min_length=1,
+ description="Filter media records by filename.",
+ )
+ state: MediaState | None = Field(
+ default=None,
+ description="Filter media records by readiness/state.",
+ )
class MediaPage(Page[MediaAsset]):
diff --git a/src/vidxp/cli_commands/media.py b/src/vidxp/cli_commands/media.py
index dcd32344..3905b4be 100644
--- a/src/vidxp/cli_commands/media.py
+++ b/src/vidxp/cli_commands/media.py
@@ -16,6 +16,7 @@
require_media_runtime,
state_from_context,
)
+from vidxp.core.media import MediaState
app = typer.Typer(no_args_is_help=True, help="Import and inspect local media.")
@@ -68,6 +69,14 @@ def list_media(
str | None,
typer.Option("--cursor", help="Cursor returned by the previous page."),
] = None,
+ filename: Annotated[
+ str | None,
+ typer.Option("--filename", help="Filter media by filename."),
+ ] = None,
+ media_state: Annotated[
+ MediaState | None,
+ typer.Option("--state", help="Filter media by readiness/state."),
+ ] = None,
json_output: Annotated[
bool,
typer.Option("--json", help="Emit machine-readable JSON."),
@@ -77,7 +86,12 @@ def list_media(
state = state_from_context(ctx)
page = state.service.list_media(
- ListMediaCommand(page_size=limit, cursor=cursor)
+ ListMediaCommand(
+ page_size=limit,
+ cursor=cursor,
+ filename=filename,
+ state=media_state,
+ )
)
assets = page.items
payload = page.model_dump(mode="json")
diff --git a/src/vidxp/control_plane.py b/src/vidxp/control_plane.py
index 08278d0a..91150bff 100644
--- a/src/vidxp/control_plane.py
+++ b/src/vidxp/control_plane.py
@@ -117,6 +117,9 @@ def list_media(self, command: ListMediaCommand) -> MediaPage:
@application_boundary
def workspace(self, command: ListMediaCommand) -> WorkspaceOverview:
page = self.list_media(command)
+ # Workspace actions are repository-level guidance, so compare against
+ # repository-wide totals rather than a potentially filtered page total.
+ repository_media_total = self.list_media(ListMediaCommand(page_size=1)).total
index = self.index_status()
snapshot = self._read_active_snapshot()
capabilities = self.list_capabilities()
@@ -166,7 +169,7 @@ def workspace(self, command: ListMediaCommand) -> WorkspaceOverview:
next_actions = []
if page.total == 0:
next_actions.append("register_media")
- if page.total > len(indexed_media) or any(
+ if repository_media_total > len(indexed_media) or any(
item.media_id not in indexed_media for item in page.items
):
next_actions.append("index_media")
diff --git a/src/vidxp/frontend.py b/src/vidxp/frontend.py
index b9f095df..a9fa7f20 100644
--- a/src/vidxp/frontend.py
+++ b/src/vidxp/frontend.py
@@ -754,7 +754,7 @@ def _select_video(busy, media_id, media_page):
assets = tuple(
asset
for asset in (media_page.items if media_page is not None else ())
- if asset.state == MediaState.ready
+ if getattr(asset, "state", None) == MediaState.ready
)
media_id = _default_media_id(media_id, assets)
if media_id is not None:
@@ -998,7 +998,9 @@ def run():
media_id = indexed_media[0]
st.session_state[MEDIA_ID_KEY] = media_id
try:
- media_page = service.list_media(ListMediaCommand(page_size=100))
+ media_page = service.list_media(
+ ListMediaCommand(page_size=100, state=MediaState.ready)
+ )
except ApplicationError:
media_page = None
st.warning(
diff --git a/src/vidxp/infrastructure/sql_catalog.py b/src/vidxp/infrastructure/sql_catalog.py
index c4336652..c35a59ab 100644
--- a/src/vidxp/infrastructure/sql_catalog.py
+++ b/src/vidxp/infrastructure/sql_catalog.py
@@ -68,6 +68,41 @@ def _record(model: Any, value: Any) -> Any:
return model.model_validate(_payload(value), strict=False)
+def _escape_like_pattern(value: str) -> str:
+ escaped = (
+ value.replace("\\", "\\\\")
+ .replace("%", "\\%")
+ .replace("_", "\\_")
+ )
+ return f"%{escaped}%"
+
+
+def _media_payload_text(key: str):
+ return media.c.payload[key].as_string()
+
+
+def _media_list_conditions(
+ *,
+ dialect_name: str,
+ filename: str | None,
+ state: MediaState | None,
+) -> tuple[Any, ...]:
+ conditions: list[Any] = []
+ if state is not None:
+ conditions.append(_media_payload_text("state") == state.value)
+ if filename is not None:
+ filename_text = _media_payload_text("original_filename")
+ if dialect_name == "postgresql":
+ filename_text = filename_text.collate("pg_unicode_fast")
+ conditions.append(
+ func.casefold(filename_text).like(
+ _escape_like_pattern(filename.casefold()),
+ escape="\\",
+ )
+ )
+ return tuple(conditions)
+
+
def _upload_record(row: Any) -> UploadIntentRecord:
return UploadIntentRecord(
intent_id=row.intent_id,
@@ -180,6 +215,12 @@ def transaction(self) -> Iterator[Connection]:
@staticmethod
def _configure_sqlite(dbapi_connection: Any, _record: Any) -> None:
+ dbapi_connection.create_function(
+ "casefold",
+ 1,
+ str.casefold,
+ deterministic=True,
+ )
cursor = dbapi_connection.cursor()
try:
cursor.execute("PRAGMA foreign_keys = ON")
@@ -316,28 +357,44 @@ def list_media(
*,
limit: int,
offset: int = 0,
+ filename: str | None = None,
+ state: MediaState | None = None,
) -> tuple[MediaRecord, ...]:
if limit <= 0 or offset < 0:
raise ValueError("limit must be positive and offset nonnegative")
+ conditions = _media_list_conditions(
+ dialect_name=self.engine.dialect.name,
+ filename=filename,
+ state=state,
+ )
+ query = select(media.c.payload).order_by(media.c.created_at, media.c.media_id)
+ if conditions:
+ query = query.where(and_(*conditions))
with self.engine.connect() as connection:
payloads = connection.execute(
- select(media.c.payload)
- .order_by(media.c.created_at, media.c.media_id)
- .limit(limit)
- .offset(offset)
+ query.limit(limit).offset(offset)
).scalars()
return tuple(
_record(MediaRecord, payload)
for payload in payloads
)
- def count_media(self) -> int:
+ def count_media(
+ self,
+ *,
+ filename: str | None = None,
+ state: MediaState | None = None,
+ ) -> int:
+ conditions = _media_list_conditions(
+ dialect_name=self.engine.dialect.name,
+ filename=filename,
+ state=state,
+ )
+ query = select(func.count()).select_from(media)
+ if conditions:
+ query = query.where(and_(*conditions))
with self.engine.connect() as connection:
- return int(
- connection.execute(
- select(func.count()).select_from(media)
- ).scalar_one()
- )
+ return int(connection.execute(query).scalar_one())
def reserve_media_import(
self,
diff --git a/src/vidxp/mcp.py b/src/vidxp/mcp.py
index c2f25ed0..c359c335 100644
--- a/src/vidxp/mcp.py
+++ b/src/vidxp/mcp.py
@@ -109,6 +109,7 @@
load_mcp_app_html,
)
from vidxp.core.identifiers import ArtifactId
+from vidxp.core.media import MediaState
from vidxp.evidence_delivery import (
EvidenceDeliveryService,
require_completed_evidence_result,
@@ -1277,13 +1278,23 @@ async def get_workspace(
str | None,
Field(min_length=1, max_length=512),
] = None,
+ filename: Annotated[
+ str | None,
+ Field(min_length=1),
+ ] = None,
+ state: MediaState | None = None,
) -> WorkspaceOverview:
return await _invoke_async(
context,
default_principal=default_principal,
permission=RepositoryPermission.read,
operation=lambda _actor: context.application.workspace(
- ListMediaCommand(page_size=page_size, cursor=cursor)
+ ListMediaCommand(
+ page_size=page_size,
+ cursor=cursor,
+ filename=filename,
+ state=state,
+ )
),
)
@@ -1349,13 +1360,23 @@ async def list_media(
str | None,
Field(min_length=1, max_length=512),
] = None,
+ filename: Annotated[
+ str | None,
+ Field(min_length=1),
+ ] = None,
+ state: MediaState | None = None,
) -> MediaPage:
return await _invoke_async(
context,
default_principal=default_principal,
permission=RepositoryPermission.read,
operation=lambda _actor: context.application.list_media(
- ListMediaCommand(page_size=page_size, cursor=cursor)
+ ListMediaCommand(
+ page_size=page_size,
+ cursor=cursor,
+ filename=filename,
+ state=state,
+ )
),
)
diff --git a/src/vidxp/media_service.py b/src/vidxp/media_service.py
index e208efae..37125260 100644
--- a/src/vidxp/media_service.py
+++ b/src/vidxp/media_service.py
@@ -1,8 +1,8 @@
from __future__ import annotations
+import hashlib
import json
from pathlib import Path
-import hashlib
from uuid import uuid4
from vidxp.application_models import (
@@ -274,14 +274,29 @@ def get(self, media_id: str) -> MediaAsset:
return media_asset(record)
def list(self, command: ListMediaCommand) -> MediaPage:
- scope = hashlib.sha256(
+ repository_scope = hashlib.sha256(
str(self.settings.repository_root.resolve()).encode()
).hexdigest()
+ scope = repository_scope
+ if command.filename is not None or command.state is not None:
+ scope = hashlib.sha256(
+ json.dumps(
+ [
+ repository_scope,
+ command.filename,
+ command.state.value if command.state is not None else None,
+ ],
+ separators=(",", ":"),
+ ).encode()
+ ).hexdigest()
try:
offset = decode_offset_cursor(command.cursor, scope=scope)
except CursorError as exc:
raise ValueError("The media cursor is invalid.") from exc
- total = self.catalog.count_media()
+ total = self.catalog.count_media(
+ filename=command.filename,
+ state=command.state,
+ )
if offset > total:
raise ValueError("The media cursor is outside the result set.")
items = tuple(
@@ -289,6 +304,8 @@ def list(self, command: ListMediaCommand) -> MediaPage:
for record in self.catalog.list_media(
limit=command.page_size,
offset=offset,
+ filename=command.filename,
+ state=command.state,
)
)
next_offset = offset + len(items)
diff --git a/src/vidxp/ports.py b/src/vidxp/ports.py
index 18c3ad23..492636ae 100644
--- a/src/vidxp/ports.py
+++ b/src/vidxp/ports.py
@@ -40,6 +40,7 @@
from vidxp.core.media import (
MediaProbe,
MediaRecord,
+ MediaState,
StagedMedia,
StoredMedia,
)
@@ -104,9 +105,16 @@ def list_media(
*,
limit: int,
offset: int = 0,
+ filename: str | None = None,
+ state: MediaState | None = None,
) -> tuple[MediaRecord, ...]: ...
- def count_media(self) -> int: ...
+ def count_media(
+ self,
+ *,
+ filename: str | None = None,
+ state: MediaState | None = None,
+ ) -> int: ...
def reserve_media_import(
self,
diff --git a/tests/test_api.py b/tests/test_api.py
index 09fd7565..1e510ff4 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -39,6 +39,7 @@
JobWaitResult,
IndexStatus,
MediaAsset,
+ MediaPage,
Principal,
SearchCommand,
SearchJobResult,
@@ -372,6 +373,56 @@ def test_workspace_endpoint_returns_actionable_repository_state(self):
command = context.application.workspace.call_args.args[0]
self.assertEqual(command.page_size, 25)
+ def test_list_media_passes_filters_to_application(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(Path(directory))
+ context.application.list_media.return_value = MediaPage(
+ items=(),
+ total=0,
+ )
+ with TestClient(create_app(context=context)) as client:
+ response = client.get(
+ "/api/v1/media",
+ params={
+ "page_size": 10,
+ "filename": "clip.mp4",
+ "state": "ready",
+ },
+ )
+
+ self.assertEqual(response.status_code, 200)
+ command = context.application.list_media.call_args.args[0]
+ self.assertEqual(command.page_size, 10)
+ self.assertEqual(command.filename, "clip.mp4")
+ self.assertEqual(command.state, MediaState.ready)
+
+ def test_workspace_passes_filters_to_application(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(Path(directory))
+ context.application.workspace.return_value = WorkspaceOverview(
+ media_total=0,
+ index=IndexStatus(
+ schema_version=2,
+ state="missing",
+ stage="status",
+ message="No index.",
+ ),
+ next_actions=("register_media",),
+ )
+ with TestClient(create_app(context=context)) as client:
+ response = client.get(
+ "/api/v1/workspace",
+ params={
+ "filename": "batch",
+ "state": "pending",
+ },
+ )
+
+ self.assertEqual(response.status_code, 200)
+ command = context.application.workspace.call_args.args[0]
+ self.assertEqual(command.filename, "batch")
+ self.assertEqual(command.state, MediaState.pending)
+
def test_repository_scopes_are_enforced_per_operation(self):
with TemporaryDirectory() as directory:
context = self.context(
diff --git a/tests/test_cli.py b/tests/test_cli.py
index f88d4677..be5165c2 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -465,6 +465,25 @@ def test_media_list_shows_pending_and_failed_states(self):
self.assertIn("failed", result.output)
self.assertIn("-", result.output)
+ def test_media_list_passes_filters_to_service(self):
+ self.service.list_media.return_value = MediaPage(items=(), total=0)
+
+ result = self.invoke(
+ [
+ "media",
+ "list",
+ "--filename",
+ "clip.mp4",
+ "--state",
+ "ready",
+ ]
+ )
+
+ self.assertEqual(result.exit_code, 0, result.output)
+ command = self.service.list_media.call_args.args[0]
+ self.assertEqual(command.filename, "clip.mp4")
+ self.assertEqual(command.state, MediaState.ready)
+
def test_ui_share_uses_streamlit_wildcard_bind_and_warns(self):
with (
patch(
diff --git a/tests/test_control_plane.py b/tests/test_control_plane.py
index 05a7d8d5..446f57b9 100644
--- a/tests/test_control_plane.py
+++ b/tests/test_control_plane.py
@@ -180,6 +180,65 @@ def test_workspace_projects_index_coverage_roles_and_next_actions(self):
)
self.assertEqual(workspace.media[1].capabilities[0].roles, ())
+ def test_workspace_index_action_uses_repository_total_when_filtered(self):
+ indexed = media_asset(MEDIA_ID, "indexed.mp4")
+ snapshot = IndexSnapshot(
+ snapshot_id=SNAPSHOT_ID,
+ created_at=datetime.now(timezone.utc),
+ config_fingerprint="b" * 64,
+ configuration={},
+ generations={
+ MEDIA_ID: GenerationReference(
+ generation_id=GENERATION_ID,
+ media_id=MEDIA_ID,
+ manifest_sha256="c" * 64,
+ input_sha256="d" * 64,
+ config_fingerprint="e" * 64,
+ modalities=("scene",),
+ record_counts={"scene": 12},
+ store_size_bytes_at_commit=100,
+ ),
+ OTHER_MEDIA_ID: GenerationReference(
+ generation_id="523456781234423481234567890abcde",
+ media_id=OTHER_MEDIA_ID,
+ manifest_sha256="f" * 64,
+ input_sha256="a" * 64,
+ config_fingerprint="b" * 64,
+ modalities=("scene",),
+ record_counts={"scene": 8},
+ store_size_bytes_at_commit=90,
+ ),
+ },
+ )
+ media = Mock()
+
+ def list_media(command: ListMediaCommand) -> MediaPage:
+ if command.filename == "clip":
+ return MediaPage(items=(indexed,), total=1)
+ return MediaPage(items=(indexed,), total=3)
+
+ media.list.side_effect = list_media
+ with TemporaryDirectory() as directory:
+ root = Path(directory)
+ application = ControlPlaneApplication(
+ layout=RepositoryLayout(root=root),
+ capabilities=CapabilityService(create_capability_registry()),
+ media=media,
+ artifacts=Mock(),
+ index_status=lambda: {
+ "schema_version": 2,
+ "state": "ready",
+ "stage": "status",
+ "message": "Index ready.",
+ },
+ active_snapshot=lambda: snapshot,
+ model_cache=root / "models",
+ )
+
+ workspace = application.workspace(ListMediaCommand(filename="clip"))
+
+ self.assertIn("index_media", workspace.next_actions)
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/test_frontend.py b/tests/test_frontend.py
index b7205069..464384a3 100644
--- a/tests/test_frontend.py
+++ b/tests/test_frontend.py
@@ -249,8 +249,10 @@ def test_busy_video_layout_keeps_controls_and_preview_stable(self):
def test_registered_video_selector_lists_only_ready_media(self):
service = Mock()
+
ready_id = MEDIA_ID
- pending_id = "223456781234423481234567890abcde"
+ pending_id = "pending-media-id"
+
media_page = SimpleNamespace(
items=(
SimpleNamespace(
@@ -262,7 +264,7 @@ def test_registered_video_selector_lists_only_ready_media(self):
SimpleNamespace(
media_id=pending_id,
original_filename="pending.mp4",
- duration_seconds=None,
+ duration_seconds=12.0,
state=MediaState.pending,
),
),
@@ -298,7 +300,7 @@ def test_registered_video_selector_lists_only_ready_media(self):
):
_uploaded, media_id = frontend._select_video(
False,
- pending_id,
+ ready_id,
media_page,
)
diff --git a/tests/test_mcp.py b/tests/test_mcp.py
index c0d04eb0..3f44ec3d 100644
--- a/tests/test_mcp.py
+++ b/tests/test_mcp.py
@@ -90,6 +90,7 @@
from vidxp.composition import HttpApplicationContext
from vidxp.control_plane import ControlPlaneApplication
from vidxp.core.artifacts import ArtifactKind, ArtifactState
+from vidxp.core.media import MediaState
from vidxp.core.uploads import UploadSessionState, UploadState
from vidxp.job_service import JobService
from vidxp.mcp import VidXPTokenVerifier, create_mcp_server, create_remote_mcp
@@ -1366,6 +1367,33 @@ async def test_discovery_tools_use_shared_media_and_job_pages(self):
)
self.assertEqual(context.jobs.list.call_args.args[0].page_size, 9)
+ async def test_list_media_passes_filters_to_application(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(Path(directory))
+ context.application.list_media.return_value = MediaPage(total=0)
+ server = create_mcp_server(
+ context,
+ default_principal=Principal(
+ subject="agent",
+ scopes=frozenset({"vidxp.read"}),
+ ),
+ )
+ async with Client(server) as client:
+ media = await client.call_tool(
+ "list_media",
+ {
+ "page_size": 5,
+ "filename": "clip.mp4",
+ "state": "ready",
+ },
+ )
+
+ self.assertFalse(media.is_error)
+ command = context.application.list_media.call_args.args[0]
+ self.assertEqual(command.page_size, 5)
+ self.assertEqual(command.filename, "clip.mp4")
+ self.assertEqual(command.state, MediaState.ready)
+
async def test_failed_model_preparation_job_is_structured_over_mcp(self):
with TemporaryDirectory() as directory:
context = self.context(Path(directory))
diff --git a/tests/test_media_catalog.py b/tests/test_media_catalog.py
index a9df9fd0..16a76b95 100644
--- a/tests/test_media_catalog.py
+++ b/tests/test_media_catalog.py
@@ -29,6 +29,7 @@
MEDIA_ID = "123456781234423481234567890abcde"
OTHER_MEDIA_ID = "223456781234423481234567890abcde"
+THIRD_MEDIA_ID = "423456781234423481234567890abcde"
ARTIFACT_ID = "323456781234423481234567890abcde"
@@ -144,6 +145,110 @@ def test_catalog_persists_and_deduplicates_media_by_checksum(self):
self.assertEqual(reopened.put_media(duplicate), record)
self.assertEqual(reopened.list_media(limit=10), (record,))
+ def test_catalog_list_media_filters_by_state(self):
+ with TemporaryDirectory() as directory:
+ catalog = LocalCatalog(Path(directory) / "catalog.sqlite3")
+ ready = media_record()
+ pending = incomplete_media_record(
+ state=MediaState.pending,
+ media_id=OTHER_MEDIA_ID,
+ checksum="2" * 64,
+ ).model_copy(update={"original_filename": "pending.mp4"})
+ failed = incomplete_media_record(
+ state=MediaState.failed,
+ media_id=THIRD_MEDIA_ID,
+ checksum="3" * 64,
+ ).model_copy(update={"original_filename": "failed.mp4"})
+ catalog.put_media(ready)
+ catalog.put_media(pending)
+ catalog.put_media(failed)
+
+ self.assertEqual(catalog.count_media(), 3)
+ self.assertEqual(catalog.count_media(state=MediaState.ready), 1)
+ self.assertEqual(catalog.count_media(state=MediaState.pending), 1)
+ self.assertEqual(catalog.count_media(state=MediaState.failed), 1)
+ self.assertEqual(
+ catalog.list_media(limit=10, state=MediaState.ready),
+ (ready,),
+ )
+
+ def test_catalog_list_media_filters_by_filename(self):
+ with TemporaryDirectory() as directory:
+ catalog = LocalCatalog(Path(directory) / "catalog.sqlite3")
+ clip = media_record().model_copy(
+ update={"original_filename": "Straße-Épisode-Clip.mp4"}
+ )
+ other = media_record(
+ OTHER_MEDIA_ID,
+ checksum="2" * 64,
+ ).model_copy(update={"original_filename": "other.mov"})
+ catalog.put_media(clip)
+ catalog.put_media(other)
+
+ self.assertEqual(catalog.count_media(filename="clip"), 1)
+ self.assertEqual(catalog.count_media(filename="CLIP"), 1)
+ self.assertEqual(catalog.count_media(filename="STRASSE"), 1)
+ self.assertEqual(catalog.count_media(filename="éPISODE"), 1)
+ self.assertEqual(
+ catalog.list_media(limit=10, filename="clip"),
+ (clip,),
+ )
+ self.assertEqual(catalog.count_media(filename="other.mov"), 1)
+
+ def test_catalog_list_media_applies_combined_filters(self):
+ with TemporaryDirectory() as directory:
+ catalog = LocalCatalog(Path(directory) / "catalog.sqlite3")
+ ready_clip = media_record().model_copy(
+ update={"original_filename": "ready-clip.mp4"}
+ )
+ pending_clip = incomplete_media_record(
+ state=MediaState.pending,
+ media_id=OTHER_MEDIA_ID,
+ checksum="2" * 64,
+ ).model_copy(update={"original_filename": "pending-clip.mp4"})
+ catalog.put_media(ready_clip)
+ catalog.put_media(pending_clip)
+
+ self.assertEqual(
+ catalog.count_media(
+ filename="clip",
+ state=MediaState.ready,
+ ),
+ 1,
+ )
+ self.assertEqual(
+ catalog.list_media(
+ limit=10,
+ filename="clip",
+ state=MediaState.ready,
+ ),
+ (ready_clip,),
+ )
+
+ def test_catalog_list_media_supports_filtered_pagination(self):
+ with TemporaryDirectory() as directory:
+ catalog = LocalCatalog(Path(directory) / "catalog.sqlite3")
+ first = media_record().model_copy(
+ update={"original_filename": "batch-001.mp4"}
+ )
+ second = media_record(
+ OTHER_MEDIA_ID,
+ checksum="2" * 64,
+ ).model_copy(update={"original_filename": "batch-002.mp4"})
+ third = media_record(
+ THIRD_MEDIA_ID,
+ checksum="3" * 64,
+ ).model_copy(update={"original_filename": "other.mp4"})
+ catalog.put_media(first)
+ catalog.put_media(second)
+ catalog.put_media(third)
+
+ self.assertEqual(catalog.count_media(filename="batch"), 2)
+ page = catalog.list_media(limit=1, offset=0, filename="batch")
+ self.assertEqual(page, (first,))
+ page = catalog.list_media(limit=1, offset=1, filename="batch")
+ self.assertEqual(page, (second,))
+
def test_catalog_replaces_pending_and_failed_media(self):
with TemporaryDirectory() as directory:
catalog = LocalCatalog(Path(directory) / "catalog.sqlite3")
diff --git a/tests/test_media_services.py b/tests/test_media_services.py
index d2dcb280..5134e448 100644
--- a/tests/test_media_services.py
+++ b/tests/test_media_services.py
@@ -1,3 +1,4 @@
+import hashlib
import unittest
from contextlib import nullcontext
from datetime import datetime, timezone
@@ -19,6 +20,7 @@
)
from vidxp.core.artifacts import ArtifactState
from vidxp.core.contracts import CancellationToken, IndexCancelledError
+from vidxp.core.cursors import encode_offset_cursor
from vidxp.core.media import (
MediaProbe,
MediaUnavailableError,
@@ -475,7 +477,8 @@ def test_publish_failure_catalogs_failed_media_without_publishing(self):
def test_media_pages_are_bounded_and_cursor_scoped(self):
with TemporaryDirectory() as directory:
- service, catalog, _store, _probe = self.service(Path(directory))
+ root = Path(directory)
+ service, catalog, _store, _probe = self.service(root)
catalog.count_media.return_value = 3
catalog.list_media.side_effect = [
(record(), record().model_copy(
@@ -501,7 +504,11 @@ def test_media_pages_are_bounded_and_cursor_scoped(self):
self.assertEqual(first.total, 3)
self.assertEqual(len(first.items), 2)
- self.assertIsNotNone(first.next_cursor)
+ scope = hashlib.sha256(str(root.resolve()).encode()).hexdigest()
+ self.assertEqual(
+ first.next_cursor,
+ encode_offset_cursor(2, scope=scope),
+ )
self.assertEqual(len(second.items), 1)
self.assertIsNone(second.next_cursor)
self.assertEqual(
@@ -509,6 +516,57 @@ def test_media_pages_are_bounded_and_cursor_scoped(self):
2,
)
+ def test_filtered_media_cursor_is_bounded_and_filter_scoped(self):
+ with TemporaryDirectory() as directory:
+ service, catalog, _store, _probe = self.service(Path(directory))
+ catalog.count_media.return_value = 2
+ catalog.list_media.side_effect = [(record(),), (record(),)]
+ command = ListMediaCommand(
+ page_size=1,
+ filename="É" * 255,
+ state=MediaState.ready,
+ )
+
+ first = service.list(command)
+ second = service.list(
+ command.model_copy(update={"cursor": first.next_cursor})
+ )
+ for changed_filter in (
+ {"filename": "other.mp4"},
+ {"state": MediaState.failed},
+ ):
+ with (
+ self.subTest(changed_filter=changed_filter),
+ self.assertRaisesRegex(ValueError, "cursor"),
+ ):
+ service.list(
+ command.model_copy(
+ update={"cursor": first.next_cursor, **changed_filter}
+ )
+ )
+
+ self.assertLessEqual(len(first.next_cursor or ""), 512)
+ self.assertIsNone(second.next_cursor)
+
+ def test_media_list_passes_filters_to_catalog_count(self):
+ with TemporaryDirectory() as directory:
+ service, catalog, _store, _probe = self.service(Path(directory))
+ catalog.count_media.return_value = 1
+ catalog.list_media.return_value = (record(),)
+
+ service.list(
+ ListMediaCommand(
+ page_size=10,
+ filename="clip.mp4",
+ state=MediaState.ready,
+ )
+ )
+
+ catalog.count_media.assert_called_once_with(
+ filename="clip.mp4",
+ state=MediaState.ready,
+ )
+
def test_failed_checksum_is_retried_to_ready(self):
with TemporaryDirectory() as directory:
root = Path(directory)
From c8942c6fda009a50bb18ceac887039a329cdb5fb Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Fri, 28 Aug 2026 14:40:12 +0500
Subject: [PATCH 2/9] feat(sound): add FineLAP audio event search (#130)
* docs(benchmarking): refresh multimodal model direction
* feat(sound): add FineLAP audio event search
* fix(desktop): refresh model cache catalog
---
.github/release-intro.md | 2 +-
INSTALLATION_GUIDE.md | 6 +
README.md | 10 +-
desktop/model-cache-catalog.json | 20 +
desktop/src/components/TargetSummary.tsx | 1 +
docs/architecture/platform.md | 12 +
docs/benchmarking/README.md | 21 +-
docs/benchmarking/benchmark_catalog.md | 87 ++--
docs/benchmarking/direction.md | 6 +
docs/benchmarking/execution_readiness.md | 5 +
docs/benchmarking/model_selection.md | 135 ++++++
docs/benchmarking/paper_validation.md | 15 +-
docs/benchmarking/published_results.md | 64 ++-
docs/benchmarking/research_papers.md | 34 +-
docs/benchmarking/results.md | 25 +-
docs/local-api.md | 6 +
pyproject.toml | 12 +-
src/vidxp/capabilities/registry.py | 3 +-
src/vidxp/capabilities/search.py | 3 +
src/vidxp/capabilities/sound/__init__.py | 1 +
src/vidxp/capabilities/sound/config.py | 15 +
src/vidxp/capabilities/sound/definition.py | 100 ++++
src/vidxp/capabilities/sound/indexing.py | 265 +++++++++++
src/vidxp/capabilities/sound/models.py | 228 +++++++++
src/vidxp/capabilities/sound/operations.py | 110 +++++
src/vidxp/capabilities/sound/requirements.txt | 8 +
src/vidxp/capabilities/sound/specs.py | 76 +++
src/vidxp/frontend.py | 3 +-
tests/test_capabilities.py | 16 +-
tests/test_frontend.py | 9 +-
tests/test_frontend_app.py | 2 +-
tests/test_local_probe.py | 6 +-
tests/test_mcp.py | 2 +-
tests/test_sound.py | 234 +++++++++
uv.lock | 448 +++++++++++++++++-
35 files changed, 1910 insertions(+), 80 deletions(-)
create mode 100644 docs/benchmarking/model_selection.md
create mode 100644 src/vidxp/capabilities/sound/__init__.py
create mode 100644 src/vidxp/capabilities/sound/config.py
create mode 100644 src/vidxp/capabilities/sound/definition.py
create mode 100644 src/vidxp/capabilities/sound/indexing.py
create mode 100644 src/vidxp/capabilities/sound/models.py
create mode 100644 src/vidxp/capabilities/sound/operations.py
create mode 100644 src/vidxp/capabilities/sound/requirements.txt
create mode 100644 src/vidxp/capabilities/sound/specs.py
create mode 100644 tests/test_sound.py
diff --git a/.github/release-intro.md b/.github/release-intro.md
index 55f5757a..94726190 100644
--- a/.github/release-intro.md
+++ b/.github/release-intro.md
@@ -1,6 +1,6 @@
## Download VidXP
-{release_notice}VidXP turns video into searchable dialogue, scenes, people, and
+{release_notice}VidXP turns video into searchable dialogue, sounds, scenes, actions, people, and
inspectable evidence. Choose the desktop app for the guided local setup, or use
the Python package and containers for command-line and server deployments.
diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md
index db5b6d07..9288c417 100644
--- a/INSTALLATION_GUIDE.md
+++ b/INSTALLATION_GUIDE.md
@@ -34,6 +34,7 @@ Approximate model downloads are:
| Feature | Download |
|---|---:|
| Dialogue search | 2.64 GiB |
+| Sound event search | 0.94 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
| Actor matching | 37 MiB |
@@ -161,6 +162,7 @@ asking for confirmation. Download only selected features when preferred:
vidxp prepare --modalities scene
vidxp prepare --modalities dialogue,actor
vidxp prepare --modalities videoprism # action search
+vidxp prepare --modalities sound # music and environmental sounds
```
For a noninteractive script, add `--yes`. Indexing and search commands do not
@@ -193,6 +195,9 @@ vidxp search scene "a yellow taxi on a city street"
# Find an action or event (`videoprism` is the CLI name for action search)
vidxp search videoprism "a person opens a door and walks outside"
+# Find music or an environmental sound
+vidxp search sound "an alarm ringing"
+
# Find something that was said
vidxp search dialogue "the bread just came out of the oven"
```
@@ -277,6 +282,7 @@ assembling a custom installation:
| Extra | Adds |
|---|---|
| `dialogue` | Transcription, dialogue embeddings, and storage |
+| `sound` | Music and environmental-sound search and storage |
| `scene` | Scene search and storage |
| `videoprism` | Action search and storage |
| `actor` | Actor matching and storage |
diff --git a/README.md b/README.md
index 5c012ac8..7f08f1b3 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
- Dialogue search · Scene search · Action search · Actor grouping
+ Dialogue search · Sound search · Scene search · Action search · Actor grouping
@@ -122,7 +122,7 @@ See the [Coolify guide](docs/deployment/coolify.md) for the complete setup.
## What you can do today
- Build searchable libraries from individual videos or whole collections.
-- Find dialogue by meaning and visual moments by describing the scene.
+- Find dialogue, sound events, visual scenes, and multi-frame actions by description.
- Ask grounded questions and inspect the supporting boards, frames, or clips.
- Group recurring faces and render highlighted actor overlays.
- Keep personal, client, or project libraries separate.
@@ -146,6 +146,9 @@ vidxp search scene "a yellow taxi on a city street"
# Find an action or event
vidxp search videoprism "a person opens a door and walks outside"
+# Find a sound event
+vidxp search sound "a dog barking over traffic noise"
+
# Find something that was said
vidxp search dialogue "the bread just came out of the oven"
```
@@ -205,12 +208,13 @@ approximately 3 GiB.
| Capability | Approximate model download |
|---|---:|
| Dialogue search | 2.64 GiB |
+| Sound event search | 0.94 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
| Actor matching | 37 MiB |
A full local Desktop setup with every search capability uses approximately
-8.1 GiB. Leave additional temporary space during installation and for indexes,
+9.0 GiB. Leave additional temporary space during installation and for indexes,
source videos, and exported results.
By default, the CLI and desktop app share the same VidXP data directory:
diff --git a/desktop/model-cache-catalog.json b/desktop/model-cache-catalog.json
index 8284badf..49590f9b 100644
--- a/desktop/model-cache-catalog.json
+++ b/desktop/model-cache-catalog.json
@@ -1,9 +1,29 @@
[
+ {
+ "id": "AndreasXi/FineLAP",
+ "label": "AndreasXi/FineLAP",
+ "relative_artifact": "models--AndreasXi--FineLAP/snapshots/b419aa22947d29907a5567f21b81bf3b39a40449/model.safetensors"
+ },
{
"id": "dropbox-dash/faster-whisper-large-v3-turbo",
"label": "dropbox-dash/faster-whisper-large-v3-turbo",
"relative_artifact": "models--dropbox-dash--faster-whisper-large-v3-turbo/snapshots/0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf/model.bin"
},
+ {
+ "id": "FacebookAI/roberta-base config",
+ "label": "FacebookAI/roberta-base config",
+ "relative_artifact": "finelap-tokenizer/config.json"
+ },
+ {
+ "id": "FacebookAI/roberta-base merges",
+ "label": "FacebookAI/roberta-base merges",
+ "relative_artifact": "finelap-tokenizer/merges.txt"
+ },
+ {
+ "id": "FacebookAI/roberta-base vocab",
+ "label": "FacebookAI/roberta-base vocab",
+ "relative_artifact": "finelap-tokenizer/vocab.json"
+ },
{
"id": "google/siglip2-base-patch16-224",
"label": "google/siglip2-base-patch16-224",
diff --git a/desktop/src/components/TargetSummary.tsx b/desktop/src/components/TargetSummary.tsx
index 06cee6f0..3311ae85 100644
--- a/desktop/src/components/TargetSummary.tsx
+++ b/desktop/src/components/TargetSummary.tsx
@@ -48,6 +48,7 @@ const CAPABILITY_LABELS: Record = {
dialogue: 'Dialogue search',
media: 'Video tools',
scene: 'Visual scene search',
+ sound: 'Sound event search',
videoprism: 'Temporal video search',
};
diff --git a/docs/architecture/platform.md b/docs/architecture/platform.md
index d4251ff4..c8dd8ef8 100644
--- a/docs/architecture/platform.md
+++ b/docs/architecture/platform.md
@@ -773,6 +773,18 @@ composition root and is sorted deterministically.
`97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3`; its published multilingual MTEB
retrieval results materially exceed the older multilingual E5 baseline and its
Apache-2.0 license permits the intended deployment.
+- Sound: use FineLAP at immutable Hugging Face revision
+ `b419aa22947d29907a5567f21b81bf3b39a40449`. Each video audio stream is decoded
+ once into ten-second windows. The sound collection stores one normalized global
+ embedding per window and the model's normalized dense embeddings as timestamped
+ activation records. Both use the shared text/audio space, and search results
+ retain `representation`, window, and activation provenance. FineLAP requires
+ repository-supplied Transformers code; VidXP loads only the pinned snapshot,
+ keeps runtime loading offline, and prepares the two small pinned RoBERTa
+ tokenizer artifacts explicitly instead of allowing a constructor-time model
+ download. The Hugging Face model card declares MIT; the upstream GitHub source
+ repository does not contain a separate license file, so redistribution review
+ must preserve that qualification.
- Actor: replace `face_recognition`/dlib with OpenCV Zoo YuNet plus SFace through
OpenCV's maintained DNN APIs. Model files are retrieved with `pooch`, pinned to
OpenCV Zoo commit `47534e27c9851bb1128ccc0102f1145e27f23f98`, and verified
diff --git a/docs/benchmarking/README.md b/docs/benchmarking/README.md
index cfe692fd..e4aecea9 100644
--- a/docs/benchmarking/README.md
+++ b/docs/benchmarking/README.md
@@ -16,7 +16,8 @@ installation and product usage, start with the main
| Guided input preparation | Complete | `vidxp benchmark prepare` estimates and confirms downloads, verifies pinned artifacts, validates DiDeMo media, resumes partial transfers, and prints the runnable benchmark command |
| DiDeMo visual localization | Legacy full result + current smoke | The legacy CLIP stack completed 4,021 official test queries over 1,037 videos; the current SigLIP2 stack passed a one-annotation real execution smoke |
| HiREST transcript localization | Legacy full result + current smoke | The legacy MiniLM stack scored all 193 validation pairs; current Qwen3 passed a two-video real execution smoke; 776 released test predictions remain unscored because their public bounds are placeholders |
-| LongVALE combined evaluation | Next | Build the visual-plus-speech adapter and validate one evaluation archive before scheduling the full run |
+| Environmental-sound retrieval | Implementation complete; benchmark pending | FineLAP stores global ten-second windows and dense timestamped sound activations; no VidXP quality score is claimed yet |
+| LongVALE combined evaluation | Next adapter and pilot | Validate vision, environmental sound, and speech together on one evaluation archive before scheduling the full run |
| Actor clustering | Data-gated | The preferred BBT/Buffy evaluation still requires lawful access to the source episodes |
Read [current results](results.md) for the scores, plain-language metric
@@ -30,6 +31,7 @@ definitions, honest comparisons, and the next benchmark decision.
| Reproduce DiDeMo or HiREST | [Adapter validation ledger](adapter_validation.md) |
| Understand the benchmark-ready Python structure | [Core contract](core_contract.md) |
| See which benchmarks exist and what each measures | [Benchmark catalog](benchmark_catalog.md) |
+| Understand the current model and benchmark choices | [Multimodal model direction](model_selection.md) |
| Find exact published competitor scores | [Published comparison results](published_results.md) |
| Review the relevant papers | [Research-paper inventory](research_papers.md) |
| Audit what was checked in each paper | [Paper-validation ledger](paper_validation.md) |
@@ -42,17 +44,20 @@ they are not the current task list.
## Current benchmark position
-No single published benchmark covers dialogue retrieval, scene retrieval, and
-actor clustering together.
+No single published benchmark covers speech retrieval, scene/action retrieval,
+environmental-sound retrieval, exact temporal boundaries, and actor clustering
+together.
The retained full DiDeMo and HiREST results establish separate legacy-provider
visual and transcript baselines. Current SigLIP2 and Qwen3 checks establish
adapter/runtime compatibility only; they do not yet provide full-corpus quality
-comparisons. LongVALE is the next combined test because it asks a system to find
-described events in long videos using visual, speech, and general audio
-evidence. VidXP can currently contribute visual and speech evidence; it does
-not recognize general sounds such as music, alarms, or barking. Any LongVALE
-result must keep that limitation visible.
+comparisons. VidXP now contributes separate visual, speech, and FineLAP sound
+evidence, including global windows and dense timestamps for music, alarms,
+barking, and other non-speech events. The next target is the LongVALE adapter and
+one-archive pilot. That work must measure the integration before any VidXP sound
+quality or combined-system claim is made. The
+[current model direction](model_selection.md) records the selection evidence and
+remaining controls.
## Evidence rules
diff --git a/docs/benchmarking/benchmark_catalog.md b/docs/benchmarking/benchmark_catalog.md
index 58cd92d1..5bf609b1 100644
--- a/docs/benchmarking/benchmark_catalog.md
+++ b/docs/benchmarking/benchmark_catalog.md
@@ -4,9 +4,10 @@ Collection index: [Benchmarking research](README.md)
Status: Candidate research complete; execution status updated
-Last verified: 2026-07-27
+Last verified: 2026-08-27
-Scope: Capabilities implemented in the current `src/vidxp` package
+Scope: Capabilities implemented in the current `src/vidxp` package, including
+timestamped environmental-sound retrieval
This catalog is the detailed decision record for published benchmarks. Current
VidXP measurements are reported separately in [results](results.md). Published
@@ -37,18 +38,21 @@ The selected suite remains component-based:
5. **BCL on BBT/Buffy** as the best reproducible actor-clustering protocol, provided
the team can obtain the underlying episodes or explicitly limits the experiment
to BCL's supplied features.
-6. **LongVALE** as a combined vision–speech temporal test using a frozen,
- non-learned fusion rule while clearly disclosing unsupported generic audio.
-7. A fixed local indexing/query timing protocol, reported as a VidXP engineering
+6. **MAEB**, **MVEB**, and **AEGBench** as component-selection and regression
+ sources for audio embeddings, video embeddings, and sound-event boundaries.
+7. **LongVALE** as the primary combined vision–sound–speech temporal test now that
+ the environmental-sound provider exists.
+8. A fixed local indexing/query timing protocol, reported as a VidXP engineering
measurement rather than as a score directly comparable with unrelated hardware.
LongVALE is the strongest peer-reviewed combined vision–audio–speech temporal
benchmark found. It still omits actor clustering and expects genuinely fused
-multi-modal interval predictions. A fixed rank/score fusion adapter makes an
-official run possible without claiming full omni-modal coverage. FLARE is a smaller
-downloadable audio-visual stress test, but it is a 2026
-preprint benchmark with generated, filtered queries. It belongs in a secondary
-experiment or watchlist until peer review and benchmark stability improve.
+multi-modal interval predictions. FineLAP now supplies separate global-window and
+dense timestamped sound evidence, but the LongVALE adapter and fusion rule remain
+unimplemented and no quality score is claimed. FLARE is a smaller downloadable
+audio-visual stress test, but it is a 2026 preprint benchmark with generated,
+filtered queries. It belongs in a secondary experiment or watchlist until peer
+review and benchmark stability improve.
See [execution readiness](execution_readiness.md) for the corrected implementation
boundary and per-benchmark engineering classification.
@@ -111,12 +115,12 @@ protocol. It does not mean that a similar published score is already comparable.
| Movie face clustering | [MovieFaceCluster / VideoClusterNet](https://www.ecva.net/papers/eccv_2024/papers_ECCV/html/4432_ECCV_2024_paper.php) | Unsupervised face-track clustering across nine movies | Whether VidXP actor clustering generalizes to a recent movie protocol | Executable evidence while the dataset link and code remain unavailable | Blocked/reference-only |
| Egocentric face clustering | [EasyCom-Clustering](https://github.com/ibug-group/Easycom-Clustering) | Face-track clustering in egocentric social video | Whether actor clusters remain stable under first-person views, occlusion, and viewpoint changes | Any executable result until the promised data is released | Blocked/reference-only |
| Face verification provenance | [LFW / dlib protocol](https://github.com/ageitgey/face_recognition) | Same/different identity verification on still-image pairs | Only the provenance and generic discrimination of the underlying face embedding | VidXP actor clustering, face detection coverage, temporal continuity, or unknown-K performance | Not an actor benchmark; provenance only |
-| Combined temporal | [LongVALE](https://github.com/ttgeng233/LongVALE) | Vision, speech, and generic-audio event grounding in long videos, with one interval per event query | Whether a frozen VidXP interval proposal and scene/speech fusion localize multimodal events | Actor clustering or full omni-modal coverage because VidXP lacks generic-audio recognition | Engineering A/medium; artifacts reachable; compliance, interval, and runtime gates remain |
+| Combined temporal | [LongVALE](https://github.com/ttgeng233/LongVALE) | Vision, speech, and generic-audio event grounding in long videos, with one interval per event query | Whether a frozen VidXP interval proposal and scene/sound/speech fusion localize multimodal events | Actor clustering or proof of omni-modal quality before the new sound path and fusion adapter are evaluated | Engineering A/medium; artifacts reachable; compliance, interval, and runtime gates remain |
| Audiovisual retrieval | [FLARE](https://flarebench.github.io/) | Caption-to-clip/video retrieval plus clip-level model-simulated visual-only, audio-only, and joint query retrieval | Where VidXP's scene, speech, and fixed-fusion clip rankings succeed or fail across evidence types | Actor performance, human-authored-query generalization, or full generic-sound capability | Engineering A/medium; ready/watchlist preprint |
| Large-corpus event | [MultiVENT 2.0](https://huggingface.co/datasets/hltcoe/MultiVENT2.0) | Multilingual event-centric ranked-video retrieval using visual, speech/ASR, embedded-text/OCR, and human-description metadata evidence | Whether existing VidXP visual, speech, and metadata paths scale to a large heterogeneous corpus; unsupported OCR becomes a measurable weakness | Timestamp localization, actor clustering, generic acoustic-event retrieval, or multilingual adequacy of the current Qwen3 embedding provider | Engineering A with large operational adapter; approximately 1.93 TB gated |
| Large-corpus shot | [TRECVID AVS / V3C](https://www-nlpir.nist.gov/projects/tv2025/avs.html) | Natural-language query to up to 1,000 ranked master shots, measured with mean xinfAP | Whether VidXP scene retrieval scales to over a million pooled/judged shots and returns useful corpus rankings | Dialogue or actor performance, or comparable latency across different hardware | Archived 2024/2025 protocol; agreement and approximately 1.6 TB gated |
| Multimodal whole-video | [MUVR](https://github.com/debby-0527/MUVR) | Paired query-video plus detailed text to ranked short normalized videos; pure-text and pure-video are ablations | Whether VidXP ranks relevant videos for a declared pure-text ablation or a reusable-CLIP visual slice | Timestamp localization, audio/dialogue understanding, actors, or equivalence to the defining paired-query task | Ablation slice adaptable; lower priority |
-| Audiovisual whole-video | [VALOR-32K](https://github.com/TXH-mercury/VALOR) | Bidirectional audiovisual-text retrieval and audiovisual captioning over 32,000 ten-second clips with human captions | Whether VidXP's visual/speech or fixed audiovisual representation ranks captioned clips on a standard released split | Temporal localization, actor clustering, long-video search, or full parity without a generic-audio model | Benchmark and official artifacts verified; media links/source rights must be checked |
+| Audiovisual whole-video | [VALOR-32K](https://github.com/TXH-mercury/VALOR) | Bidirectional audiovisual-text retrieval and audiovisual captioning over 32,000 ten-second clips with human captions | Whether VidXP's visual/sound/speech or fixed audiovisual representation ranks captioned clips on a standard released split | Temporal localization, actor clustering, long-video search, or full parity before a matching adapter run | Benchmark and official artifacts verified; media links/source rights must be checked |
| Agentic multimodal retrieval | [MM-MSRVTT and TVR-1200](https://openaccess.thecvf.com/content/WACV2026/html/Shah_VRAgent_Self-Refining_Agent_for_Zero-Shot_Multimodal_Video_Retrieval_WACV_2026_paper.html) | Visual-plus-ASR/joint whole-video retrieval on 500 generated MSR-VTT queries and 1,200 adapted TVR queries | A future released run could compare VidXP's frozen fusion with an agent-refined multimodal query strategy | Any current reproducible claim while the defining annotations and code are absent | Benchmark-defining but artifact-blocked |
| Advertising-context retrieval | [ContextIQ Val-1](https://openaccess.thecvf.com/content/WACV2025/html/Chaubey_ContextIQ_A_Multimodal_Expert-Based_Video_Retrieval_System_for_Contextual_Advertising_WACV_2025_paper.html) | Eight ad-concept queries over 500 YouTube movie clips using manually judged top-five results | Whether VidXP's modality experts produce useful contextual whole-video rankings on this small released annotation set | Standard large-corpus generalization, temporal localization, actors, or comparison to a widely adopted protocol | Low-priority custom benchmark; IDs/splits/queries public, media survival gated |
| Vector database | [VectorDBBench](https://github.com/zilliztech/vectordbbench) | ANN index build time, recall, latency, and throughput | Whether Chroma retrieves VidXP embeddings accurately and efficiently at a stated scale | Video decoding/model cost or end-to-end retrieval quality | Engineering A; ready diagnostic |
@@ -146,8 +150,8 @@ their published numbers alone do not answer a VidXP capability question:
| 5 | Dialogue | TVR, `t` subset | A, medium adapter | Gated | Lawful TV clips with original audio |
| 6 | Actor | BCL on BBT/Buffy | A, medium clustering adapter | Gated | Released inference script cannot score VidXP clusters; lawful raw episodes needed |
| 7 | Visual | Charades-STA | A, medium adapter | Gated | Dataset agreement and narrow staged domain |
-| 8 | Whole system | LongVALE | A, medium fixed-fusion adapter | Artifacts reachable; compliance/runtime gate | Evaluation-only raw archives are 40.523 GiB; full 254 GB repository is not required; no generic-audio capability |
-| 9 | Whole system | FLARE | A, medium adapter | Ready artifacts; runtime gate/watchlist | 66.267 GiB release; preprint; generated rank-filtered queries; generic audio unsupported |
+| 8 | Whole system | LongVALE | A/medium fusion adapter | Artifacts reachable; compliance/runtime gates | Evaluation-only raw archives are 40.523 GiB; full 254 GB repository is not required; FineLAP sound records are available, while the evaluation adapter remains |
+| 9 | Whole system | FLARE | A/medium adapter | Ready artifacts; runtime gate/watchlist | 66.267 GiB release; preprint; generated rank-filtered queries; visual/audio/joint coverage now needs adapter validation |
| 10 | Actor | Hannah | A, medium evaluator adapter | Gated | Research agreement and separately obtained movie |
| 11 | Actor/system | MovieNet | A for component slices | Gated | Registration; movies excluded; actor labels are keyframe-oriented |
@@ -480,6 +484,22 @@ The evaluator, not VidXP, should compute the published metrics.
| [MovieFaceCluster / VideoClusterNet](https://www.ecva.net/papers/eccv_2024/papers_ECCV/html/4432_ECCV_2024_paper.php) | Relevant nine-movie protocol; paper's dataset URL currently returns 404 and no code was found | Reference-only |
| [EasyCom-Clustering](https://github.com/ibug-group/Easycom-Clustering) | 22 sessions, 94,047 face tracks, 1,623,633 facial images, and 53 participants; repository still promises a future download and has no release | Reference-only |
+## Model-selection and component regression benchmarks
+
+These sources narrow provider choices before VidXP spends local compute. They do
+not replace end-to-end temporal evaluation.
+
+| Benchmark | Exact role | VidXP use | Important limit |
+| --- | --- | --- | --- |
+| [MAEB](https://arxiv.org/abs/2602.16008) | Thirty representative audio-embedding tasks selected from a 98-task pool, spanning speech, music, environmental sounds, and audio-text reasoning | Select and regression-test audio representations by domain instead of assuming one audio model leads every task | It evaluates embeddings, not VidXP's long-media windowing, boundaries, fusion, or system cost |
+| [MVEB](https://arxiv.org/abs/2606.14958) | Twenty-three representative video-embedding tasks selected from a 184-task pool, with modality-restricted tables and paired video-only/audio-plus-video variants | Select video retrieval candidates and measure when preserving audio helps | It does not provide a checked VideoPrism row or unrestricted long-video temporal-grounding score |
+| [AEGBench](https://arxiv.org/abs/2607.04383) | Open-vocabulary audio-event grounding with exact intervals, including difficult, repeated, and overlapping events | Evaluate environmental-sound boundary quality after retrieval | It is an audio component test and does not exercise visual scenes, speech, or cross-modal fusion |
+| [OVSD](https://research.ibm.com/publications/robust-and-efficient-video-scene-detection-using-optimal-sequential-grouping) | Semantic scene segmentation over open-licensed movies and animations | Test temporal-unit or scene-boundary proposals without copyright-gated commercial films | It has no natural-language query, action-label, environmental-sound, speech, or retrieval task |
+
+The active provider conclusions and exact published selection scores are in
+[multimodal model direction](model_selection.md) and
+[published comparison results](published_results.md).
+
## Whole-system and efficiency evaluation
### LongVALE
@@ -512,15 +532,18 @@ The evaluator, not VidXP, should compute the published metrics.
- **VidXP fit:** strongest peer-reviewed combined temporal target. It covers vision,
speech, and generic audio but not actors. VidXP must freeze a point-to-interval
or interval-proposal rule and emit one top-ranked interval, then combine its
- existing visual and speech evidence with a frozen, non-learned fusion rule.
+ visual, environmental-sound, and speech evidence with a frozen,
+ provenance-preserving fusion rule. The FineLAP provider now exists, but that
+ fusion adapter does not.
Returning top three alone does not satisfy the protocol. Generic-audio evidence
- within official event queries remains unsupported; keep all 13,867 queries in
- the denominator unless a separately justified diagnostic slice is declared.
+ within official event queries is unsupported by the current implementation;
+ keep all 13,867 queries in the denominator unless a separately justified
+ diagnostic slice is declared.
Released LongVALE features are precomputed inputs to the official LongVALE-LLM
path, not a standalone reproduction package. That path additionally needs its
base model/projector/stage weights, CUDA environment, and evaluator. In all
cases, those features bypass VidXP and cannot substitute for running VidXP's
- configured scene/transcription/dialogue providers on the raw evaluation
+ configured scene/sound/transcription/dialogue providers on the raw evaluation
videos.
- **Operations:** raw evaluation media are now packaged directly, so YouTube
survival is no longer the access gate. The raw MP4 payload expands to
@@ -531,8 +554,8 @@ The evaluator, not VidXP, should compute the published metrics.
require a measured pilot. No LongVALE model checkpoint is needed for a VidXP
run.
- **Verdict/confidence:** **Adaptable with reachable evaluation artifacts and
- license/compliance plus runtime/capacity gates; confirmed.** Generic-audio
- coverage remains an explicit limitation.
+ license/compliance plus runtime/capacity gates; confirmed.** Run one archive
+ through the new sound path and fusion adapter before the complete protocol.
### FLARE
@@ -556,9 +579,9 @@ The evaluator, not VidXP, should compute the published metrics.
71,153,909,874 bytes (66.267 GiB). It contains 399 directories and 87,697
segmented MP4s with audio. Code is MIT; the dataset states CC BY 4.0; the
repository includes a harness for 15 retrievers.
-- **VidXP fit:** closest obtainable combined scene/dialogue retrieval stress test.
- It does not test actor clustering. Audio queries include music and sound events,
- so a declared speech-only subset is necessary for VidXP's dialogue path.
+- **VidXP fit:** closest obtainable combined visual/sound/speech retrieval stress
+ test. It does not test actor clustering. The sound provider now exists; a full
+ protocol claim still requires the FLARE adapter and visual/audio/joint run.
- **Caveat:** a unified generated query was retained when it succeeded at rank one
while its component queries failed, creating material model-selection bias.
- **Verdict/confidence:** **Adaptable watchlist; artifacts confirmed, publication
@@ -702,18 +725,20 @@ Completed:
Next:
-1. Implement the fixed LongVALE visual/speech adapter and validate one evaluation
- archive before committing to the full 1,171-video run.
-2. Add fixed hardware-aware indexing and query measurements to each subsequent
+1. Complete a bounded real-media FineLAP integration smoke, retaining LAION-CLAP
+ as the mature comparison.
+2. Implement the fixed LongVALE visual/sound/speech adapter and validate one
+ evaluation archive before committing to the full 1,171-video run.
+3. Add fixed hardware-aware indexing and query measurements to each subsequent
full experiment.
-3. Run QVHighlights validation, first on a declared subset for preprocessing
+4. Run QVHighlights validation, first on a declared subset for preprocessing
validation and then in full if storage permits; the subset does not reduce the
monolithic archive download.
-4. Resolve TVR media access. If successful, run `t`, `v`, and `vt` separately.
-5. Resolve lawful BBT/Buffy media access before implementing the VidXP actor
+5. Resolve TVR media access. If successful, run `t`, `v`, and `vt` separately.
+6. Resolve lawful BBT/Buffy media access before implementing the VidXP actor
scoring adapter. Reproduce BCL's supplied-feature pipeline only as a separate
protocol check.
-6. Audit and prepare QuerYD raw-video media; use released features only for a
+7. Audit and prepare QuerYD raw-video media; use released features only for a
clearly labelled Collaborative Experts protocol/reference check.
-7. Consider Charades-STA, FLARE, and large-corpus MultiVENT/TRECVID experiments
+8. Consider Charades-STA, FLARE, and large-corpus MultiVENT/TRECVID experiments
only after the core suite produces valid, archived predictions.
diff --git a/docs/benchmarking/direction.md b/docs/benchmarking/direction.md
index c5e2a477..2b8eae23 100644
--- a/docs/benchmarking/direction.md
+++ b/docs/benchmarking/direction.md
@@ -8,6 +8,12 @@ Established: 2026-07-25
Applies to: VidXP / ActorDB paper benchmarking work
+> **Current direction:** The 2026-08-27
+> [multimodal model decision](model_selection.md) supersedes this brief's
+> implemented-capability-only sequencing. Environmental-sound retrieval has now
+> been implemented; full vision/sound/speech evaluation is the next phase.
+> This file remains the historical discovery contract.
+
This document preserves the rules used to start the benchmark research. It
predates the benchmark-ready core and completed DiDeMo/HiREST runs. Use
[current results](results.md) and the [collection index](README.md) for the
diff --git a/docs/benchmarking/execution_readiness.md b/docs/benchmarking/execution_readiness.md
index 245b3fdf..74c959d8 100644
--- a/docs/benchmarking/execution_readiness.md
+++ b/docs/benchmarking/execution_readiness.md
@@ -1,5 +1,10 @@
# Benchmark execution readiness
+> **Historical assessment:** Statements below that generic sound was unsupported
+> accurately describe the implementation when this assessment was written. The
+> active [multimodal model direction](model_selection.md) now records the shipped
+> FineLAP sound layer and places LongVALE and FLARE adapter validation next.
+
Collection index: [Benchmarking research](README.md)
Status: Historical pre-implementation assessment
diff --git a/docs/benchmarking/model_selection.md b/docs/benchmarking/model_selection.md
new file mode 100644
index 00000000..de3acf68
--- /dev/null
+++ b/docs/benchmarking/model_selection.md
@@ -0,0 +1,135 @@
+# Multimodal model and benchmark direction
+
+Collection index: [Benchmarking research](README.md)
+
+Status: Current decision record; FineLAP and VideoPrism are implemented, while
+other candidate providers remain planned unless architecture says otherwise
+
+Last verified: 2026-08-27
+
+## Product requirement
+
+VidXP needs three independently searchable, timestamped evidence channels:
+
+1. visual scenes and actions;
+2. environmental sounds, music, and other non-speech acoustic events; and
+3. spoken words through ASR and text retrieval.
+
+Query-time fusion must preserve which channel produced each hit. A shared
+embedding model is optional; collapsing the channels is not the requirement.
+LongVALE makes this boundary explicit because its events can depend on vision,
+generic audio, speech, or their temporal relationship.
+
+## Repository baseline
+
+The history before this change contained no shipped CLAP provider or generic-sound
+capability. CLAP appears in the later landscape/roadmap research, not in the
+application implementation history, so it was not removed by the VideoPrism
+change. This branch now implements the missing layer with FineLAP; LAION-CLAP
+remains the mature comparison rather than the production provider.
+
+VideoPrism is different: it is a current, separately registered temporal-video
+capability using `google/videoprism-lvt-base-f16r288` through Transformers. The
+new model direction keeps that implementation as the incumbent control while
+testing whether Qwen3-VL-Embedding improves text-to-video scene/action retrieval.
+
+## How models are selected
+
+Published benchmark tables, release history, licensing, adoption, artifact
+format, and runtime size are sufficient to choose the first integration
+candidates. VidXP does not need to spend model or agent runs recreating public
+leaderboards before implementation.
+
+Local evaluation has a narrower purpose: verify preprocessing, timestamps,
+memory, latency, index size, failure behavior, and regressions in this repository.
+It does not substitute a tiny private sample for broad published comparisons.
+Promptfoo is therefore not required for component-model selection; an agent
+evaluation harness would answer a different question.
+
+## Current provider direction
+
+| Role | First direction | Control or ceiling | Reason |
+| --- | --- | --- | --- |
+| Speech transcription and semantic search | Keep faster-whisper plus the current Qwen3 text-embedding path | Existing released-ASR benchmark paths | Speech and acoustic-event retrieval are different tasks; MAEB shows that no single audio encoder dominates linguistic and environmental-sound work. |
+| Environmental-sound retrieval | [FineLAP](https://github.com/xiquan-li/FineLAP), now integrated | [LAION-CLAP](https://github.com/LAION-AI/CLAP) as the mature native-Transformers baseline | FineLAP combines global audio-text retrieval with dense frame features and leads the checked same-table AudioCaps comparison. VidXP supplies fixed ten-second windowing and timestamped dense records. |
+| Open-vocabulary sound localization | FineLAP dense features, now stored; compare [PE-A-Frame](https://github.com/facebookresearch/perception_models) | AEGBench methods as research ceilings | Clip retrieval alone cannot identify exact sound intervals, especially repeated or overlapping events. FineLAP integration does not establish boundary quality until AEGBench or LongVALE is run. |
+| Visual scene/action retrieval | Evaluate [Qwen3-VL-Embedding-2B](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B) as the practical candidate | Qwen3-VL-Embedding-8B as the quality ceiling; VideoPrism as the incumbent control | MVEB's text-video table ranks Qwen 8B and 2B first and second. The checked table has no directly comparable VideoPrism row, so this is stronger current selection evidence, not proof that VideoPrism lost a head-to-head. |
+| Visual temporal grounding | Evaluate [TimeLens2-4B](https://github.com/MCG-NJU/TimeLens2) after candidate retrieval | TimeLens2-8B and existing temporal baselines | The published 4B average nearly matches 8B at much lower cost. TimeLens2 is visual-only and cannot replace the sound or speech channels. |
+| Cross-modal fusion | Keep modality-specific providers and fuse timestamped candidates | A unified permissive audio-video-text encoder can be a later comparison | Separate providers preserve provenance, allow independent upgrades, and match the evidence that different model families lead different modalities and tasks. |
+
+Before promotion, every new checkpoint still needs an immutable revision, artifact
+hash, license review, safe-loading review, dependency fit, and a bounded real-media
+smoke test.
+
+## Published selection evidence
+
+Scores are comparable only within the named paper and task.
+
+| Source and task | Relevant result | Decision use |
+| --- | --- | --- |
+| [FineLAP, AudioCaps retrieval](https://aclanthology.org/2026.acl-long.473/) | FineLAP T→A/A→T R@1: 45.7/62.5; the paper's LAION-CLAP row: 35.1/44.2 | Select FineLAP for the first sound integration and retain CLAP as the mature control. |
+| [MVEB text-video leaderboard](https://arxiv.org/abs/2606.14958) | Qwen3-VL-Embedding-8B: 60.9 mean; 2B: 58.1; LCO-Embedding-Omni-7B: 56.8 | Prefer Qwen 2B for the practical visual candidate and 8B only when maximizing published quality. |
+| [TimeLens2 visual grounding](https://github.com/MCG-NJU/TimeLens2) | Seven-dataset average mIoU: 47.7 for 4B and 48.0 for 8B | Start with 4B; the 0.3-point gain does not justify making 8B the default candidate. |
+| [AEGBench](https://arxiv.org/abs/2607.04383) | PE-A-Frame Large: 0.389 mIoU, 0.407 event-F1, 0.607 segment-F1 in the checked table | Use a released specialist to test exact open-vocabulary sound intervals. |
+
+VideoPrism remains a credible multi-frame video encoder. The decision above does
+not reject it on quality. It rejects two unsupported claims: that implementation
+friction still blocks it, and that it is automatically the first text-video
+retrieval pick despite being absent from the current common MVEB comparison.
+
+## What each dataset or benchmark contributes
+
+| Dataset or benchmark | Use in VidXP | Does not establish |
+| --- | --- | --- |
+| [MAEB](https://arxiv.org/abs/2602.16008) | Broad audio-embedding selection across speech, music, environmental sound, and audio-text tasks | Long-video timestamp accuracy or end-to-end VidXP quality |
+| [MVEB](https://arxiv.org/abs/2606.14958) | Common video-embedding selection across retrieval and other representation tasks, including paired video-only and audio-plus-video variants | A direct VideoPrism comparison, unrestricted temporal localization, or system latency |
+| [AEGBench](https://arxiv.org/abs/2607.04383) | Open-vocabulary environmental-sound interval grounding, including difficult and repeated events | Visual or speech retrieval |
+| [LongVALE](https://github.com/ttgeng233/LongVALE) | Primary combined target: Omni-TVG for vision, sound, and speech event localization in long videos | Actor clustering; its captioning tasks are relevant only if VidXP claims generation |
+| [FLARE](https://flarebench.github.io/) | Secondary long-video retrieval stress test with visual-only, audio-only, and hard joint queries | Human-authored-query generalization; the queries are model-generated and filtered |
+| [OVSD](https://research.ibm.com/publications/robust-and-efficient-video-scene-detection-using-optimal-sequential-grouping) | Open-licensed scene-boundary segmentation data and a useful temporal-unit regression set | Natural-language retrieval, action recognition, environmental-sound search, speech search, or cross-modal fusion |
+| [MultiVENT 2.0](https://huggingface.co/datasets/hltcoe/MultiVENT2.0) | Large-corpus event retrieval for visual, ASR, OCR, and metadata channels | Generic acoustic-event retrieval or moment boundaries |
+
+LongVALE supplies three tasks: omni-modal temporal grounding, dense video
+captioning, and segment captioning. Omni-TVG directly matches VidXP's search and
+timestamp contract. The two captioning tasks should not be adopted merely because
+they share the dataset.
+
+## Remaining benchmark gap
+
+A generic centralized audio or video embedding leaderboard is not new white
+space: MAEB and MVEB already provide that infrastructure in the MTEB ecosystem,
+and AEGBench, LongVALE, and FLARE cover adjacent temporal and multimodal slices.
+
+The defensible gap is narrower: a live, reproducible long-video system benchmark
+that combines scene/action, environmental-sound, and speech queries; scores both
+retrieval and exact boundaries; includes modality-isolation and fusion ablations;
+uses realistic queries; and reports latency, memory, index size, and
+commodity-hardware behavior. If VidXP publishes this, it should extend or
+interoperate with the MTEB/MOEB ecosystem instead of creating an isolated model
+leaderboard.
+
+## Cost and execution policy
+
+Reading published papers, leaderboards, model cards, and open benchmark metadata
+does not consume Codex, Claude, or model-inference runs. Downloading and running
+open checkpoints locally normally has no per-call API charge, but it does consume
+the machine's storage, memory, electricity, and time; dataset and checkpoint
+licenses can also restrict use.
+
+Metered model or agent comparisons are not part of the selection gate. Spend
+local compute only after the provider exists, using the smallest smoke that can
+catch integration defects. Schedule full MAEB, MVEB, LongVALE, FLARE, or AEGBench
+runs only when their result answers an approved paper or release question.
+
+## Implementation order
+
+1. Validate the implemented FineLAP sound capability on a bounded real-media
+ sample, then run the LongVALE one-archive adapter pilot.
+2. Compare LAION-CLAP as the mature integration baseline and PE-A-Frame where
+ boundary quality requires a specialist.
+3. Add or replace the visual video-embedding provider with
+ Qwen3-VL-Embedding-2B while keeping current and VideoPrism controls.
+4. Add TimeLens2-4B only after cheap candidate retrieval, for visual temporal
+ proposal or reranking work.
+5. Run LongVALE Omni-TVG and FLARE with all three evidence channels and frozen
+ fusion. Keep OVSD as a scene-boundary component test.
diff --git a/docs/benchmarking/paper_validation.md b/docs/benchmarking/paper_validation.md
index a3bdfc25..c068f2b3 100644
--- a/docs/benchmarking/paper_validation.md
+++ b/docs/benchmarking/paper_validation.md
@@ -22,10 +22,23 @@ The inventory contained 57 paper rows when this validation pass began. The audit
first added 18 omitted benchmark-defining, protocol-lineage, and direct-comparator
papers, then the published-results pass added three directly relevant retrieval
comparators that the first audit missed: MMMORRF, OmniEmbed-MultiVENT, and Q2E.
-The reconciled inventory contains 79 unique paper rows. Every inventory paper's
+The 2026-08-27 model-selection refresh added MAEB, MVEB, FineLAP, Auto-AEG/
+AEGBench, TimeLens2, and the OVSD-defining paper. The reconciled inventory now
+contains 85 unique paper rows. Every inventory paper's
exact source URL appears in a
paper-level ledger row below; the final coverage check found zero omissions.
+## Current component-model selection
+
+| Paper or release | Evidence checked | Actual experimental use | Measures/results reported | Validation outcome |
+| --- | --- | --- | --- | --- |
+| [MAEB](https://arxiv.org/abs/2602.16008) | Full text and released MTEB relationship checked | Thirty representative audio-embedding tasks selected from a 98-task pool; 50+ models across speech, music, environmental sound, and cross-modal audio-text work | Task-family metrics and aggregate/Borda comparisons | Correct broad source for audio-provider selection. It shows that speech-pretrained and contrastive audio-text models lead different domains; it does not measure long-video windowing or VidXP. |
+| [MVEB](https://arxiv.org/abs/2606.14958) | Full text and main/appendix result tables checked | Twenty-three representative video-embedding tasks selected from a 184-task pool; 33 models; paired video-only and audio-plus-video variants plus modality-restricted tables | Classification, clustering, retrieval, QA, and aggregate means; text-video Table 11 | Qwen3-VL-Embedding-8B/2B rank first/second on the checked text-video table at 60.9/58.1. VideoPrism is absent, so no direct quality claim between them is valid. |
+| [FineLAP](https://aclanthology.org/2026.acl-long.473/) | Full text, official repository, and checkpoint surface checked | AudioCaps/Clotho retrieval, classification, sound-event detection, and text-to-audio grounding | Retrieval R@1 plus task-specific dense metrics | Supports FineLAP as the first sound candidate: AudioCaps T→A/A→T R@1 45.7/62.5 versus the paper's LAION-CLAP 35.1/44.2. Fixed ten-second input remains a VidXP integration constraint. |
+| [Auto-AEG and AEGBench](https://arxiv.org/abs/2607.04383) | Full text, HTML tables, and dataset link checked | Open-vocabulary audio event grounding over 3,427 items/9,790 queries with difficulty-stratified hard cases | mIoU, recall/precision IoU, event F1, segment F1, and onset precision/recall | Direct environmental-sound boundary benchmark. Table 3 reports PE-A-Frame Large at 0.389 mIoU/0.407 event-F1/0.607 segment-F1; the larger trained Auto-AEG system is research ceiling context. |
+| [TimeLens2](https://github.com/MCG-NJU/TimeLens2) | Official paper/repository and released checkpoint table checked | Seven visual temporal-grounding datasets with 2B/4B/8B checkpoints | Average mIoU and per-dataset temporal-grounding metrics | The official release reports 47.7 average mIoU for 4B and 48.0 for 8B. Select 4B first; all variants are visual-only. |
+| [OVSD defining paper](https://research.ibm.com/publications/robust-and-efficient-video-scene-detection-using-optimal-sequential-grouping) | Primary IBM publication and later dataset-use records checked | Scene-boundary segmentation over open-licensed movies and animations | Scene-segmentation measures | Useful temporal-unit regression source only. OVSD contains no text-query retrieval, action, environmental-sound, speech, or fusion objective. |
+
## Whole-system and multimodal benchmark definitions
| Paper or specification | Evidence checked | Actual benchmark protocol | Measures/results reported | Validation outcome |
diff --git a/docs/benchmarking/published_results.md b/docs/benchmarking/published_results.md
index daaa5d58..de30385e 100644
--- a/docs/benchmarking/published_results.md
+++ b/docs/benchmarking/published_results.md
@@ -4,7 +4,7 @@ Collection index: [Benchmarking research](README.md)
Status: Primary-source result extraction complete
-Last verified: 2026-07-26
+Last verified: 2026-08-27
This is the answer to “what did the published competitors actually score?” It is
the result-level companion to the capability matrix in the
@@ -33,6 +33,68 @@ Scores are preserved on the authors' scale. In particular, FLARE, LongVALE,
VALOR-32K, CLaMR, MUVR, and several temporal-retrieval papers report percentages,
while MultiVENT 2.0 and TRECVID report fractions in `[0, 1]`.
+## Component-model selection results
+
+These results choose first integration candidates. They are not VidXP scores and
+do not remove the need for bounded repository-specific validation after a provider
+exists.
+
+### FineLAP: audio-text retrieval and dense sound features
+
+Source: [ACL 2026 paper](https://aclanthology.org/2026.acl-long.473/), Table 2,
+proceedings pp. 10398–10399.
+
+| Model | AudioCaps text→audio R@1 | AudioCaps audio→text R@1 | Selection use |
+| --- | ---: | ---: | --- |
+| FineLAP | 45.7 | 62.5 | First environmental-sound provider; also exposes dense frame features |
+| LAION-CLAP | 35.1 | 44.2 | Mature native-Transformers integration baseline |
+
+The same paper uses fixed ten-second FineLAP inputs and identifies variable-length
+audio as future work. Long-media integration therefore still needs timestamped
+windowing, overlap, and span merging owned by VidXP.
+
+### MVEB: current text-video embedding comparison
+
+Source: [MVEB paper](https://arxiv.org/pdf/2606.14958), Table 11, PDF p. 30.
+
+| Model | MVEB(text, video) mean | Selection use |
+| --- | ---: | --- |
+| Qwen3-VL-Embedding-8B | 60.9 | Published quality ceiling |
+| Qwen3-VL-Embedding-2B | 58.1 | Practical first visual scene/action candidate |
+| LCO-Embedding-Omni-7B | 56.8 | Strong audio-video-text context, but substantially larger |
+
+The checked MVEB paper contains no VideoPrism row. These numbers support the Qwen
+candidate order, but they do not establish a direct Qwen-versus-VideoPrism win.
+VideoPrism remains an incumbent multi-frame encoder control.
+
+### TimeLens2: visual temporal grounding size trade-off
+
+Source: [official TimeLens2 release table](https://github.com/MCG-NJU/TimeLens2),
+checked 2026-08-27.
+
+| Checkpoint | Seven-dataset average mIoU | Selection use |
+| --- | ---: | --- |
+| TimeLens2-4B | 47.7 | First visual temporal-grounding candidate after cheap recall |
+| TimeLens2-8B | 48.0 | Quality ceiling; not the practical default for a 0.3-point gain |
+
+Both checkpoints are visual-only. Neither evaluates environmental audio or spoken
+content, so neither can cover LongVALE's full task alone.
+
+### AEGBench: open-vocabulary sound boundaries
+
+Source: [Auto-AEG/AEGBench](https://arxiv.org/html/2607.04383v4), Table 3.
+
+| Model | mIoU | Event F1 | Segment F1 | Selection use |
+| --- | ---: | ---: | ---: | --- |
+| PE-A-Frame Large | 0.389 | 0.407 | 0.607 | Released sound-localization specialist and integration comparator |
+| DASM | 0.204 | 0.215 | 0.277 | Lower detector baseline |
+| Qwen3-Omni-30B + SFT + GRPO | 0.480 | 0.524 | 0.697 | Training-heavy research ceiling, not the local first pick |
+
+AEGBench contains 3,427 items and 9,790 queries with multiple hard-case labels,
+including repeated occurrence, polyphonic overlap, gradual boundaries, and long
+duration. It is a component benchmark, not evidence of end-to-end visual/sound/
+speech fusion.
+
## Whole-system and multimodal retrieval
### LongVALE: known-video omni-modal temporal grounding
diff --git a/docs/benchmarking/research_papers.md b/docs/benchmarking/research_papers.md
index fdc5d2c5..09911664 100644
--- a/docs/benchmarking/research_papers.md
+++ b/docs/benchmarking/research_papers.md
@@ -4,7 +4,7 @@ Collection index: [Benchmarking research](README.md)
Status: Paper-level benchmark-use audit complete; reading queue active
-Last verified: 2026-07-26
+Last verified: 2026-08-27
Related decision record: [Published benchmark catalog](benchmark_catalog.md)
@@ -21,21 +21,33 @@ actually relies on.
Start with these papers before reviewing individual model variants:
-1. **TVR / XML** for the closest peer-reviewed corpus-level visual/transcript
+1. **MAEB** and **MVEB** for the current common audio/video embedding landscape.
+2. **FineLAP** and **AEGBench** for environmental-sound retrieval and boundaries.
+3. **LongVALE** and **FLARE** for combined long-video vision, sound, and speech.
+4. **TVR / XML** for the closest peer-reviewed corpus-level visual/transcript
temporal-retrieval task.
-2. **Localizing Moments in Video with Natural Language** for the simplest
+5. **Localizing Moments in Video with Natural Language** for the simplest
executable visual moment benchmark.
-3. **QVHighlights / Moment-DETR** for modern interval and highlight evaluation.
-4. **Zero-shot Video Moment Retrieval With Off-the-Shelf Models** for the closest
+6. **QVHighlights / Moment-DETR** for modern interval and highlight evaluation.
+7. **Zero-shot Video Moment Retrieval With Off-the-Shelf Models** for the closest
methodological comparison to VidXP's untuned CLIP retrieval.
-5. **HiREST** and **QuerYD** for speech-backed retrieval options.
-6. **BCL** for unknown-number video face clustering and its WCP/NMI protocol.
-7. **VPCD** and **C1C** for stronger person/track constraints and dataset context.
-8. **LongVALE** for the strongest peer-reviewed combined vision–audio–speech
- temporal benchmark.
-9. **Towards a Complete Benchmark on Video Moment Localization** for cross-dataset
+8. **HiREST** and **QuerYD** for speech-backed retrieval options.
+9. **BCL** for unknown-number video face clustering and its WCP/NMI protocol.
+10. **VPCD** and **C1C** for stronger person/track constraints and dataset context.
+11. **Towards a Complete Benchmark on Video Moment Localization** for cross-dataset
bias and evaluation methodology.
+## Current model-selection and modality benchmarks
+
+| Paper | Venue/year | Benchmarks or models | Why it belongs |
+| --- | --- | --- | --- |
+| [MAEB: Massive Audio Embedding Benchmark](https://arxiv.org/abs/2602.16008) | arXiv 2026 | 30-task MAEB from a 98-task pool; 50+ models | Current common audio-embedding landscape across speech, music, environmental sound, and audio-text work; shows why speech and sound need separate providers |
+| [MVEB: Massive Video Embedding Benchmark](https://arxiv.org/abs/2606.14958) | arXiv 2026 | 23-task MVEB from a 184-task pool; 33 models | Current common video-embedding comparison, with Qwen3-VL-Embedding leading its text-video table and paired video/audio variants |
+| [FineLAP: Taming Heterogeneous Supervision for Fine-grained Language-Audio Pretraining](https://aclanthology.org/2026.acl-long.473/) | ACL 2026 | AudioCaps, Clotho, classification, sound-event detection, and text-to-audio grounding | Implemented environmental-sound provider because one model exposes both global retrieval and dense localization features |
+| [Auto-AEG and AEGBench](https://arxiv.org/abs/2607.04383) | arXiv 2026 | Open-vocabulary audio-event grounding and AEGBench | Direct sound-interval benchmark for hard, repeated, and overlapping environmental events |
+| [TimeLens2](https://github.com/MCG-NJU/TimeLens2) | arXiv 2026 | Seven visual temporal-grounding datasets | Supports the 4B visual-localizer choice; it has no audio input and cannot cover LongVALE alone |
+| [Robust and Efficient Video Scene Detection using Optimal Sequential Grouping](https://research.ibm.com/publications/robust-and-efficient-video-scene-detection-using-optimal-sequential-grouping) | ISM 2016 | Introduces OVSD | Open-licensed semantic scene-boundary source; useful for segmentation only, not query retrieval, actions, sound, or speech |
+
## Multimodal and whole-system retrieval
| Paper | Venue/year | Benchmarks introduced or used | Why it belongs |
diff --git a/docs/benchmarking/results.md b/docs/benchmarking/results.md
index ea5e2334..0a2e8be7 100644
--- a/docs/benchmarking/results.md
+++ b/docs/benchmarking/results.md
@@ -152,20 +152,25 @@ The result is a useful legacy validation baseline, not a final held-out paper
result. The current two-video Qwen3 smoke establishes compatibility only; it
does not supersede this score.
-## Next benchmark: LongVALE
+## Next combined benchmark
-LongVALE is the next useful experiment because it combines visual and spoken
-evidence in longer videos. The immediate work is limited to:
+The FineLAP environmental-sound layer is implemented but has no VidXP quality
+result yet. LongVALE is the primary next experiment because it contains visual,
+generic-audio, and spoken evidence in long videos. The work is ordered as follows:
-1. Convert LongVALE event descriptions into VidXP visual and dialogue searches.
-2. Combine those two result lists using one fixed rule.
-3. Return the single start/end range required by the official evaluator.
-4. Process one of the nine evaluation archives to measure runtime, temporary
+1. Complete a bounded real-media FineLAP integration smoke and record resource use.
+2. Convert LongVALE event descriptions into visual, sound, and dialogue searches.
+3. Combine those result lists using one fixed, provenance-preserving rule.
+4. Return the single start/end range required by the official evaluator.
+5. Process one of the nine evaluation archives to measure runtime, temporary
storage, and index growth.
-5. Run the complete evaluation only if that pilot finishes cleanly.
+6. Run the complete evaluation only if that pilot finishes cleanly.
-VidXP does not currently understand general sound events. Sound-only misses must
-remain in the official result and be disclosed rather than filtered out.
+VidXP now indexes general sound events, but implementation is not evidence of
+retrieval or boundary quality. The full LongVALE query set must remain in the
+official denominator, including sound-only misses. See
+[multimodal model direction](model_selection.md) for the selection evidence and
+benchmark roles.
## Sources and reproduction
diff --git a/docs/local-api.md b/docs/local-api.md
index fcd41d7f..9c0a26cc 100644
--- a/docs/local-api.md
+++ b/docs/local-api.md
@@ -94,6 +94,12 @@ The file data stays on the computer and does not pass through MCP. As with an
upload, the client can use `modalities` to select a smaller set of search
features.
+Use the `sound` modality to index or search music, environmental sounds, and
+other non-speech audio events. CLI, HTTP, local stdio MCP, remote MCP, browser,
+and Desktop all resolve that name through the same capability contract. Spoken
+words remain a separate `dialogue` modality, while multi-frame visible actions
+use `videoprism`.
+
## Connect from another computer
To make the API reachable from another device on the same trusted network,
diff --git a/pyproject.toml b/pyproject.toml
index fe2a0df6..78ce7670 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ authors = [
{ name = "Saad Bazaz" },
]
-description = "VidXP - Video indexing and search by dialogue, scene, and actor"
+description = "VidXP - Search video by dialogue, sound, scenes, actions, and actors"
readme = "README.md"
requires-python = ">=3.11,<3.15"
license = "MIT"
@@ -99,12 +99,17 @@ videoprism = { file = [
"src/vidxp/requirements/storage.txt",
"src/vidxp/capabilities/videoprism/requirements.txt",
] }
+sound = { file = [
+ "src/vidxp/requirements/storage.txt",
+ "src/vidxp/capabilities/sound/requirements.txt",
+] }
all = { file = [
"src/vidxp/requirements/storage.txt",
"src/vidxp/capabilities/dialogue/requirements.txt",
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
"src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/sound/requirements.txt",
] }
local-worker = { file = [
"src/vidxp/requirements/storage.txt",
@@ -113,6 +118,7 @@ local-worker = { file = [
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
"src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/sound/requirements.txt",
] }
mcp = { file = ["src/vidxp/requirements/mcp.txt"] }
slm = { file = ["src/vidxp/requirements/slm.txt"] }
@@ -128,6 +134,7 @@ server-worker = { file = [
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
"src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/sound/requirements.txt",
] }
test = { file = ["src/vidxp/requirements/test.txt"] }
frontend = { file = ["src/vidxp/requirements/frontend.txt"] }
@@ -146,6 +153,9 @@ torch = [
torchvision = [
{ index = "pytorch-cpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
]
+torchaudio = [
+ { index = "pytorch-cpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+]
[[tool.uv.index]]
name = "pytorch-cpu"
diff --git a/src/vidxp/capabilities/registry.py b/src/vidxp/capabilities/registry.py
index 681d814d..c9ed84c1 100644
--- a/src/vidxp/capabilities/registry.py
+++ b/src/vidxp/capabilities/registry.py
@@ -503,9 +503,10 @@ def _builtin_plugins() -> tuple[CapabilityPlugin, ...]:
from vidxp.capabilities.actor.definition import PLUGIN as actor
from vidxp.capabilities.dialogue.definition import PLUGIN as dialogue
from vidxp.capabilities.scene.definition import PLUGIN as scene
+ from vidxp.capabilities.sound.definition import PLUGIN as sound
from vidxp.capabilities.videoprism.definition import PLUGIN as videoprism
- return dialogue, scene, actor, videoprism
+ return dialogue, sound, scene, actor, videoprism
def _external_entry_points(allowlist: tuple[str, ...]) -> tuple[EntryPoint, ...]:
diff --git a/src/vidxp/capabilities/search.py b/src/vidxp/capabilities/search.py
index 484abe61..9c57ddc3 100644
--- a/src/vidxp/capabilities/search.py
+++ b/src/vidxp/capabilities/search.py
@@ -22,6 +22,9 @@
"timestamp",
"fps",
"duration",
+ "representation",
+ "window_index",
+ "activation_index",
}
)
diff --git a/src/vidxp/capabilities/sound/__init__.py b/src/vidxp/capabilities/sound/__init__.py
new file mode 100644
index 00000000..e64fd75b
--- /dev/null
+++ b/src/vidxp/capabilities/sound/__init__.py
@@ -0,0 +1 @@
+"""Fine-grained sound indexing and search."""
diff --git a/src/vidxp/capabilities/sound/config.py b/src/vidxp/capabilities/sound/config.py
new file mode 100644
index 00000000..0b3413b0
--- /dev/null
+++ b/src/vidxp/capabilities/sound/config.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+
+from pydantic import Field
+
+from vidxp.capabilities.contracts import CapabilityConfig
+from vidxp.core.contracts import IndexConfig
+
+
+class SoundConfig(CapabilityConfig):
+ batch_size: int = Field(default=1, gt=0)
+ window_seconds: float = Field(default=10.0, gt=0, le=10.0)
+
+
+def sound_config(config: IndexConfig) -> SoundConfig:
+ return SoundConfig.model_validate(config.options_for("sound"))
diff --git a/src/vidxp/capabilities/sound/definition.py b/src/vidxp/capabilities/sound/definition.py
new file mode 100644
index 00000000..5c0caea5
--- /dev/null
+++ b/src/vidxp/capabilities/sound/definition.py
@@ -0,0 +1,100 @@
+from __future__ import annotations
+
+from typing import Any, Mapping
+
+from vidxp.application_models import CapabilityRole
+from vidxp.capabilities.contracts import (
+ CapabilityDefinition,
+ CapabilityExecutor,
+ CapabilityPlugin,
+ OperationDefinition,
+ PreparationContext,
+ module_import_check,
+)
+from vidxp.capabilities.schemas import SearchInput, SearchResult
+from vidxp.capabilities.sound.config import SoundConfig
+from vidxp.capabilities.sound.models import get_sound_model
+from vidxp.capabilities.sound.operations import index_capability, search_operation
+from vidxp.capabilities.sound.specs import FINELAP_MODEL, SOUND_MODEL_SPECS
+from vidxp.core.contracts import IndexConfig, VideoSource
+from vidxp.core.indexing_common import ProgressCallback, report_preparation
+
+
+def prepare_models(
+ context: PreparationContext,
+ progress: ProgressCallback | None,
+) -> tuple[str, ...]:
+ SoundConfig.model_validate(context.settings)
+ report_preparation(
+ progress,
+ "sound_model",
+ f"Preparing sound model: {FINELAP_MODEL.model_id}",
+ )
+ get_sound_model(context.runtime, download=True, progress=progress)
+ return tuple(spec.model_id for spec in SOUND_MODEL_SPECS)
+
+
+def model_manifest(
+ _config: IndexConfig,
+ _sources: tuple[VideoSource, ...],
+) -> Mapping[str, Any]:
+ return {
+ "sound": FINELAP_MODEL.identity(),
+ "sound_text_assets": [
+ spec.identity() for spec in SOUND_MODEL_SPECS[1:]
+ ],
+ }
+
+
+DEFINITION = CapabilityDefinition(
+ name="sound",
+ description="Index and search music, environmental sounds, and audio events.",
+ extra="sound",
+ config_model=SoundConfig,
+ collection_name="sound",
+ index_stage="sound_indexing",
+ execution_group="sound",
+ prepares_models=True,
+ roles=(CapabilityRole.searchable, CapabilityRole.queryable),
+ model_specs=SOUND_MODEL_SPECS,
+ operations={
+ "search": OperationDefinition(
+ input_model=SearchInput,
+ output_model=SearchResult,
+ )
+ },
+)
+
+
+def create_executor() -> CapabilityExecutor:
+ return CapabilityExecutor(
+ indexer=index_capability,
+ operations={"search": search_operation},
+ prepare=prepare_models,
+ model_manifest=model_manifest,
+ runtime_checks=(
+ module_import_check("PyAV audio import", "av", "AudioResampler"),
+ module_import_check("NumPy import", "numpy"),
+ module_import_check("Torch import", "torch"),
+ module_import_check("TorchAudio import", "torchaudio"),
+ module_import_check("timm import", "timm"),
+ module_import_check(
+ "Transformers FineLAP import",
+ "transformers",
+ "AutoConfig",
+ "RobertaModel",
+ "RobertaTokenizer",
+ ),
+ module_import_check(
+ "Hugging Face Hub import",
+ "huggingface_hub",
+ "snapshot_download",
+ ),
+ ),
+ )
+
+
+PLUGIN = CapabilityPlugin(
+ definition=DEFINITION,
+ executor_factory=create_executor,
+)
diff --git a/src/vidxp/capabilities/sound/indexing.py b/src/vidxp/capabilities/sound/indexing.py
new file mode 100644
index 00000000..3ab601b2
--- /dev/null
+++ b/src/vidxp/capabilities/sound/indexing.py
@@ -0,0 +1,265 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from itertools import chain, islice
+from pathlib import Path
+from typing import Any, Iterable, Sequence
+
+from vidxp.capabilities.sound.config import sound_config
+from vidxp.capabilities.sound.models import get_sound_model
+from vidxp.capabilities.sound.specs import FINELAP_MODEL
+from vidxp.core.contracts import (
+ CancellationToken,
+ IndexConfig,
+ StorageRecord,
+ VideoSource,
+ stable_source_id,
+)
+from vidxp.core.indexing_common import ProgressCallback, report_progress
+from vidxp.ports import IndexStore, ModelRuntimePort
+
+
+SAMPLE_RATE = 16_000
+PCM_BYTES_PER_SAMPLE = 2
+DENSE_INTERVAL_SECONDS = 0.16
+
+
+@dataclass(frozen=True)
+class AudioWindow:
+ index: int
+ start: float
+ end: float
+ pcm: bytes
+
+
+def _resampled_pcm_frames(frame: Any, resampler: Any) -> Iterable[bytes]:
+ import numpy as np
+
+ converted = resampler.resample(frame)
+ if converted is None:
+ return
+ frames = converted if isinstance(converted, list) else [converted]
+ for item in frames:
+ values = item.to_ndarray().reshape(-1)
+ yield np.asarray(values, dtype=" Iterable[AudioWindow]:
+ import av
+
+ samples_per_window = round(window_seconds * SAMPLE_RATE)
+ bytes_per_window = samples_per_window * PCM_BYTES_PER_SAMPLE
+ pending = bytearray()
+ emitted_samples = 0
+ window_index = 0
+ with av.open(str(input_path)) as container:
+ if not container.streams.audio:
+ return
+ max_samples = (
+ round(float(container.duration * av.time_base) * SAMPLE_RATE)
+ if container.duration is not None
+ else None
+ )
+
+ def append_pcm(block: bytes) -> None:
+ if max_samples is None:
+ pending.extend(block)
+ return
+ buffered_samples = len(pending) // PCM_BYTES_PER_SAMPLE
+ remaining = max_samples - emitted_samples - buffered_samples
+ if remaining > 0:
+ pending.extend(block[: remaining * PCM_BYTES_PER_SAMPLE])
+
+ stream = container.streams.audio[0]
+ resampler = av.AudioResampler(
+ format="s16",
+ layout="mono",
+ rate=SAMPLE_RATE,
+ )
+ for frame in container.decode(stream):
+ cancellation.raise_if_cancelled()
+ for block in _resampled_pcm_frames(frame, resampler):
+ append_pcm(block)
+ while len(pending) >= bytes_per_window:
+ pcm = bytes(pending[:bytes_per_window])
+ del pending[:bytes_per_window]
+ start = emitted_samples / SAMPLE_RATE
+ emitted_samples += samples_per_window
+ yield AudioWindow(
+ index=window_index,
+ start=start,
+ end=emitted_samples / SAMPLE_RATE,
+ pcm=pcm,
+ )
+ window_index += 1
+ for block in _resampled_pcm_frames(None, resampler):
+ append_pcm(block)
+ if pending:
+ sample_count = len(pending) // PCM_BYTES_PER_SAMPLE
+ if sample_count:
+ pcm = bytes(pending[: sample_count * PCM_BYTES_PER_SAMPLE])
+ start = emitted_samples / SAMPLE_RATE
+ yield AudioWindow(
+ index=window_index,
+ start=start,
+ end=(emitted_samples + sample_count) / SAMPLE_RATE,
+ pcm=pcm,
+ )
+
+
+def _window_batches(
+ windows: Iterable[AudioWindow],
+ batch_size: int,
+) -> Iterable[tuple[AudioWindow, ...]]:
+ iterator = iter(windows)
+ while group := tuple(islice(iterator, batch_size)):
+ yield group
+
+
+def sound_records(
+ windows: Sequence[AudioWindow],
+ global_embeddings: Any,
+ dense_embeddings: Any,
+ config: IndexConfig,
+) -> list[StorageRecord]:
+ records = []
+ for window, global_vector, dense_vectors in zip(
+ windows,
+ global_embeddings,
+ dense_embeddings,
+ ):
+ window_id = stable_source_id(
+ config.run_id,
+ str(config.video_id),
+ "sound",
+ f"w{window.index:08d}",
+ generation_id=config.generation_id,
+ )
+ records.append(
+ StorageRecord(
+ source_id=window_id,
+ embedding=global_vector.tolist(),
+ metadata={
+ **config.record_identity("sound", window_id),
+ "representation": "window",
+ "window_index": window.index,
+ "start": window.start,
+ "end": window.end,
+ "duration": round(window.end - window.start, 6),
+ },
+ )
+ )
+ for activation_index, dense_vector in enumerate(dense_vectors):
+ start = round(
+ window.start + activation_index * DENSE_INTERVAL_SECONDS,
+ 6,
+ )
+ if start >= window.end:
+ break
+ end = round(
+ min(window.end, start + DENSE_INTERVAL_SECONDS),
+ 6,
+ )
+ source_id = stable_source_id(
+ config.run_id,
+ str(config.video_id),
+ "sound",
+ f"w{window.index:08d}-a{activation_index:04d}",
+ generation_id=config.generation_id,
+ )
+ records.append(
+ StorageRecord(
+ source_id=source_id,
+ embedding=dense_vector.tolist(),
+ metadata={
+ **config.record_identity("sound", source_id),
+ "representation": "activation",
+ "window_index": window.index,
+ "activation_index": activation_index,
+ "start": start,
+ "end": end,
+ "duration": round(end - start, 6),
+ },
+ )
+ )
+ return records
+
+
+def index_sound(
+ source: VideoSource,
+ *,
+ config: IndexConfig,
+ storage: IndexStore,
+ cancellation: CancellationToken,
+ runtime: ModelRuntimePort,
+ progress: ProgressCallback | None = None,
+) -> dict[str, Any]:
+ if config.video_id is None:
+ raise ValueError("IndexConfig.video_id is required for indexing.")
+ if source.path is None:
+ raise ValueError("Sound indexing requires a video or audio path.")
+ settings = sound_config(config)
+ windows = iter_audio_windows(
+ source.path,
+ window_seconds=settings.window_seconds,
+ cancellation=cancellation,
+ )
+ groups = iter(_window_batches(windows, settings.batch_size))
+ first_group = next(groups, None)
+ if first_group is None:
+ report_progress(
+ progress,
+ "sound_skipped",
+ "No audio samples were found; sound indexing was skipped.",
+ )
+ return {"sound_windows": 0, "sound_activations": 0}
+ report_progress(
+ progress,
+ "preparing_sound_model",
+ f"Preparing sound model: {FINELAP_MODEL.model_id}.",
+ )
+ provider = get_sound_model(runtime)
+ report_progress(
+ progress,
+ "sound_indexing",
+ "Indexing sound windows and dense activations.",
+ 0,
+ None,
+ )
+ stored_windows = 0
+ stored_activations = 0
+ for group in chain((first_group,), groups):
+ cancellation.raise_if_cancelled()
+ global_embeddings, dense_embeddings = provider.encode_audio(
+ [window.pcm for window in group]
+ )
+ records = sound_records(
+ group,
+ global_embeddings,
+ dense_embeddings,
+ config,
+ )
+ storage.upsert(
+ "sound",
+ records,
+ batch_size=config.storage_batch_size,
+ cancellation=cancellation,
+ )
+ stored_windows += len(group)
+ stored_activations += len(records) - len(group)
+ report_progress(
+ progress,
+ "sound_indexing",
+ "Indexing sound windows and dense activations.",
+ stored_windows,
+ None,
+ )
+ return {
+ "sound_windows": stored_windows,
+ "sound_activations": stored_activations,
+ }
diff --git a/src/vidxp/capabilities/sound/models.py b/src/vidxp/capabilities/sound/models.py
new file mode 100644
index 00000000..00dbebd1
--- /dev/null
+++ b/src/vidxp/capabilities/sound/models.py
@@ -0,0 +1,228 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+import sys
+from typing import Any, Callable, Sequence
+
+from vidxp.capabilities.sound.specs import (
+ FINELAP_MODEL,
+ ROBERTA_CONFIG,
+ ROBERTA_MERGES,
+ ROBERTA_VOCAB,
+)
+from vidxp.core.indexing_common import report_preparation
+from vidxp.model_contracts import loaded_compute_precision
+from vidxp.ports import ModelRuntimePort
+
+
+@dataclass(frozen=True)
+class FineLAPProvider:
+ model: Any
+ device: str
+
+ def encode_audio(
+ self,
+ pcm_windows: Sequence[bytes],
+ ) -> tuple[Any, Any]:
+ """Return normalized global and dense embeddings in one audio pass."""
+ import torch
+ import torchaudio
+
+ mels = []
+ for pcm in pcm_windows:
+ waveform = (
+ torch.frombuffer(bytearray(pcm), dtype=torch.int16)
+ .to(torch.float32)
+ / 32768.0
+ )
+ if waveform.numel() < 400:
+ waveform = torch.nn.functional.pad(
+ waveform,
+ (0, 400 - waveform.numel()),
+ )
+ waveform = waveform - waveform.mean()
+ mel = torchaudio.compliance.kaldi.fbank(
+ waveform.unsqueeze(0),
+ htk_compat=True,
+ sample_frequency=16_000,
+ use_energy=False,
+ window_type="hanning",
+ num_mel_bins=128,
+ dither=0.0,
+ frame_shift=10,
+ )
+ if mel.shape[0] < 1024:
+ mel = torch.nn.functional.pad(
+ mel,
+ (0, 0, 0, 1024 - mel.shape[0]),
+ )
+ else:
+ mel = mel[:1024, :]
+ mels.append((mel - (-4.268)) / (4.569 * 2))
+ mel_batch = torch.stack(mels, dim=0).unsqueeze(1).to(self.device)
+
+ with torch.inference_mode():
+ outputs = self.model.audio_encoder.extract_features(mel_batch)
+ raw = outputs["x"] if isinstance(outputs, dict) else outputs
+ batch, tokens, width = raw[:, 1:, :].shape
+ patches = raw[:, 1:, :].reshape(
+ batch,
+ tokens // 8,
+ 8,
+ width,
+ ).mean(dim=2)
+ features = torch.cat([raw[:, 0:1, :], patches], dim=1)
+ global_embeddings = torch.nn.functional.normalize(
+ self.model.global_audio_proj(features[:, 0, :]),
+ dim=-1,
+ )
+ dense_embeddings = self.model.local_audio_proj(features[:, 1:, :])
+ if self.model.local_audio_proj_type == "rnn":
+ dense_embeddings = dense_embeddings[0]
+ if self.model.config.normalize_dense_audio_embeds:
+ dense_embeddings = torch.nn.functional.normalize(
+ dense_embeddings,
+ dim=-1,
+ )
+ return global_embeddings.cpu(), dense_embeddings.cpu()
+
+ def encode_text(self, query: str) -> list[float]:
+ import torch
+
+ with torch.inference_mode():
+ embedding = self.model.get_global_text_embeds(
+ [query],
+ device=self.device,
+ )
+ return embedding.cpu().numpy().tolist()[0]
+
+
+def _load_finelap_class(snapshot: str, module_cache: str) -> type:
+ from transformers import AutoConfig
+ from transformers import dynamic_module_utils
+
+ dynamic_module_utils.HF_MODULES_CACHE = module_cache
+ dynamic_module_utils.init_hf_modules()
+
+ config = AutoConfig.from_pretrained(
+ snapshot,
+ trust_remote_code=True,
+ local_files_only=True,
+ )
+ class_reference = config.auto_map["AutoModel"]
+ return dynamic_module_utils.get_class_from_dynamic_module(
+ class_reference,
+ snapshot,
+ local_files_only=True,
+ )
+
+
+def _load_finelap_model(
+ model_class: type,
+ snapshot: str,
+ *,
+ config_path: str,
+ vocab_path: str,
+ merges_path: str,
+) -> Any:
+ """Load pinned FineLAP code without an implicit RoBERTa download.
+
+ FineLAP's checkpoint already contains the trained RoBERTa weights, but its
+ constructor asks Transformers to fetch a second copy of roberta-base. The
+ two small tokenizer assets are prepared explicitly; the temporary module
+ factories only initialize the architecture that the FineLAP checkpoint
+ immediately fills.
+ """
+ from transformers import AutoConfig, RobertaConfig, RobertaModel
+ from transformers import RobertaTokenizer
+
+ module = sys.modules[model_class.__module__]
+
+ class OfflineRobertaModel:
+ @classmethod
+ def from_pretrained(cls, *_args: Any, **_kwargs: Any) -> Any:
+ return RobertaModel(
+ RobertaConfig.from_json_file(config_path),
+ add_pooling_layer=False,
+ )
+
+ class OfflineRobertaTokenizer:
+ @classmethod
+ def from_pretrained(cls, *_args: Any, **_kwargs: Any) -> Any:
+ return RobertaTokenizer(
+ vocab_file=vocab_path,
+ merges_file=merges_path,
+ model_max_length=512,
+ )
+
+ module.RobertaModel = OfflineRobertaModel
+ module.RobertaTokenizer = OfflineRobertaTokenizer
+ config = AutoConfig.from_pretrained(
+ snapshot,
+ trust_remote_code=True,
+ local_files_only=True,
+ )
+ return model_class.from_pretrained(
+ snapshot,
+ config=config,
+ local_files_only=True,
+ )
+
+
+def get_sound_model(
+ runtime: ModelRuntimePort,
+ *,
+ download: bool = False,
+ progress: Callable[[dict[str, Any]], None] | None = None,
+) -> FineLAPProvider:
+ device = runtime.device_for("sound")
+ key = FINELAP_MODEL.key(device)
+
+ def load() -> FineLAPProvider:
+ snapshot = runtime.resolve_model(
+ FINELAP_MODEL,
+ download=download,
+ progress=progress,
+ )
+ config_path = runtime.resolve_artifact(
+ ROBERTA_CONFIG,
+ download=download,
+ progress=progress,
+ )
+ vocab_path = runtime.resolve_artifact(
+ ROBERTA_VOCAB,
+ download=download,
+ progress=progress,
+ )
+ merges_path = runtime.resolve_artifact(
+ ROBERTA_MERGES,
+ download=download,
+ progress=progress,
+ )
+ report_preparation(
+ progress,
+ "loading_model",
+ f"Loading {FINELAP_MODEL.model_id}.",
+ )
+ model_class = _load_finelap_class(
+ str(snapshot),
+ str(runtime.model_cache / "transformers_modules"),
+ )
+ model = _load_finelap_model(
+ model_class,
+ str(snapshot),
+ config_path=str(config_path),
+ vocab_path=str(vocab_path),
+ merges_path=str(merges_path),
+ ).to(device)
+ model.eval()
+ runtime.record_compute_precision(
+ FINELAP_MODEL.capability,
+ loaded_compute_precision(
+ model,
+ fallback=FINELAP_MODEL.weights_precision,
+ ),
+ )
+ return FineLAPProvider(model=model, device=device)
+
+ return runtime.get_or_load(key, load)
diff --git a/src/vidxp/capabilities/sound/operations.py b/src/vidxp/capabilities/sound/operations.py
new file mode 100644
index 00000000..89d10e24
--- /dev/null
+++ b/src/vidxp/capabilities/sound/operations.py
@@ -0,0 +1,110 @@
+from __future__ import annotations
+
+from typing import Any, Mapping
+
+from vidxp.capabilities.contracts import (
+ CapabilityContext,
+ CapabilityIndexResult,
+)
+from vidxp.capabilities.registry import CapabilityRegistry
+from vidxp.capabilities.schemas import SearchInput, SearchResult
+from vidxp.capabilities.search import search_embeddings
+from vidxp.capabilities.sound.indexing import index_sound
+from vidxp.capabilities.sound.models import get_sound_model
+from vidxp.core.contracts import (
+ CancellationToken,
+ IndexConfig,
+ VideoSource,
+)
+from vidxp.core.indexing_common import ProgressCallback
+from vidxp.ports import IndexStore, ModelRuntimePort
+
+
+REQUIRED_METADATA = frozenset(
+ {
+ "dataset",
+ "split",
+ "run_id",
+ "video_id",
+ "source_id",
+ "start",
+ "end",
+ "representation",
+ "window_index",
+ "modality",
+ }
+)
+
+
+def index_capability(
+ source: VideoSource,
+ *,
+ config: IndexConfig,
+ storage: IndexStore,
+ cancellation: CancellationToken,
+ registry: CapabilityRegistry,
+ runtime: ModelRuntimePort,
+ progress: ProgressCallback | None = None,
+ modalities: tuple[str, ...] = ("sound",),
+) -> CapabilityIndexResult:
+ if modalities != ("sound",):
+ raise ValueError("The sound indexer only accepts sound.")
+ return CapabilityIndexResult(
+ summary=index_sound(
+ source,
+ config=config,
+ storage=storage,
+ cancellation=cancellation,
+ runtime=runtime,
+ progress=progress,
+ )
+ )
+
+
+def sound_embedding(query: str, runtime: ModelRuntimePort) -> list[float]:
+ return get_sound_model(runtime).encode_text(query)
+
+
+def search_sound(
+ query: str,
+ *,
+ config: IndexConfig,
+ runtime: ModelRuntimePort,
+ top_k: int = 10,
+ video_id: str | None = None,
+ query_id: str | None = None,
+ filters: Mapping[str, Any] | None = None,
+ storage: IndexStore,
+) -> SearchResult:
+ cleaned = query.strip()
+ if not cleaned:
+ raise ValueError("Search query must not be empty.")
+ if top_k <= 0:
+ raise ValueError("top_k must be greater than zero.")
+ return search_embeddings(
+ cleaned,
+ "sound",
+ sound_embedding(cleaned, runtime),
+ config=config,
+ required_metadata=REQUIRED_METADATA,
+ top_k=top_k,
+ video_id=video_id,
+ query_id=query_id,
+ filters=filters,
+ storage=storage,
+ )
+
+
+def search_operation(
+ context: CapabilityContext,
+ request: SearchInput,
+) -> SearchResult:
+ config = context.require_config()
+ return search_sound(
+ request.query,
+ config=config,
+ top_k=request.top_k,
+ video_id=request.media_id or config.video_id,
+ runtime=context.runtime,
+ storage=context.require_storage(),
+ )
diff --git a/src/vidxp/capabilities/sound/requirements.txt b/src/vidxp/capabilities/sound/requirements.txt
new file mode 100644
index 00000000..fb1a650b
--- /dev/null
+++ b/src/vidxp/capabilities/sound/requirements.txt
@@ -0,0 +1,8 @@
+av>=18,<19
+numpy>=2.3,<3
+torch>=2.13,<3
+torchaudio>=2.11,<2.12
+timm>=1.0.20,<2
+transformers>=5.14.1,<6
+huggingface-hub>=1.25.1,<2
+matplotlib>=3.10,<4
diff --git a/src/vidxp/capabilities/sound/specs.py b/src/vidxp/capabilities/sound/specs.py
new file mode 100644
index 00000000..421ea6e9
--- /dev/null
+++ b/src/vidxp/capabilities/sound/specs.py
@@ -0,0 +1,76 @@
+from vidxp.model_contracts import ArtifactSpec, ModelSpec
+
+
+FINELAP_MODEL = ModelSpec(
+ capability="sound.embedding",
+ provider="transformers",
+ model_id="AndreasXi/FineLAP",
+ revision="b419aa22947d29907a5567f21b81bf3b39a40449",
+ download_size_bytes=980_404_741,
+ weights_file="model.safetensors",
+ weights_sha256=(
+ "13b9646c9f9d48513c0145bed75e654179e83f0fd8d49ed4ffc5d6b8f3353fb4"
+ ),
+ license="MIT (model card)",
+ weights_precision="float32",
+)
+
+ROBERTA_REVISION = "e2da8e2f811d1448a5b465c236feacd80ffbac7b"
+ROBERTA_CONFIG = ArtifactSpec(
+ capability="sound.text_encoder.config",
+ provider="finelap-tokenizer",
+ model_id="FacebookAI/roberta-base config",
+ revision=ROBERTA_REVISION,
+ download_size_bytes=481,
+ url=(
+ "https://huggingface.co/FacebookAI/roberta-base/resolve/"
+ f"{ROBERTA_REVISION}/config.json"
+ ),
+ filename="config.json",
+ sha256=(
+ "ef0185e2aae6e06c5f105a285006952c340e20c7dbf43c86ec82601b13fc45e9"
+ ),
+ license="MIT",
+ weights_precision="not applicable",
+)
+ROBERTA_VOCAB = ArtifactSpec(
+ capability="sound.tokenizer.vocab",
+ provider="finelap-tokenizer",
+ model_id="FacebookAI/roberta-base vocab",
+ revision=ROBERTA_REVISION,
+ download_size_bytes=898_823,
+ url=(
+ "https://huggingface.co/FacebookAI/roberta-base/resolve/"
+ f"{ROBERTA_REVISION}/vocab.json"
+ ),
+ filename="vocab.json",
+ sha256=(
+ "9e7f63c2d15d666b52e21d250d2e513b87c9b713cfa6987a82ed89e5e6e50655"
+ ),
+ license="MIT",
+ weights_precision="not applicable",
+)
+ROBERTA_MERGES = ArtifactSpec(
+ capability="sound.tokenizer.merges",
+ provider="finelap-tokenizer",
+ model_id="FacebookAI/roberta-base merges",
+ revision=ROBERTA_REVISION,
+ download_size_bytes=456_318,
+ url=(
+ "https://huggingface.co/FacebookAI/roberta-base/resolve/"
+ f"{ROBERTA_REVISION}/merges.txt"
+ ),
+ filename="merges.txt",
+ sha256=(
+ "1ce1664773c50f3e0cc8842619a93edc4624525b728b188a9e0be33b7726adc5"
+ ),
+ license="MIT",
+ weights_precision="not applicable",
+)
+
+SOUND_MODEL_SPECS = (
+ FINELAP_MODEL,
+ ROBERTA_CONFIG,
+ ROBERTA_VOCAB,
+ ROBERTA_MERGES,
+)
diff --git a/src/vidxp/frontend.py b/src/vidxp/frontend.py
index a9fa7f20..ae3d4b04 100644
--- a/src/vidxp/frontend.py
+++ b/src/vidxp/frontend.py
@@ -133,6 +133,7 @@ def _settings_from_arguments(
"dialogue": "Dialogue search",
"natural-language": "Ask a question",
"scene": "Scene search",
+ "sound": "Sound event search (FineLAP)",
"videoprism": "Temporal action search (VideoPrism)",
}
@@ -907,7 +908,7 @@ def run():
service = _configured_service()
st.title("VidXP")
st.caption(
- "Index and search video by dialogue, scene, temporal clips, and actor."
+ "Index and search video by dialogue, sound, scenes, actions, and actor."
)
st.caption(f"Index repository: {service.layout.root}")
if notice := st.session_state.pop(MEDIA_NOTICE_KEY, None):
diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py
index 9edd0d72..dc7a10cc 100644
--- a/tests/test_capabilities.py
+++ b/tests/test_capabilities.py
@@ -25,6 +25,7 @@
)
from vidxp.capability_service import CapabilityService
from vidxp.capabilities.scene.config import SceneConfig
+from vidxp.capabilities.sound.config import SoundConfig
from vidxp.capabilities.videoprism.config import VideoPrismConfig
from vidxp.core.contracts import IndexConfig
from vidxp.core.runner import _index_groups
@@ -63,17 +64,18 @@ def test_module_import_checks_run_in_an_isolated_process(self):
def test_registry_drives_capability_metadata(self):
self.assertEqual(
self.registry.names(),
- ("dialogue", "scene", "actor", "videoprism"),
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
)
self.assertEqual(self.registry.index_names(), self.registry.names())
self.assertEqual(
self.registry.preparable_names(),
- ("dialogue", "scene", "actor", "videoprism"),
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
)
self.assertEqual(
self.registry.collection_names(),
{
"dialogue": "dialogue",
+ "sound": "sound",
"scene": "scene",
"actor": "actor",
"videoprism": "videoprism",
@@ -142,6 +144,7 @@ def test_built_in_settings_are_owned_and_validated(self):
)
self.assertIs(self.registry.get("scene").config_model, SceneConfig)
self.assertIs(self.registry.get("actor").config_model, ActorConfig)
+ self.assertIs(self.registry.get("sound").config_model, SoundConfig)
self.assertIs(
self.registry.get("videoprism").config_model,
VideoPrismConfig,
@@ -213,10 +216,14 @@ def test_operation_only_capability_needs_no_index_metadata(self):
def test_visual_execution_group_is_explicit(self):
self.assertEqual(
_index_groups(
- ("dialogue", "scene", "actor", "videoprism"),
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
self.registry,
),
- (("dialogue",), ("scene", "actor", "videoprism")),
+ (
+ ("dialogue",),
+ ("sound",),
+ ("scene", "actor", "videoprism"),
+ ),
)
self.assertIsNotNone(
self.registry.executor("scene").index_processor
@@ -227,6 +234,7 @@ def test_visual_execution_group_is_explicit(self):
self.assertIsNone(
self.registry.executor("dialogue").index_processor
)
+ self.assertIsNone(self.registry.executor("sound").index_processor)
def test_core_config_has_no_provider_specific_fields(self):
fields = IndexConfig.__dataclass_fields__
diff --git a/tests/test_frontend.py b/tests/test_frontend.py
index 464384a3..d03329a0 100644
--- a/tests/test_frontend.py
+++ b/tests/test_frontend.py
@@ -90,12 +90,12 @@ def test_query_modalities_use_real_capability_service_contracts(self):
return_value=service,
):
available = frontend._available_query_modalities(
- ("dialogue", "scene", "actor", "videoprism"),
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
)
self.assertEqual(
available,
- ("dialogue", "scene", "actor", "videoprism"),
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
)
def tearDown(self):
@@ -339,7 +339,10 @@ def check(command):
):
available = frontend._available_index_modalities()
- self.assertEqual(available, ("dialogue", "scene", "videoprism"))
+ self.assertEqual(
+ available,
+ ("dialogue", "sound", "scene", "videoprism"),
+ )
self.assertTrue(
all(
not call.args[0].include_runtime_checks
diff --git a/tests/test_frontend_app.py b/tests/test_frontend_app.py
index 0b74bcd7..bb300601 100644
--- a/tests/test_frontend_app.py
+++ b/tests/test_frontend_app.py
@@ -161,7 +161,7 @@ def ready_status() -> IndexStatus:
snapshot_id=SNAPSHOT_ID,
media_count=1,
media_ids=(MEDIA_ID,),
- modalities=("dialogue", "scene", "actor", "videoprism"),
+ modalities=("dialogue", "sound", "scene", "actor", "videoprism"),
),
)
diff --git a/tests/test_local_probe.py b/tests/test_local_probe.py
index 2832c404..8b23d90f 100644
--- a/tests/test_local_probe.py
+++ b/tests/test_local_probe.py
@@ -247,7 +247,7 @@ def test_non_windows_launcher_resolution_does_not_add_executable_suffix(self):
def test_desktop_model_catalog_is_derived_from_canonical_specs(self):
catalog = desktop_model_cache_catalog()
- self.assertEqual(len(catalog), 6)
+ self.assertEqual(len(catalog), 10)
self.assertEqual(
{item["id"] for item in catalog},
{
@@ -255,6 +255,10 @@ def test_desktop_model_catalog_is_derived_from_canonical_specs(self):
"google/videoprism-lvt-base-f16r288",
"Qwen/Qwen3-Embedding-0.6B",
"dropbox-dash/faster-whisper-large-v3-turbo",
+ "AndreasXi/FineLAP",
+ "FacebookAI/roberta-base config",
+ "FacebookAI/roberta-base vocab",
+ "FacebookAI/roberta-base merges",
"yunet",
"sface",
},
diff --git a/tests/test_mcp.py b/tests/test_mcp.py
index 3f44ec3d..d480138d 100644
--- a/tests/test_mcp.py
+++ b/tests/test_mcp.py
@@ -2734,7 +2734,7 @@ async def test_stdio_entrypoint_serves_the_filesystem_aware_surface(self):
)
self.assertEqual(
[item["name"] for item in result.structured_content["items"]],
- ["dialogue", "scene", "actor", "videoprism"],
+ ["dialogue", "sound", "scene", "actor", "videoprism"],
)
async def test_streamable_http_works_with_the_official_remote_client(self):
diff --git a/tests/test_sound.py b/tests/test_sound.py
new file mode 100644
index 00000000..165f1a66
--- /dev/null
+++ b/tests/test_sound.py
@@ -0,0 +1,234 @@
+from pathlib import Path
+from tempfile import TemporaryDirectory
+import unittest
+from unittest.mock import Mock, patch
+import wave
+
+from vidxp.capabilities.sound.config import SoundConfig
+from vidxp.capabilities.sound.indexing import (
+ AudioWindow,
+ index_sound,
+ iter_audio_windows,
+ sound_records,
+)
+from vidxp.capabilities.sound.operations import search_sound
+from vidxp.capabilities.sound.specs import (
+ FINELAP_MODEL,
+ ROBERTA_CONFIG,
+ ROBERTA_MERGES,
+ ROBERTA_VOCAB,
+)
+from vidxp.core.contracts import CancellationToken, IndexConfig, VideoSource
+
+
+MEDIA_ID = "123456781234423481234567890abcde"
+GENERATION_ID = "223456781234423481234567890abcde"
+
+
+class Vector(list):
+ def tolist(self):
+ return list(self)
+
+
+class SoundTests(unittest.TestCase):
+ def config(self, **options):
+ return IndexConfig.local(
+ video_id=MEDIA_ID,
+ enabled_modalities=("sound",),
+ generation_id=GENERATION_ID,
+ capability_options={"sound": options},
+ )
+
+ def test_config_limits_windows_to_finelap_input_length(self):
+ self.assertEqual(SoundConfig().window_seconds, 10.0)
+ with self.assertRaises(ValueError):
+ SoundConfig(window_seconds=10.1)
+
+ def test_specs_pin_model_and_explicit_tokenizer_assets(self):
+ self.assertEqual(FINELAP_MODEL.model_id, "AndreasXi/FineLAP")
+ self.assertEqual(len(FINELAP_MODEL.revision), 40)
+ self.assertEqual(ROBERTA_VOCAB.revision, ROBERTA_MERGES.revision)
+ self.assertEqual(ROBERTA_CONFIG.revision, ROBERTA_VOCAB.revision)
+ self.assertIn(ROBERTA_CONFIG.revision, ROBERTA_CONFIG.url)
+ self.assertIn(ROBERTA_VOCAB.revision, ROBERTA_VOCAB.url)
+ self.assertIn(ROBERTA_MERGES.revision, ROBERTA_MERGES.url)
+
+ def test_records_include_window_and_dense_activation_intervals(self):
+ config = self.config()
+ windows = (
+ AudioWindow(0, 0.0, 10.0, b""),
+ AudioWindow(1, 10.0, 12.0, b""),
+ )
+ global_embeddings = (Vector([1.0, 0.0]), Vector([0.5, 0.5]))
+ dense_embeddings = (
+ [Vector([1.0, 0.0]) for _ in range(64)],
+ [Vector([0.5, 0.5]) for _ in range(64)],
+ )
+
+ records = sound_records(
+ windows,
+ global_embeddings,
+ dense_embeddings,
+ config,
+ )
+
+ self.assertEqual(len(records), 78)
+ self.assertEqual(records[0].metadata["representation"], "window")
+ self.assertEqual(records[1].metadata["representation"], "activation")
+ self.assertEqual(records[1].metadata["start"], 0.0)
+ self.assertEqual(records[1].metadata["end"], 0.16)
+ self.assertAlmostEqual(records[-1].metadata["start"], 11.92)
+ self.assertEqual(records[-1].metadata["end"], 12.0)
+ self.assertTrue(
+ all(record.metadata["generation_id"] == GENERATION_ID for record in records)
+ )
+
+ def test_audio_decode_resamples_and_preserves_source_duration(self):
+ with TemporaryDirectory() as directory:
+ path = Path(directory) / "sample.wav"
+ with wave.open(str(path), "wb") as output:
+ output.setnchannels(1)
+ output.setsampwidth(2)
+ output.setframerate(8_000)
+ output.writeframes(b"\0\0" * 4_000)
+
+ windows = list(
+ iter_audio_windows(
+ path,
+ window_seconds=10.0,
+ cancellation=CancellationToken(),
+ )
+ )
+
+ self.assertEqual(len(windows), 1)
+ self.assertEqual(windows[0].start, 0.0)
+ self.assertEqual(windows[0].end, 0.5)
+ self.assertEqual(len(windows[0].pcm), 16_000)
+
+ def test_index_stores_global_and_dense_records_in_one_collection(self):
+ config = self.config()
+ windows = (
+ AudioWindow(0, 0.0, 10.0, b"\0\0" * 16),
+ AudioWindow(1, 10.0, 12.0, b"\0\0" * 16),
+ )
+ provider = Mock()
+ provider.encode_audio.return_value = (
+ (Vector([1.0]), Vector([2.0])),
+ (
+ [Vector([1.0]) for _ in range(64)],
+ [Vector([2.0]) for _ in range(64)],
+ ),
+ )
+ storage = Mock()
+ storage.upsert.return_value = 78
+
+ with (
+ TemporaryDirectory() as directory,
+ patch(
+ "vidxp.capabilities.sound.indexing.iter_audio_windows",
+ return_value=iter(windows),
+ ),
+ patch(
+ "vidxp.capabilities.sound.indexing.get_sound_model",
+ return_value=provider,
+ ),
+ ):
+ summary = index_sound(
+ VideoSource(path=Path(directory) / "video.mp4"),
+ config=config,
+ storage=storage,
+ cancellation=CancellationToken(),
+ runtime=Mock(),
+ )
+
+ self.assertEqual(
+ summary,
+ {"sound_windows": 2, "sound_activations": 76},
+ )
+ self.assertEqual(storage.upsert.call_count, 2)
+ self.assertTrue(
+ all(call.args[0] == "sound" for call in storage.upsert.call_args_list)
+ )
+ self.assertEqual(
+ sum(len(call.args[1]) for call in storage.upsert.call_args_list),
+ 78,
+ )
+
+ def test_index_skips_media_without_audio_before_loading_model(self):
+ with (
+ patch(
+ "vidxp.capabilities.sound.indexing.iter_audio_windows",
+ return_value=iter(()),
+ ),
+ patch(
+ "vidxp.capabilities.sound.indexing.get_sound_model",
+ ) as get_model,
+ ):
+ summary = index_sound(
+ VideoSource(path="silent.mp4"),
+ config=self.config(),
+ storage=Mock(),
+ cancellation=CancellationToken(),
+ runtime=Mock(),
+ )
+
+ self.assertEqual(
+ summary,
+ {"sound_windows": 0, "sound_activations": 0},
+ )
+ get_model.assert_not_called()
+
+ def test_sound_search_uses_shared_search_contract_and_public_metadata(self):
+ config = self.config()
+ storage = Mock()
+ storage.query.return_value = [
+ {
+ "source_id": "sound:1",
+ "raw_distance": 0.2,
+ "metadata": {
+ **config.record_identity("sound", "sound:1"),
+ "generation_id": GENERATION_ID,
+ "representation": "activation",
+ "window_index": 3,
+ "activation_index": 9,
+ "start": 31.4,
+ "end": 31.6,
+ "private": "hidden",
+ },
+ }
+ ]
+ provider = Mock()
+ provider.encode_text.return_value = [0.1, 0.2]
+
+ with patch(
+ "vidxp.capabilities.sound.operations.get_sound_model",
+ return_value=provider,
+ ):
+ result = search_sound(
+ "dog barking",
+ config=config,
+ runtime=Mock(),
+ storage=storage,
+ )
+
+ self.assertEqual(result.modality, "sound")
+ self.assertEqual(result.hits[0].start, 31.4)
+ self.assertEqual(
+ result.hits[0].metadata,
+ {
+ "representation": "activation",
+ "window_index": 3,
+ "activation_index": 9,
+ },
+ )
+ storage.query.assert_called_once_with(
+ "sound",
+ [0.1, 0.2],
+ top_k=10,
+ video_id=None,
+ filters=None,
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/uv.lock b/uv.lock
index f2dcccdb..f1867cd7 100644
--- a/uv.lock
+++ b/uv.lock
@@ -600,6 +600,89 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
+[[package]]
+name = "contourpy"
+version = "1.3.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
+ { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
+ { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
+ { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
+ { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
+ { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
+ { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
+ { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
+ { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
+ { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
+ { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
+ { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
+ { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
+ { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
+ { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
+ { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
+ { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
+ { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
+ { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
+ { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
+ { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
+ { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
+ { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
+ { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
+ { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
+ { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
+ { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
+ { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
+ { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
+ { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
+]
+
[[package]]
name = "cryptography"
version = "50.0.0"
@@ -694,6 +777,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/03/126e963fc3237a416f3085b8a663ebd8ab449ed6c37195b4e0b49597ba0c/ctranslate2-4.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:dc9f1abef55579cc02cdc74b3a55df38491ec56d177d6e6039609d61d09ed30e", size = 19499597, upload-time = "2026-07-03T12:40:01.68Z" },
]
+[[package]]
+name = "cycler"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
+]
+
[[package]]
name = "dbos"
version = "2.29.0"
@@ -778,6 +870,55 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" },
]
+[[package]]
+name = "fonttools"
+version = "4.63.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" },
+ { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" },
+ { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" },
+ { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" },
+ { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" },
+ { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" },
+ { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" },
+ { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" },
+ { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" },
+ { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" },
+ { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" },
+ { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" },
+ { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" },
+ { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" },
+ { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" },
+ { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" },
+ { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" },
+]
+
[[package]]
name = "frozenlist"
version = "1.8.0"
@@ -1381,6 +1522,112 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
]
+[[package]]
+name = "kiwisolver"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" },
+ { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" },
+ { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" },
+ { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" },
+ { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" },
+ { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" },
+ { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" },
+ { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" },
+ { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" },
+ { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" },
+ { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" },
+ { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" },
+ { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" },
+ { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" },
+ { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" },
+ { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" },
+ { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" },
+ { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" },
+ { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" },
+ { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" },
+]
+
[[package]]
name = "kubernetes"
version = "36.0.3"
@@ -1509,6 +1756,71 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
+[[package]]
+name = "matplotlib"
+version = "3.11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "contourpy" },
+ { name = "cycler" },
+ { name = "fonttools" },
+ { name = "kiwisolver" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
+ { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
+ { name = "packaging" },
+ { name = "pillow" },
+ { name = "pyparsing" },
+ { name = "python-dateutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/49/64/f9a391af28f518b11ad45a8a712353c94a0aefce09d3703200e5c54b610a/matplotlib-3.11.1.tar.gz", hash = "sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30", size = 32612045, upload-time = "2026-07-18T03:39:46.63Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6e/d0/791aa183dd88491555cf7d4be0b52b0bcf6c3c2a2c22c815a2e819bf53e2/matplotlib-3.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b7cf158e7add54a8d51ac9b5a84abd6d4e13ed4951b4f25f1c5139f41c2addb2", size = 9440302, upload-time = "2026-07-18T03:38:03.844Z" },
+ { url = "https://files.pythonhosted.org/packages/35/74/82bbdf683a301f4478384c8aaba6903631a2ca18294b2d7655c9a542bffb/matplotlib-3.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d2ace7273b9a5061a3b420918a16fae1f2dc5dfee1abcc13aba71b5d94b1820c", size = 9268549, upload-time = "2026-07-18T03:38:06.144Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f0/9b4298911303f74e6d83e64a81d996c0616405ec95046fac7f17e4258b9e/matplotlib-3.11.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee55e9041211bf84302ab55ec3965df18dd90ae19f8b58332a7feaf208bfe83", size = 10024922, upload-time = "2026-07-18T03:38:08.236Z" },
+ { url = "https://files.pythonhosted.org/packages/84/6f/0bc3c3d05b021db44c14bc379a7c0df7d57302aa15380c16fd4e63fd6a9b/matplotlib-3.11.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f4bdeea33a8d15a071dbfe6d119451b1d719c733ac666d65357082901a9099", size = 10832170, upload-time = "2026-07-18T03:38:10.276Z" },
+ { url = "https://files.pythonhosted.org/packages/db/4d/e375f39acdb2af5a9342730618608e39790ec842e6f1b392863028781459/matplotlib-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b4c78ceb2f11bcac7389d305cda17aeb1f4586a857854ab5780bd3dd8dbfc407", size = 10916701, upload-time = "2026-07-18T03:38:12.512Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/be/fa26ed085b41298f64a8f9b7592c671bbf1acc8b0df124c1c5de96b859f8/matplotlib-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f33a781e12b1e53b278deb2f5373c2e55ec4f10727be3440c0cfb5cda9f944f", size = 9315331, upload-time = "2026-07-18T03:38:14.949Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/f3/eb5bdf3b6e191b200db298b08bbc1638b7f3c82cdc8680f9d88bf72559ae/matplotlib-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:67e4c3cd578c65ebd81bdc09a1b6592ceafee6dfafe116dc85dfcb647b5bbb18", size = 9003475, upload-time = "2026-07-18T03:38:17.205Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6c/7ef7ebcb2bd9739b2b66b18b076e077f44bb46fdbe28ca0506edb3c62c79/matplotlib-3.11.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e15ef41507f3d525f46154ac9e3ae785dacde9f20e593a25de8986267892ef74", size = 9453849, upload-time = "2026-07-18T03:38:19.593Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/f8/6d0c312c8d9738e7d9677f09fe5c986b3239e651a7b73a2deb38b65e4a71/matplotlib-3.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21a67b961a6d597bca54fae826cd20695ba4a6e4d05424a08da6e13e3176fd6b", size = 9283113, upload-time = "2026-07-18T03:38:21.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/cf/b4ad2cc81b6672ea29ea04e64e350a9f9b493b0908ccd884c67eeff8f7b2/matplotlib-3.11.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba8f811b8ddfac493734d6af0b2dff96919d0c28ca0d641858dab4262777c6ea", size = 10035615, upload-time = "2026-07-18T03:38:24.315Z" },
+ { url = "https://files.pythonhosted.org/packages/88/90/4e10e033d9b66589d8ed98b84c95cdbb57033d57c1f41339d7393dbd2f2e/matplotlib-3.11.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c52f7ad20ef476806ed212380b1d54d20310c8b86bdc2c9a68b51f0024a44472", size = 10842559, upload-time = "2026-07-18T03:38:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/88/eb/799612d0f8cd3e816a10fec59329fca52cd2353264df80378dfc541ae855/matplotlib-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8b14eb22961fe865efb0e4ff167e333e428908b00115a8d800ccb65ee108e481", size = 10927532, upload-time = "2026-07-18T03:38:28.532Z" },
+ { url = "https://files.pythonhosted.org/packages/88/89/56649bbaa2fd12e20f3be03dbcc135b0c8676d88bac17977599e3eb442a0/matplotlib-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88a2a27dd9691ae448dfae4b26f59036be90c3c28757edd3553a29559d00859f", size = 9333886, upload-time = "2026-07-18T03:38:30.477Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/11/4d124efbbad677b7b7552f6f85a3bd432d4232f95400cea98fcd2ae36ef3/matplotlib-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:480194afceca4df2f137c2721227d3cba67121fbf4397b69cee7f83714b0a58a", size = 9007545, upload-time = "2026-07-18T03:38:32.833Z" },
+ { url = "https://files.pythonhosted.org/packages/04/6c/4798363b7fb5644e309fe1fac30216e9146c9f70859d80d588c18caf5317/matplotlib-3.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191", size = 9454341, upload-time = "2026-07-18T03:38:35.001Z" },
+ { url = "https://files.pythonhosted.org/packages/59/98/6acadbe7f98df19d274bc107ac58bb439fa75df82c33dc110d71a4a8501f/matplotlib-3.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1", size = 9283627, upload-time = "2026-07-18T03:38:37.061Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ea/65cec46fe241390ccea1b1754207ee28eb71c5ab866bd5f22fe47e538fa4/matplotlib-3.11.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3", size = 10035860, upload-time = "2026-07-18T03:38:39.663Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/10/63fdccccbabe002fb0960876baabc5e3f24d9c1bb4cfb25651457f74b3a0/matplotlib-3.11.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e", size = 10843594, upload-time = "2026-07-18T03:38:42.144Z" },
+ { url = "https://files.pythonhosted.org/packages/98/51/a1155945bff7b91381875022ac1522c5dfdac0d006be8e7df389b3134eae/matplotlib-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f", size = 10927962, upload-time = "2026-07-18T03:38:44.302Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/3a/3d5e1f42dc761bf53401a62a83ff93389b37de9d2c093b2a3aa49ac34f1b/matplotlib-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b", size = 9334074, upload-time = "2026-07-18T03:38:46.616Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/db/3f5ea5a5b64060ef5e1ff60a19170423e41ce21b8497a6fe15a36e0b43e3/matplotlib-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2", size = 9007662, upload-time = "2026-07-18T03:38:49.112Z" },
+ { url = "https://files.pythonhosted.org/packages/98/6e/c7ae5e0531425b69c0826b00ebbc264c85cab853f1cd6e096c9983c2cdc1/matplotlib-3.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f", size = 9503790, upload-time = "2026-07-18T03:38:51.527Z" },
+ { url = "https://files.pythonhosted.org/packages/92/79/15be162e0a2ed546939674e2e97d0e33ec2447d86d4d4e611fa295bb178c/matplotlib-3.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1", size = 9336148, upload-time = "2026-07-18T03:38:53.564Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/7f/36ffe144fc4aacfe0e3ed2318f72b6755d1e73b041d619b4d393e60f5a66/matplotlib-3.11.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464", size = 10049244, upload-time = "2026-07-18T03:38:55.911Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/55812d68c0a840d3a463638f48c00ab1fe338518ec49a640cb6473b444af/matplotlib-3.11.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf", size = 10860798, upload-time = "2026-07-18T03:38:58.282Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/64/cca444b4eb5e6c768c44fc5e1f0b5211f20ca2b282778051996e996a2bdf/matplotlib-3.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f", size = 10943282, upload-time = "2026-07-18T03:39:00.465Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/0f/a49c329d394f2e9ef38506982107e8b04ecf94dd41a9d8423ff82cc737c7/matplotlib-3.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78", size = 9383532, upload-time = "2026-07-18T03:39:02.468Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/50/103e86afb806d8f64d04ede14e4cfc09dbfc25f512421ff85fdd6ebd59cf/matplotlib-3.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a", size = 9059665, upload-time = "2026-07-18T03:39:04.607Z" },
+ { url = "https://files.pythonhosted.org/packages/35/04/3079499fa8cb661ea66d13d6439d5a3ae6710a7afd5c7f72e08914f275f8/matplotlib-3.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:30c492d4ba9448595b6fd8708c6725963f8148e25c0d8842948da5b05f0ee8d3", size = 9456022, upload-time = "2026-07-18T03:39:07.041Z" },
+ { url = "https://files.pythonhosted.org/packages/53/a2/69acfe84ec1f32930e801a5782a07fc5c79c8c6599a507b806d859d5da8e/matplotlib-3.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ac104be2768ffdd8655db9e71b768cbb45f2b9aa7b450cf1595e8f65d3822319", size = 9285475, upload-time = "2026-07-18T03:39:09.562Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/b3/31b15a2ca56d4ddd6aaa1c884c2f51cf9a61cfaf5ca6f6fbd6343d38e6df/matplotlib-3.11.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be943cb68bc6660ead58c55b3aa6366cba2ef7feb06460fbcce32360376f19f", size = 10847102, upload-time = "2026-07-18T03:39:11.532Z" },
+ { url = "https://files.pythonhosted.org/packages/64/0d/a17e966e620545c1548125af0b29ac812dd17b197a18a7462ac12fa859ee/matplotlib-3.11.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5af0dcda57d471440a7b5b623e70e0a61003518443d9098f211a96ecfbbc25be", size = 11131087, upload-time = "2026-07-18T03:39:13.764Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c5/5e100efdd67abb7de20befaa333612ef9bfc63417fb71398f904f25d083c/matplotlib-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3d3fd84082b1afbd9398466c81309e20045be20d48fe0fb18c43504d164cbbb2", size = 10929036, upload-time = "2026-07-18T03:39:16.888Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/04/d719a0a36930ecc8dfc801ff340f9dcfc4223f8ca5d39d06b4020032fff8/matplotlib-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:9601a1e90be21e4884c53b4f3dc3ee0544654946f9975258d691f1c2e2f119c6", size = 9489571, upload-time = "2026-07-18T03:39:19.449Z" },
+ { url = "https://files.pythonhosted.org/packages/48/65/facabdc2f1f6caba7e856db64dfedddca25f7608df07d96a1c8fd114fd3b/matplotlib-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:ae30c6109848ac0f9fa36c5d6270938487614c47ba31860bd5361266dabc5685", size = 9164486, upload-time = "2026-07-18T03:39:21.424Z" },
+ { url = "https://files.pythonhosted.org/packages/88/dd/18da6cd01cf96354534f98c468a25380c68ce582a2c9dd0cae12b04af4f2/matplotlib-3.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dadfe80797174e2984aae3be0b77594a3c72d2c0a40fbd4a0de48d2728caf3ae", size = 9504876, upload-time = "2026-07-18T03:39:23.633Z" },
+ { url = "https://files.pythonhosted.org/packages/79/b0/f0b63555a18b79d038c81fd6126f35fc4dfce0eaff48d96103348c7cf935/matplotlib-3.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:89b193b255f4f6f7948dbcee3691f4f341ab05d9a8874a67b45ddb4182922eda", size = 9336120, upload-time = "2026-07-18T03:39:25.797Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/dd/f210ec7c4a6f198d5567237048a93d0811fb5a1f1691f13320e592f95b41/matplotlib-3.11.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191163532cdefcb1571ca38a6d7e6474baccde64495783e6ba47aa07ec4b9bbb", size = 10858033, upload-time = "2026-07-18T03:39:27.999Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/d2/d6d5324507c5fbb316db48e258c09c2807f3de03d9af47017e120070926f/matplotlib-3.11.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fdf1c818ab05d0e74002091ddaf414478a3a449ec9d51c8976d45be7e3a01e2", size = 11141827, upload-time = "2026-07-18T03:39:30.092Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/68/3c22e9320bdce2c4d2f1320643ef706db7a24cb7420eea28b97a2d67f5a8/matplotlib-3.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b937b9dba5f5f6c1e31c47abe2186c865c0914fd18f2ce0dfc39c9adcef5951d", size = 10943061, upload-time = "2026-07-18T03:39:32.356Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/4a/907ed190ee81a9df581e0ed5456134fc0f7cb55ffcfda2f9e54ca900761c/matplotlib-3.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f2912f647f3fbe1ccf085f91e213936f9101bead81a5e670565b1f1b3712f4fb", size = 9540074, upload-time = "2026-07-18T03:39:34.789Z" },
+ { url = "https://files.pythonhosted.org/packages/23/d4/97c19b77e0a6e3b48581185bb65088f431cd20186076cc0f650a1757ea46/matplotlib-3.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:54d47b8ae8b579633a3902ca5b4ad6c1e132a5626d64447b2e22a66394e79987", size = 9213472, upload-time = "2026-07-18T03:39:37.141Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/38/ceb1d637c4db6d06141f3739e93af3321e7caaabe69b57ae48ffe3ee95b1/matplotlib-3.11.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:427258425f9a3fc4ed79a91f9e9b9aaf5a82cb6571e85dc14063cc6fbb993741", size = 9438045, upload-time = "2026-07-18T03:39:39.491Z" },
+ { url = "https://files.pythonhosted.org/packages/89/25/72ad8b58602d3a6ef1dfc4b65ecd01634ab65a2bdf494c9fe0e966dbf081/matplotlib-3.11.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:1ac697e591c11b6ad04679a73c2d2f9980fe9d9f0311fb414a2e329706343dfb", size = 9266127, upload-time = "2026-07-18T03:39:41.597Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/6d/69552382fcc8e93d1f2763ef2665980a900a48b7f3a4c57ed290726d1cbc/matplotlib-3.11.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4b9ac2f1f607ecda2af90a5232beee2af7582fce1cc30c4b6a1b012dc21ee99", size = 10019439, upload-time = "2026-07-18T03:39:43.78Z" },
+]
+
[[package]]
name = "mcp"
version = "2.0.0"
@@ -2981,6 +3293,15 @@ crypto = [
{ name = "cryptography" },
]
+[[package]]
+name = "pyparsing"
+version = "3.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
+]
+
[[package]]
name = "pypika"
version = "0.51.1"
@@ -3848,6 +4169,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/16/5b/f2aa703a4fc5d2dff73460a7d46cc2f3f44aa0f3dd8eeb20d2a0ecf68862/tiktoken-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:85b78cc3a2c3d48723ca751fa981f1fedccd54194ca0471b957364353a898b07", size = 918110, upload-time = "2026-05-15T04:51:17.237Z" },
]
+[[package]]
+name = "timm"
+version = "1.0.28"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "huggingface-hub" },
+ { name = "pyyaml" },
+ { name = "safetensors" },
+ { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/35/03/e41389ac641747bfec48d016fde8be1eade1901e6f2c1aedcb0c8cb4b5d9/timm-1.0.28.tar.gz", hash = "sha256:3789d313fdd5541a327b60180d70dbb4bdec73db8ff0655e413db3c3d134a9a4", size = 2451413, upload-time = "2026-07-11T17:24:32.615Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/76/de1bfac17d183c49c6d0887903d3064ced51cf1d9ba7a8d611c1a8808c4f/timm-1.0.28-py3-none-any.whl", hash = "sha256:e577b88da96b3a722ea5e2f042455ce6f715d398304d8e63b17d126ed7d89968", size = 2597944, upload-time = "2026-07-11T17:24:30.869Z" },
+]
+
[[package]]
name = "tokenizers"
version = "0.22.2"
@@ -3955,6 +4294,56 @@ wheels = [
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp314-cp314t-win_amd64.whl", hash = "sha256:a43376bd094124ef626bfdd3d4c2c62eacb0b5ddc99776f4a32d4fd16f1f3420", upload-time = "2026-07-08T19:31:48Z" },
]
+[[package]]
+name = "torchaudio"
+version = "2.11.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
+ "python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
+ "python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
+ "python_full_version < '3.12' and sys_platform != 'linux' and sys_platform != 'win32'",
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/77/0eec7f175d88f312296bd5b11c23bd58da37c1021f53da3db4df449ce3ee/torchaudio-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:492dd64645e9d0bb843e94f1d9a4d1e31426262ffc594fafecc1697df9df5eb9", size = 684142, upload-time = "2026-03-23T18:13:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b1/77658817acacd01a72b714440c62f419efc4d90170e704e8e7a2c0918988/torchaudio-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1cf1acc883bee9cb906a933572fed6a8a933f86ef34e9ea7d803f72317e8c1b", size = 684226, upload-time = "2026-03-23T18:13:40.023Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/9e/f76fcd9877c8c78f258ee34e0fb8291fdb91e6218d582d9ca66b1e4bd4ae/torchaudio-2.11.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e3f9696a9ef1d49acc452159b052370c636406d072e9d8f10895fda87b591ea9", size = 679904, upload-time = "2026-03-23T18:13:28.329Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/ce/52c652d30af7d6e96c8f1735d26131e94708e3f38d852b8fa97958804dd8/torchaudio-2.11.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:bda09ea630ae7207384fb0f28c35e4f8c0d82dd6eba020b6b335ad0caa9fed49", size = 680814, upload-time = "2026-03-23T18:13:17.08Z" },
+ { url = "https://files.pythonhosted.org/packages/39/fe/ffa618b4f0d9732d7df7a2fa2bd48657d896599bc224e5af3c70d46c546b/torchaudio-2.11.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:cc09cd1f6015b8549e7fe255fb1be5346b57e7fee06541d3f3dbb012d8c4715f", size = 679901, upload-time = "2026-03-23T18:13:25.472Z" },
+ { url = "https://files.pythonhosted.org/packages/60/84/1c792b0b700eac9a96772cfd9f96c097b17bca3234a2fde3c64b8063660d/torchaudio-2.11.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:da2725e250866da42a12934c9a6552f65a18b7187fd7a6221387f0e605fb3b96", size = 679926, upload-time = "2026-03-23T18:13:24.452Z" },
+]
+
+[[package]]
+name = "torchaudio"
+version = "2.11.0+cpu"
+source = { registry = "https://download.pytorch.org/whl/cpu" }
+resolution-markers = [
+ "(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
+ "(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
+ "(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
+ "(python_full_version < '3.12' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform == 'win32')",
+]
+wheels = [
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a5ecdd20fb26d8eb1d62135ddc6db0f806b17667c10e82df913fd509d1f5cfe", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2ddcb49458aa9783121222c535c0eb9cd54445ec44bb8dd22c9e52cdb730dbc2", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp311-cp311-win_amd64.whl", hash = "sha256:abbff04127caf6e16e3a7c3e1ca2739b68899e14926d7cbec4bd8b40f72375d5", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b9dd2c6ac144001dc6dac38b564c1de73ac26ef0c195d5037c4a94990b0e2b5a", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2354248848d06a9ae1e7a12165f800f0dda7df60ecac9fca892322b722b922c0", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:95d517bd1a0a28dacd1c37550ced95cab64f3a7a4ef9b8219b41049388a71163", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2faa8d8f251d1fa44813765b00791048b617e9dc06e6cd9222aba81023929119", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3c0175d0ed054bf0dc3b154a744b1a127c94291b3f3b7bdd0639b4b238c89445", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c22f58f60c3537b7d28ed2501e0995acb2a65d9af2708f21edaad67186cd8", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:5d665234def325e22c15518c581b0107a651c9f843176e3192360b092ebcb656", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a3df9c41706a22de0f43fe2734f54db31cb5a7314cc92ecc84657a8492b3ff8d", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:18818a326b779abc7bfd5cfb9fe88501916dde144cb1944fc9e7b4fb6208dfc7", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:591ed8279f0170ef28933873f65e9f5f8c439287088ef6285cda65988b0ba614", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:34c5dcd704e17a2b01c097b4fe3b5f83c5cdbc42b9f2abd095e026c588f873d4", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314-win_amd64.whl", hash = "sha256:be19f467eb7a173264653369426e7ecc4745e28d15f9c52eea2ad5316ec685eb", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ad3e6141c1078c5c9c8ae7c3cdf6c80eb35612c99c8cf3f76fe295845f45bb9d", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:69db7c3d86af1bc8224f7053395ace3e2bd8c56b0c3922bc7b798114440c88e5", upload-time = "2026-03-23T15:50:10Z" },
+ { url = "https://download-r2.pytorch.org/whl/cpu/torchaudio-2.11.0%2Bcpu-cp314-cp314t-win_amd64.whl", hash = "sha256:b6bfca873bd67d02fbbaa254312283be48dc1ca5532013152527210db432ef8c", upload-time = "2026-03-23T15:50:10Z" },
+]
+
[[package]]
name = "torchvision"
version = "0.28.0"
@@ -4197,9 +4586,11 @@ actor = [
{ name = "psutil" },
]
all = [
+ { name = "av" },
{ name = "chromadb" },
{ name = "faster-whisper" },
{ name = "huggingface-hub" },
+ { name = "matplotlib" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "opencv-python-headless" },
@@ -4207,8 +4598,11 @@ all = [
{ name = "pooch" },
{ name = "psutil" },
{ name = "sentence-transformers" },
+ { name = "timm" },
{ name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchaudio", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchaudio", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "transformers" },
@@ -4227,9 +4621,11 @@ frontend = [
{ name = "streamlit" },
]
local-worker = [
+ { name = "av" },
{ name = "chromadb" },
{ name = "faster-whisper" },
{ name = "huggingface-hub" },
+ { name = "matplotlib" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "opencv-python-headless" },
@@ -4238,8 +4634,11 @@ local-worker = [
{ name = "psutil" },
{ name = "pydantic-ai-slim", extra = ["openai"] },
{ name = "sentence-transformers" },
+ { name = "timm" },
{ name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchaudio", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchaudio", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "transformers" },
@@ -4273,10 +4672,12 @@ server = [
server-worker = [
{ name = "alembic" },
{ name = "asgi-correlation-id" },
+ { name = "av" },
{ name = "chromadb-client" },
{ name = "fastapi" },
{ name = "faster-whisper" },
{ name = "huggingface-hub" },
+ { name = "matplotlib" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "opencv-python-headless" },
@@ -4288,8 +4689,11 @@ server-worker = [
{ name = "pyjwt", extra = ["crypto"] },
{ name = "python-multipart" },
{ name = "sentence-transformers" },
+ { name = "timm" },
{ name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchaudio", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchaudio", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "transformers" },
@@ -4298,6 +4702,21 @@ server-worker = [
slm = [
{ name = "pydantic-ai-slim", extra = ["openai"] },
]
+sound = [
+ { name = "av" },
+ { name = "chromadb" },
+ { name = "huggingface-hub" },
+ { name = "matplotlib" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
+ { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
+ { name = "psutil" },
+ { name = "timm" },
+ { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchaudio", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchaudio", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "transformers" },
+]
storage = [
{ name = "chromadb" },
{ name = "psutil" },
@@ -4326,11 +4745,16 @@ requires-dist = [
{ name = "alembic", marker = "extra == 'server-worker'", specifier = ">=1.18.5,<2" },
{ name = "asgi-correlation-id", marker = "extra == 'server'", specifier = ">=5.0.1,<6" },
{ name = "asgi-correlation-id", marker = "extra == 'server-worker'", specifier = ">=5.0.1,<6" },
+ { name = "av", marker = "extra == 'all'", specifier = ">=18,<19" },
+ { name = "av", marker = "extra == 'local-worker'", specifier = ">=18,<19" },
+ { name = "av", marker = "extra == 'server-worker'", specifier = ">=18,<19" },
+ { name = "av", marker = "extra == 'sound'", specifier = ">=18,<19" },
{ name = "chromadb", marker = "extra == 'actor'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'all'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'dialogue'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'local-worker'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'scene'", specifier = ">=1.5.9,<2" },
+ { name = "chromadb", marker = "extra == 'sound'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'storage'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'videoprism'", specifier = ">=1.5.9,<2" },
{ name = "chromadb-client", marker = "extra == 'server-worker'", specifier = ">=1.5.9,<2" },
@@ -4348,7 +4772,12 @@ requires-dist = [
{ name = "huggingface-hub", marker = "extra == 'local-worker'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'scene'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'server-worker'", specifier = ">=1.25.1,<2" },
+ { name = "huggingface-hub", marker = "extra == 'sound'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'videoprism'", specifier = ">=1.25.1,<2" },
+ { name = "matplotlib", marker = "extra == 'all'", specifier = ">=3.10,<4" },
+ { name = "matplotlib", marker = "extra == 'local-worker'", specifier = ">=3.10,<4" },
+ { name = "matplotlib", marker = "extra == 'server-worker'", specifier = ">=3.10,<4" },
+ { name = "matplotlib", marker = "extra == 'sound'", specifier = ">=3.10,<4" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = ">=2.0,<3" },
{ name = "mcp", marker = "extra == 'server'", specifier = ">=2.0,<3" },
{ name = "numpy", marker = "extra == 'actor'", specifier = ">=2.3,<3" },
@@ -4356,6 +4785,7 @@ requires-dist = [
{ name = "numpy", marker = "extra == 'local-worker'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'scene'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'server-worker'", specifier = ">=2.3,<3" },
+ { name = "numpy", marker = "extra == 'sound'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'videoprism'", specifier = ">=2.3,<3" },
{ name = "opencv-python-headless", marker = "extra == 'actor'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'all'", specifier = ">=5.0.0.93,<6" },
@@ -4380,6 +4810,7 @@ requires-dist = [
{ name = "psutil", marker = "extra == 'scene'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'server'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'server-worker'", specifier = ">=7.2.2,<8" },
+ { name = "psutil", marker = "extra == 'sound'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'storage'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'videoprism'", specifier = ">=7.2.2,<8" },
{ name = "psycopg", extras = ["binary"], marker = "extra == 'server'", specifier = ">=3.3.4,<4" },
@@ -4403,16 +4834,30 @@ requires-dist = [
{ name = "sqlalchemy", specifier = ">=2.0.51,<2.1" },
{ name = "srt", marker = "extra == 'benchmarks'", specifier = ">=3.5,<4" },
{ name = "streamlit", marker = "extra == 'frontend'", specifier = ">=1.60,<2" },
+ { name = "timm", marker = "extra == 'all'", specifier = ">=1.0.20,<2" },
+ { name = "timm", marker = "extra == 'local-worker'", specifier = ">=1.0.20,<2" },
+ { name = "timm", marker = "extra == 'server-worker'", specifier = ">=1.0.20,<2" },
+ { name = "timm", marker = "extra == 'sound'", specifier = ">=1.0.20,<2" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'scene') or (sys_platform == 'win32' and extra == 'scene')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torch", marker = "(sys_platform == 'linux' and extra == 'sound') or (sys_platform == 'win32' and extra == 'sound')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'videoprism') or (sys_platform == 'win32' and extra == 'videoprism')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'all'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'local-worker'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'scene'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'server-worker'", specifier = ">=2.13,<3" },
+ { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'sound'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'videoprism'", specifier = ">=2.13,<3" },
+ { name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'sound') or (sys_platform == 'win32' and extra == 'sound')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'all'", specifier = ">=2.11,<2.12" },
+ { name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'local-worker'", specifier = ">=2.11,<2.12" },
+ { name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'server-worker'", specifier = ">=2.11,<2.12" },
+ { name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'sound'", specifier = ">=2.11,<2.12" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
@@ -4425,12 +4870,13 @@ requires-dist = [
{ name = "transformers", marker = "extra == 'local-worker'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'scene'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'server-worker'", specifier = ">=5.14.1,<6" },
+ { name = "transformers", marker = "extra == 'sound'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'videoprism'", specifier = ">=5.14.1,<6" },
{ name = "typer", specifier = ">=0.27,<1" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.51,<0.52" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'server-worker'", specifier = ">=0.51,<0.52" },
]
-provides-extras = ["storage", "dialogue", "scene", "actor", "videoprism", "all", "local-worker", "mcp", "slm", "server", "server-worker", "test", "frontend", "benchmarks"]
+provides-extras = ["storage", "dialogue", "scene", "actor", "videoprism", "sound", "all", "local-worker", "mcp", "slm", "server", "server-worker", "test", "frontend", "benchmarks"]
[[package]]
name = "watchdog"
From ae2babd5f42f7a509d0be6017f626472f4e50241 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Sat, 29 Aug 2026 22:58:24 +0500
Subject: [PATCH 3/9] feat(premiere): add installable media search for CEP and
UXP (#131)
* feat(premiere): add project media indexing and search
* feat(premiere): add Desktop-managed CEP and UXP installation
* fix(desktop): expose sound in guided setup
* fix(desktop): make Premiere setup own dependencies
* fix(desktop): recover stalled Windows media setup
* fix(desktop): derive setup metadata from capabilities
* build(premiere): adopt Bolt UXP tooling
* build(premiere): complete Bolt UXP adoption
---
.github/workflows/desktop.yml | 35 +
.gitignore | 5 +
.vscode/launch.json | 11 +
INSTALLATION_GUIDE.md | 84 +-
README.md | 1 +
desktop/capability-catalog.json | 85 +
desktop/runtime-manifest.json | 25 +-
desktop/scripts/model-catalog.py | 32 +-
desktop/src-tauri/build.rs | 15 +
desktop/src-tauri/src/lib.rs | 253 +-
desktop/src-tauri/src/media_setup.rs | 110 +
desktop/src-tauri/src/premiere_integration.rs | 373 +
desktop/src-tauri/tauri.conf.json | 10 +-
desktop/src/App.test.tsx | 108 +-
desktop/src/App.tsx | 23 +-
desktop/src/components/ManagedSetup.tsx | 153 +-
.../src/components/PremiereIntegration.tsx | 112 +
desktop/src/components/TargetSummary.tsx | 35 +-
desktop/src/desktopPackagingContracts.test.ts | 10 +-
desktop/src/tauri.test.ts | 9 +
desktop/src/tauri.ts | 42 +-
docs/adding-a-capability.md | 12 +-
docs/desktop.md | 31 +-
docs/integrations/premiere-pro.md | 106 +
docs/local-api.md | 18 +
premiere/README.md | 102 +
premiere/cep/CSXS/manifest.xml | 55 +
premiere/cep/index.html | 13 +
premiere/cep/index.tsx | 19 +
premiere/cep/jsx/host.jsx | 121 +
premiere/docs/MANUAL_TEST_CHECKLIST.md | 92 +
premiere/eslint.config.mjs | 30 +
premiere/index.html | 12 +
premiere/index.tsx | 42 +
premiere/package-lock.json | 6764 +++++++++++++++++
premiere/package.json | 52 +
premiere/scripts/package-extensions.mjs | 55 +
premiere/scripts/prepare-cep.mjs | 12 +
premiere/src/premiere/adapter.ts | 104 +
premiere/src/premiere/cep-adapter.ts | 43 +
premiere/src/premiere/library.ts | 69 +
premiere/src/premiere/types.ts | 28 +
premiere/src/services/vidxp/cep-fetch.ts | 75 +
premiere/src/services/vidxp/client.ts | 226 +
premiere/src/services/vidxp/types.ts | 112 +
premiere/src/ui/App.tsx | 650 ++
premiere/src/ui/components/Spectrum.tsx | 334 +
premiere/src/ui/spectrum-elements.d.ts | 29 +
premiere/src/ui/styles.css | 502 ++
premiere/src/ui/theme.ts | 126 +
premiere/src/vite-env.d.ts | 1 +
premiere/tests/cep-adapter.test.ts | 41 +
premiere/tests/library.test.ts | 72 +
premiere/tests/theme.test.ts | 61 +
premiere/tests/uxp-config.test.ts | 21 +
premiere/tests/vidxp-client.test.ts | 124 +
premiere/tsconfig.json | 32 +
premiere/uxp.config.ts | 68 +
premiere/vite.config.ts | 60 +
src/vidxp/api_routes/media.py | 74 +
src/vidxp/application_models.py | 1 +
src/vidxp/capabilities/actor/definition.py | 1 +
src/vidxp/capabilities/contracts.py | 7 +
src/vidxp/capabilities/dialogue/definition.py | 1 +
src/vidxp/capabilities/scene/definition.py | 1 +
src/vidxp/capabilities/sound/definition.py | 1 +
.../capabilities/videoprism/definition.py | 1 +
src/vidxp/capability_service.py | 1 +
src/vidxp/control_plane.py | 28 +
src/vidxp/local_probe.py | 48 +-
src/vidxp/upload_service.py | 2 +-
tests/test_api.py | 143 +-
tests/test_capabilities.py | 13 +
tests/test_control_plane.py | 24 +
tests/test_local_probe.py | 22 +-
tests/test_packaging.py | 9 +-
76 files changed, 12114 insertions(+), 108 deletions(-)
create mode 100644 .vscode/launch.json
create mode 100644 desktop/capability-catalog.json
create mode 100644 desktop/src-tauri/src/premiere_integration.rs
create mode 100644 desktop/src/components/PremiereIntegration.tsx
create mode 100644 docs/integrations/premiere-pro.md
create mode 100644 premiere/README.md
create mode 100644 premiere/cep/CSXS/manifest.xml
create mode 100644 premiere/cep/index.html
create mode 100644 premiere/cep/index.tsx
create mode 100644 premiere/cep/jsx/host.jsx
create mode 100644 premiere/docs/MANUAL_TEST_CHECKLIST.md
create mode 100644 premiere/eslint.config.mjs
create mode 100644 premiere/index.html
create mode 100644 premiere/index.tsx
create mode 100644 premiere/package-lock.json
create mode 100644 premiere/package.json
create mode 100644 premiere/scripts/package-extensions.mjs
create mode 100644 premiere/scripts/prepare-cep.mjs
create mode 100644 premiere/src/premiere/adapter.ts
create mode 100644 premiere/src/premiere/cep-adapter.ts
create mode 100644 premiere/src/premiere/library.ts
create mode 100644 premiere/src/premiere/types.ts
create mode 100644 premiere/src/services/vidxp/cep-fetch.ts
create mode 100644 premiere/src/services/vidxp/client.ts
create mode 100644 premiere/src/services/vidxp/types.ts
create mode 100644 premiere/src/ui/App.tsx
create mode 100644 premiere/src/ui/components/Spectrum.tsx
create mode 100644 premiere/src/ui/spectrum-elements.d.ts
create mode 100644 premiere/src/ui/styles.css
create mode 100644 premiere/src/ui/theme.ts
create mode 100644 premiere/src/vite-env.d.ts
create mode 100644 premiere/tests/cep-adapter.test.ts
create mode 100644 premiere/tests/library.test.ts
create mode 100644 premiere/tests/theme.test.ts
create mode 100644 premiere/tests/uxp-config.test.ts
create mode 100644 premiere/tests/vidxp-client.test.ts
create mode 100644 premiere/tsconfig.json
create mode 100644 premiere/uxp.config.ts
create mode 100644 premiere/vite.config.ts
diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml
index 33f465df..50eb03ee 100644
--- a/.github/workflows/desktop.yml
+++ b/.github/workflows/desktop.yml
@@ -62,9 +62,39 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
+ premiere-packages:
+ name: Premiere extension packages
+ runs-on: windows-2025
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ ref: ${{ inputs.checkout_ref || github.event.inputs.checkout_ref || github.sha }}
+
+ - uses: actions/setup-node@v7
+ with:
+ node-version: "22"
+ cache: npm
+ cache-dependency-path: premiere/package-lock.json
+
+ - name: Build and package both Premiere host generations
+ run: |
+ npm ci
+ npm run check
+ npm run package
+ working-directory: premiere
+
+ - name: Preserve the Premiere packages
+ uses: actions/upload-artifact@v7
+ with:
+ name: vidxp-premiere-packages
+ path: premiere/packages/*
+ if-no-files-found: error
+ retention-days: ${{ inputs.artifact_retention_days || 14 }}
+
build:
name: ${{ matrix.target == 'windows' && 'Windows x86-64 NSIS' || matrix.target == 'macos' && 'macOS Apple Silicon DMG' || 'Linux x86-64 AppImage' }}
runs-on: ${{ matrix.target == 'windows' && 'windows-2025' || matrix.target == 'macos' && 'macos-15' || 'ubuntu-24.04' }}
+ needs: premiere-packages
strategy:
fail-fast: false
matrix:
@@ -75,6 +105,11 @@ jobs:
with:
ref: ${{ inputs.checkout_ref || github.event.inputs.checkout_ref || github.sha }}
+ - uses: actions/download-artifact@v8
+ with:
+ name: vidxp-premiere-packages
+ path: premiere/packages/
+
- uses: actions/download-artifact@v8
if: inputs.package_artifact_name != ''
with:
diff --git a/.gitignore b/.gitignore
index 1e944329..5cb56467 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,11 @@ pnpm-debug.log*
/target
/desktop/node_modules/
/desktop/dist/
+/premiere/dist/
+/premiere/ccx/
+/premiere/coverage/
+/premiere/packages/
+/premiere/.rnd
/desktop/src-tauri/target/
/desktop/src-tauri/gen/
/desktop/src-tauri/binaries/uv-*
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..c18fc217
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,11 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "uxp",
+ "request": "attach",
+ "name": "Attach to VidXP Premiere UXP",
+ "manifestPath": "${workspaceFolder}/premiere/dist/manifest.json"
+ }
+ ]
+}
diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md
index 9288c417..7ae4036b 100644
--- a/INSTALLATION_GUIDE.md
+++ b/INSTALLATION_GUIDE.md
@@ -33,14 +33,16 @@ Approximate model downloads are:
| Feature | Download |
|---|---:|
-| Dialogue search | 2.64 GiB |
-| Sound event search | 0.94 GiB |
+| Dialogue search | 2.63 GiB |
+| Sound event search | 0.91 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
| Actor matching | 37 MiB |
The Desktop-managed runtime can use about 3 GiB in addition to selected models.
-A complete local setup uses about 8.1 GiB before adding videos and indexes.
+All five model sets total about 5.95 GiB, so a complete local setup uses about
+8.95 GiB before temporary installation space, videos, and indexes. Desktop
+calculates the storage plan from the features currently selected during setup.
## Desktop app
@@ -78,6 +80,13 @@ setup leaves the previous working setup available.
- **AI assistant integration** lets a local MCP-compatible assistant use VidXP.
- **App integration service** lets other local applications use the HTTP API or
Streamable HTTP MCP.
+- **Premiere Pro extension** installs the matching Adobe panel and automatically
+ includes local video processing plus the private app integration service.
+
+The [Premiere Pro extension](docs/integrations/premiere-pro.md) searches media
+that is already loaded in an editing project. Desktop includes both supported
+Adobe extension packages; users do not need the source repository or a
+JavaScript toolchain.
Choose where models should be stored, then decide whether to download them
during setup. VidXP displays the required downloads before starting them.
@@ -94,6 +103,37 @@ After setup finishes:
Browser and API sharing are off by default. If you enable sharing, use it only
on a trusted local network and follow the warning shown by Desktop.
+### Install the Premiere Pro extension
+
+On Windows, VidXP supports Premiere Pro 23.0 or newer with two packaged host
+variants:
+
+- Premiere Pro 23.0 through 25.5 uses the CEP extension;
+- Premiere Pro 25.6 or newer uses the UXP extension.
+
+In Desktop setup, select **Premiere Pro extension** and the search features you
+want. Desktop automatically includes local video processing and its private app
+service, then detects standard Premiere installations and installs the matching
+package through Adobe Creative Cloud's plugin installer. If Adobe requires
+confirmation or elevation, finish the Creative Cloud prompt. Restart Premiere
+after installation.
+
+For an existing Desktop-managed VidXP installation, select **Set up Premiere**
+on the summary screen. Desktop opens the same setup flow with the Premiere
+requirements already selected. **Install for Premiere** remains available there
+for reinstalling or retrying only the Adobe package.
+
+For Premiere Pro 23.2, open the panel from **Window > Extensions (Legacy) >
+VidXP Search**. For Premiere Pro 25.6 or newer, use **Window > UXP Plugins >
+VidXP Search**. If both Premiere generations are installed, Desktop installs
+both packages; their host ranges do not overlap.
+
+Plain loopback HTTP is blocked by Premiere UXP on macOS. Desktop therefore does
+not claim the 25.6+ macOS panel as usable until the integration has a trusted
+local HTTPS transport. The CEP package can use its native local transport on
+supported macOS Premiere versions, but still requires host validation before a
+release claims support.
+
## Command line
Command-line installation uses
@@ -410,6 +450,44 @@ vidxp doctor
Review the suggested package-manager command before approving it.
+### Windows Desktop is stuck while checking FFmpeg
+
+Select **Cancel setup**. VidXP stops the package-manager process and keeps the
+previous installation and your setup choices. If an older Desktop release does
+not show that button, use **Quit VidXP** from the system tray. If it does not
+respond, end **VidXP Desktop** in Task Manager. End `winget.exe` there too if it
+continues running after VidXP has closed.
+
+Open a new PowerShell window so it receives any PATH changes made by WinGet,
+then check whether FFmpeg finished installing:
+
+```powershell
+winget list --id Gyan.FFmpeg --exact
+where.exe ffmpeg
+where.exe ffprobe
+```
+
+If WinGet lists `Gyan.FFmpeg` and both executables are found, reopen VidXP
+Desktop and retry setup. If the package is missing, or WinGet lists it but the
+executables are still not found, repair the package:
+
+```powershell
+winget install --id Gyan.FFmpeg --exact --source winget --force --silent `
+ --disable-interactivity --accept-package-agreements `
+ --accept-source-agreements
+```
+
+Open another new PowerShell window and verify the result:
+
+```powershell
+ffmpeg -hide_banner -version
+ffprobe -hide_banner -version
+ffmpeg -hide_banner -encoders | findstr /i "libx264 aac"
+```
+
+The final command should list both `libx264` and `aac`. Reopen VidXP Desktop
+and retry setup after all three commands succeed.
+
### Linux or Windows starts downloading NVIDIA packages
Reinstall with the supported CPU dependency set:
diff --git a/README.md b/README.md
index 7f08f1b3..3c199746 100644
--- a/README.md
+++ b/README.md
@@ -193,6 +193,7 @@ ordinary MCP tools.
- [Python, HTTP, and MCP installation](INSTALLATION_GUIDE.md)
- [Local HTTP API and MCP server](docs/local-api.md)
+- [Premiere Pro extension](docs/integrations/premiere-pro.md)
- [ChatGPT and Codex plugin integration](docs/integrations/openai-plugin.md)
- [Optional capability packages](INSTALLATION_GUIDE.md#optional-dependency-extras)
- [Coolify server setup](docs/deployment/coolify.md)
diff --git a/desktop/capability-catalog.json b/desktop/capability-catalog.json
new file mode 100644
index 00000000..543a5b9d
--- /dev/null
+++ b/desktop/capability-catalog.json
@@ -0,0 +1,85 @@
+{
+ "schema_version": 1,
+ "capabilities": {
+ "dialogue": {
+ "extra": "dialogue",
+ "modality": "dialogue",
+ "label": "Dialogue search",
+ "description": "Index and search spoken dialogue.",
+ "models": [
+ {
+ "cache_key": "models--Qwen--Qwen3-Embedding-0.6B/snapshots/97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3/model.safetensors",
+ "download_size_bytes": 1207489041
+ },
+ {
+ "cache_key": "models--dropbox-dash--faster-whisper-large-v3-turbo/snapshots/0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf/model.bin",
+ "download_size_bytes": 1621668947
+ }
+ ]
+ },
+ "sound": {
+ "extra": "sound",
+ "modality": "sound",
+ "label": "Sound event search",
+ "description": "Index and search music, environmental sounds, and audio events.",
+ "models": [
+ {
+ "cache_key": "finelap-tokenizer/config.json",
+ "download_size_bytes": 481
+ },
+ {
+ "cache_key": "finelap-tokenizer/merges.txt",
+ "download_size_bytes": 456318
+ },
+ {
+ "cache_key": "finelap-tokenizer/vocab.json",
+ "download_size_bytes": 898823
+ },
+ {
+ "cache_key": "models--AndreasXi--FineLAP/snapshots/b419aa22947d29907a5567f21b81bf3b39a40449/model.safetensors",
+ "download_size_bytes": 980404741
+ }
+ ]
+ },
+ "scene": {
+ "extra": "scene",
+ "modality": "scene",
+ "label": "Visual scene search",
+ "description": "Index and search visual scenes.",
+ "models": [
+ {
+ "cache_key": "models--google--siglip2-base-patch16-224/snapshots/75de2d55ec2d0b4efc50b3e9ad70dba96a7b2fa2/model.safetensors",
+ "download_size_bytes": 1539458338
+ }
+ ]
+ },
+ "actor": {
+ "extra": "actor",
+ "modality": "actor",
+ "label": "Actor recognition",
+ "description": "Index, inspect, and render actor clusters.",
+ "models": [
+ {
+ "cache_key": "opencv-zoo/face_detection_yunet_2026may.onnx",
+ "download_size_bytes": 229738
+ },
+ {
+ "cache_key": "opencv-zoo/face_recognition_sface_2021dec.onnx",
+ "download_size_bytes": 38696353
+ }
+ ]
+ },
+ "videoprism": {
+ "extra": "videoprism",
+ "modality": "videoprism",
+ "label": "Temporal video search",
+ "description": "Index and search temporal video clips with VideoPrism.",
+ "models": [
+ {
+ "cache_key": "models--google--videoprism-lvt-base-f16r288/snapshots/fb6de9f0eb7bc285be86bdca1cf7daa3e3ef51ff/model.safetensors",
+ "download_size_bytes": 993993146
+ }
+ ]
+ }
+ }
+}
diff --git a/desktop/runtime-manifest.json b/desktop/runtime-manifest.json
index ba16d94f..6a2b7583 100644
--- a/desktop/runtime-manifest.json
+++ b/desktop/runtime-manifest.json
@@ -6,6 +6,7 @@
"dependency_index": "https://pypi.org/simple",
"python_version": "3.14.6",
"uv_version": "0.12.0",
+ "managed_runtime_estimated_size_bytes": 3221225472,
"surfaces": {
"worker": {
"extra": "local-worker",
@@ -28,32 +29,10 @@
"server": {
"extra": "server",
"label": "App integration service",
- "description": "Run an API and network-style MCP connection for other software. It is private by default and can be shared on your local network with bearer-token authentication.",
+ "description": "Connect other apps through VidXP's private local API. Local-network sharing is optional and requires bearer-token authentication.",
"default": false
}
},
- "capabilities": {
- "dialogue": {
- "extra": "dialogue",
- "modality": "dialogue",
- "label": "Dialogue search"
- },
- "scene": {
- "extra": "scene",
- "modality": "scene",
- "label": "Visual scene search"
- },
- "actor": {
- "extra": "actor",
- "modality": "actor",
- "label": "Actor recognition"
- },
- "videoprism": {
- "extra": "videoprism",
- "modality": "videoprism",
- "label": "Temporal video search"
- }
- },
"media_runtime": {
"strategy": "system",
"executables": [
diff --git a/desktop/scripts/model-catalog.py b/desktop/scripts/model-catalog.py
index be30acc7..e48ad832 100644
--- a/desktop/scripts/model-catalog.py
+++ b/desktop/scripts/model-catalog.py
@@ -9,15 +9,21 @@
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "src"))
-from vidxp.local_probe import desktop_model_cache_catalog # noqa: E402
+from vidxp.local_probe import ( # noqa: E402
+ desktop_capability_catalog,
+ desktop_model_cache_catalog,
+)
-CATALOG_PATH = ROOT / "desktop" / "model-cache-catalog.json"
+CATALOGS = {
+ ROOT / "desktop" / "capability-catalog.json": desktop_capability_catalog,
+ ROOT / "desktop" / "model-cache-catalog.json": desktop_model_cache_catalog,
+}
-def rendered_catalog() -> str:
+def rendered_catalog(value: object) -> str:
return json.dumps(
- desktop_model_cache_catalog(),
+ value,
indent=2,
ensure_ascii=False,
) + "\n"
@@ -29,13 +35,23 @@ def main() -> int:
mode.add_argument("--write", action="store_true")
mode.add_argument("--check", action="store_true")
arguments = parser.parse_args()
- derived = rendered_catalog()
if arguments.write:
- CATALOG_PATH.write_text(derived, encoding="utf-8", newline="\n")
+ for path, derive in CATALOGS.items():
+ path.write_text(
+ rendered_catalog(derive()),
+ encoding="utf-8",
+ newline="\n",
+ )
return 0
- if CATALOG_PATH.read_text(encoding="utf-8") != derived:
+ stale = [
+ path.relative_to(ROOT).as_posix()
+ for path, derive in CATALOGS.items()
+ if not path.exists()
+ or path.read_text(encoding="utf-8") != rendered_catalog(derive())
+ ]
+ if stale:
raise SystemExit(
- "desktop/model-cache-catalog.json is stale; run npm run "
+ f"{', '.join(stale)} is stale; run npm run "
"model-catalog:write from desktop/."
)
return 0
diff --git a/desktop/src-tauri/build.rs b/desktop/src-tauri/build.rs
index 37090520..174f6707 100644
--- a/desktop/src-tauri/build.rs
+++ b/desktop/src-tauri/build.rs
@@ -6,6 +6,20 @@ fn main() {
let mut manifest: serde_json::Value =
serde_json::from_slice(include_bytes!("../runtime-manifest.json"))
.expect("desktop/runtime-manifest.json must be valid JSON");
+ let capability_catalog: serde_json::Value =
+ serde_json::from_slice(include_bytes!("../capability-catalog.json"))
+ .expect("desktop/capability-catalog.json must be valid JSON");
+ assert_eq!(
+ capability_catalog["schema_version"].as_u64(),
+ Some(1),
+ "desktop capability catalog uses an unsupported schema version"
+ );
+ let capabilities = capability_catalog["capabilities"]
+ .as_object()
+ .filter(|capabilities| !capabilities.is_empty())
+ .expect("desktop capability catalog must contain capabilities")
+ .clone();
+ manifest["capabilities"] = serde_json::Value::Object(capabilities);
let expected = manifest["uv_version"]
.as_str()
.expect("runtime manifest must contain uv_version");
@@ -152,6 +166,7 @@ fn main() {
println!("cargo:rerun-if-changed=../../uv.lock");
println!("cargo:rerun-if-changed=../../dist");
println!("cargo:rerun-if-changed=../runtime-manifest.json");
+ println!("cargo:rerun-if-changed=../capability-catalog.json");
let attributes = tauri_build::Attributes::new();
#[cfg(windows)]
diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index d699e420..fca2be96 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -31,6 +31,7 @@ mod background_process;
mod browser_readiness;
mod lifecycle;
mod media_setup;
+mod premiere_integration;
mod target_profiles;
use activation::{ActivationRecovery, ActivationStage, activation_recovery};
@@ -56,13 +57,22 @@ const CODEX_PLUGIN_MARKETPLACE_REF: Option<&str> = option_env!("VIDXP_PLUGIN_MAR
const PRODUCT_DATA_DIRECTORY_NAME: &str = "VidXP";
const RUNTIME_CONSTRAINTS_FILE_NAME: &str = "runtime-constraints.txt";
const MAX_SETUP_OUTPUT_BYTES: usize = 4 * 1024 * 1024;
+const MEDIA_RUNTIME_INSTALL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
static READINESS_SEQUENCE: AtomicU64 = AtomicU64::new(0);
+#[derive(Clone, Deserialize, Serialize)]
+struct ModelDownloadSpec {
+ cache_key: String,
+ download_size_bytes: u64,
+}
+
#[derive(Clone, Deserialize, Serialize)]
struct CapabilitySpec {
extra: String,
modality: String,
label: String,
+ description: String,
+ models: Vec,
}
#[derive(Clone, Deserialize, Serialize)]
@@ -90,6 +100,7 @@ struct RuntimeManifest {
dependency_constraints_sha256: String,
python_version: String,
uv_version: String,
+ managed_runtime_estimated_size_bytes: u64,
surfaces: BTreeMap,
capabilities: BTreeMap,
media_runtime: MediaRuntimeSpec,
@@ -1374,6 +1385,13 @@ fn executable_candidates(name: &str) -> Vec {
.join("Links"),
);
}
+ if cfg!(windows) {
+ for variable in ["PROGRAMFILES", "PROGRAMFILES(X86)", "ProgramW6432"] {
+ if let Some(program_files) = env::var_os(variable) {
+ directories.push(PathBuf::from(program_files).join("WinGet").join("Links"));
+ }
+ }
+ }
if cfg!(target_os = "macos") {
directories.extend([
PathBuf::from("/opt/homebrew/bin"),
@@ -1400,10 +1418,18 @@ fn executable_candidates(name: &str) -> Vec {
}
fn resolve_system_executable(name: &str) -> Option {
- executable_candidates(name)
+ let resolved = executable_candidates(name)
.into_iter()
.find(|candidate| candidate.is_file())
- .and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)))
+ .and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)));
+ if resolved.is_some() {
+ return resolved;
+ }
+ #[cfg(windows)]
+ if matches!(name, "ffmpeg" | "ffprobe") {
+ return media_setup::resolve_winget_ffmpeg_executable(&format!("{name}.exe"));
+ }
+ None
}
fn combined_output(output: &background_process::BackgroundOutput) -> String {
@@ -2061,11 +2087,26 @@ async fn supervised_output(
command: Command,
cancellation: background_process::CancellationToken,
operation: &str,
+) -> Result {
+ supervised_output_with_timeout(
+ command,
+ cancellation,
+ operation,
+ Duration::from_secs(30 * 60),
+ )
+ .await
+}
+
+async fn supervised_output_with_timeout(
+ command: Command,
+ cancellation: background_process::CancellationToken,
+ operation: &str,
+ timeout: Duration,
) -> Result {
let output = background_process::run_async(
command,
background_process::BackgroundPolicy {
- timeout: Duration::from_secs(30 * 60),
+ timeout,
max_output_bytes: MAX_SETUP_OUTPUT_BYTES,
},
cancellation,
@@ -2546,6 +2587,54 @@ async fn cancel_managed_setup(
})?
}
+#[tauri::command]
+fn cancel_managed_setup_operation(
+ state: tauri::State<'_, DesktopState>,
+ draft_id: String,
+) -> Result<(), String> {
+ cancel_managed_setup_operation_inner(&state, &draft_id)
+}
+
+fn cancel_managed_setup_operation_inner(
+ state: &DesktopState,
+ draft_id: &str,
+) -> Result<(), String> {
+ {
+ let transition = state
+ .transition
+ .lock()
+ .map_err(|_| "The managed setup operation is unavailable.".to_string())?;
+ let record = transition
+ .draft
+ .as_ref()
+ .ok_or_else(|| "This managed setup draft has expired.".to_string())?;
+ if record.draft.id != draft_id {
+ return Err("A stale managed setup screen cannot stop the current operation.".into());
+ }
+ if record.phase != DraftPhase::Applying
+ || !transition.active.is_some_and(|active| {
+ matches!(
+ active.kind,
+ TransitionKind::InstallMedia
+ | TransitionKind::InstallRuntime
+ | TransitionKind::PrepareModels
+ )
+ })
+ {
+ return Err("No cancellable managed setup operation is active.".into());
+ }
+ }
+ let active = state
+ .operation_cancellation
+ .lock()
+ .map_err(|_| "The setup cancellation supervisor is unavailable.".to_string())?;
+ let cancellation = active
+ .as_ref()
+ .ok_or_else(|| "The managed setup operation has already settled.".to_string())?;
+ cancellation.cancel();
+ Ok(())
+}
+
#[tauri::command]
fn choose_model_directory(
app: AppHandle,
@@ -2571,8 +2660,10 @@ async fn install_media_runtime(
app: AppHandle,
state: tauri::State<'_, DesktopState>,
draft_id: String,
+ total_steps: u8,
) -> Result {
let cancellation = OperationCancellationGuard::register(&state)?;
+ let total_steps = total_steps.max(1);
let _transition =
TargetTransitionCoordinator::begin_apply(&state, &draft_id, TransitionKind::InstallMedia)
.map_err(|error| error.to_string())?;
@@ -2618,20 +2709,47 @@ async fn install_media_runtime(
if !approved {
return Err("FFmpeg setup was deferred.".into());
}
+ emit_managed_setup_progress(
+ &app,
+ &draft_id,
+ 1,
+ total_steps,
+ "video-tools",
+ &format!("Installing FFmpeg with {}", plan.manager),
+ );
let command = app
.shell()
.command(plan.command[0].clone())
.args(&plan.command[1..]);
let command: Command = command.into();
- supervised_output(
+ supervised_output_with_timeout(
command,
cancellation.token(),
&format!("{} FFmpeg installation", plan.manager),
+ MEDIA_RUNTIME_INSTALL_TIMEOUT,
)
.await?;
- let status = tauri::async_runtime::spawn_blocking(inspect_media_runtime)
- .await
- .map_err(|error| format!("Media runtime verification stopped unexpectedly: {error}"))?;
+ emit_managed_setup_progress(
+ &app,
+ &draft_id,
+ 1,
+ total_steps,
+ "video-tools",
+ "Verifying FFmpeg and required video codecs",
+ );
+ let status = tauri::async_runtime::spawn_blocking(|| {
+ let mut status = inspect_media_runtime();
+ for _ in 0..4 {
+ if status.ready {
+ break;
+ }
+ thread::sleep(Duration::from_secs(1));
+ status = inspect_media_runtime();
+ }
+ status
+ })
+ .await
+ .map_err(|error| format!("Media runtime verification stopped unexpectedly: {error}"))?;
if !status.ready {
return Err(format!(
"FFmpeg installation finished but verification failed: {}",
@@ -3728,6 +3846,60 @@ async fn install_codex_plugin(
.map_err(|error| format!("Codex plugin setup stopped unexpectedly: {error}"))?
}
+#[tauri::command]
+fn premiere_integration_state(
+ app: AppHandle,
+) -> Result {
+ let resource_dir = app
+ .path()
+ .resource_dir()
+ .map_err(|error| format!("Could not locate VidXP Desktop resources: {error}"))?;
+ Ok(premiere_integration::state(&resource_dir))
+}
+
+#[tauri::command]
+async fn install_premiere_extensions(
+ app: AppHandle,
+ state: tauri::State<'_, DesktopState>,
+) -> Result {
+ let _active = state.active_operations.register()?;
+ let (profile, _) = selected_target_context(&app)?;
+ if !profile.surfaces.iter().any(|surface| surface == "server") {
+ return Err("Enable the App integration service in Setup options before installing the Premiere extension.".into());
+ }
+ let worker_app = app.clone();
+ let (result, packages) = tauri::async_runtime::spawn_blocking(move || {
+ let resource_dir = worker_app
+ .path()
+ .resource_dir()
+ .map_err(|error| format!("Could not locate VidXP Desktop resources: {error}"))?;
+ premiere_integration::install(&resource_dir)
+ })
+ .await
+ .map_err(|error| format!("Premiere setup stopped unexpectedly: {error}"))??;
+ for package in packages {
+ app.opener()
+ .open_path(package.display().to_string(), None::<&str>)
+ .map_err(|error| {
+ format!(
+ "Could not open {} with Adobe Creative Cloud: {error}",
+ package.display()
+ )
+ })?;
+ }
+ Ok(result)
+}
+
+#[tauri::command]
+async fn uninstall_premiere_extensions(
+ state: tauri::State<'_, DesktopState>,
+) -> Result<(), String> {
+ let _active = state.active_operations.register()?;
+ tauri::async_runtime::spawn_blocking(premiere_integration::uninstall)
+ .await
+ .map_err(|error| format!("Premiere removal stopped unexpectedly: {error}"))?
+}
+
fn execute_worker_action(app: &AppHandle, action: &str) -> Result {
let (profile, paths) = selected_target_context(app)?;
if !profile.surfaces.iter().any(|surface| surface == "worker") {
@@ -4748,6 +4920,7 @@ pub fn run() {
confirm_forget_target,
begin_managed_setup,
cancel_managed_setup,
+ cancel_managed_setup_operation,
choose_model_directory,
install_media_runtime,
runtime_status,
@@ -4759,6 +4932,9 @@ pub fn run() {
configure_external_installation,
mcp_client_config,
install_codex_plugin,
+ premiere_integration_state,
+ install_premiere_extensions,
+ uninstall_premiere_extensions,
local_worker_status,
start_local_worker,
stop_local_worker,
@@ -5038,6 +5214,44 @@ mod tests {
);
}
+ #[test]
+ fn active_managed_operation_can_be_cancelled_without_cancelling_its_draft() {
+ let state = DesktopState::default();
+ state.transition.lock().expect("transition").draft = Some(DraftRecord {
+ draft: ManagedSetupDraft {
+ id: "draft-current".into(),
+ previous_profile_id: None,
+ },
+ phase: DraftPhase::Draft,
+ });
+ let operation = super::OperationCancellationGuard::register(&state).expect("operation");
+ let token = operation.token();
+ let applying = TargetTransitionCoordinator::begin_apply(
+ &state,
+ "draft-current",
+ TransitionKind::InstallMedia,
+ )
+ .expect("apply");
+
+ super::cancel_managed_setup_operation_inner(&state, "draft-current")
+ .expect("cancel operation");
+
+ assert!(token.is_cancelled());
+ assert_eq!(
+ state
+ .transition
+ .lock()
+ .expect("transition")
+ .draft
+ .as_ref()
+ .expect("draft")
+ .phase,
+ DraftPhase::Applying
+ );
+ drop(applying);
+ drop(operation);
+ }
+
fn worker_supervisor_fixture() -> (
Arc,
Arc,
@@ -5362,6 +5576,31 @@ mod tests {
assert!(selected_capabilities(&manifest, &["other".into()]).is_err());
}
+ #[test]
+ fn runtime_manifest_exposes_sound_setup_and_dependency() {
+ let manifest = manifest().expect("manifest");
+ let sound = manifest
+ .capabilities
+ .get("sound")
+ .expect("sound setup capability");
+
+ assert_eq!(sound.extra, "sound");
+ assert_eq!(sound.modality, "sound");
+ assert_eq!(sound.label, "Sound event search");
+ assert_eq!(
+ sound
+ .models
+ .iter()
+ .map(|model| model.download_size_bytes)
+ .sum::(),
+ 981_760_363
+ );
+ assert_eq!(
+ package_specification(&manifest, &["sound".into()], &[]),
+ format!("vidxp[sound]=={}", manifest.package_version)
+ );
+ }
+
#[test]
fn runtime_constraints_digest_is_independent_of_checkout_line_endings() {
let canonical = normalized_runtime_constraints();
diff --git a/desktop/src-tauri/src/media_setup.rs b/desktop/src-tauri/src/media_setup.rs
index 192ed68c..7ac27e0d 100644
--- a/desktop/src-tauri/src/media_setup.rs
+++ b/desktop/src-tauri/src/media_setup.rs
@@ -1,5 +1,8 @@
use std::path::PathBuf;
+#[cfg(windows)]
+use std::{env, fs, path::Path};
+
pub(crate) struct SystemInstallPlan {
pub(crate) manager: String,
pub(crate) command: Vec,
@@ -21,6 +24,8 @@ pub(crate) fn system_install_plan(
"--exact".into(),
"--source".into(),
"winget".into(),
+ "--silent".into(),
+ "--disable-interactivity".into(),
"--accept-package-agreements".into(),
"--accept-source-agreements".into(),
],
@@ -86,3 +91,108 @@ pub(crate) fn required_encoder_missing(output: &str, encoder: &str) -> bool {
.flat_map(|line| line.split_whitespace())
.any(|token| token == encoder)
}
+
+#[cfg(windows)]
+fn find_winget_package_executable_in(root: &Path, name: &str) -> Option {
+ fn visit(directory: &Path, name: &str, remaining_depth: u8, matches: &mut Vec) {
+ if remaining_depth == 0 {
+ return;
+ }
+ let Ok(entries) = fs::read_dir(directory) else {
+ return;
+ };
+ for entry in entries.flatten() {
+ let path = entry.path();
+ if path.is_file()
+ && path
+ .file_name()
+ .is_some_and(|candidate| candidate.to_string_lossy().eq_ignore_ascii_case(name))
+ {
+ matches.push(path);
+ } else if path.is_dir() {
+ visit(&path, name, remaining_depth - 1, matches);
+ }
+ }
+ }
+
+ let entries = fs::read_dir(root).ok()?;
+ let mut matches = Vec::new();
+ for entry in entries.flatten() {
+ let package = entry.path();
+ if package.is_dir()
+ && package
+ .file_name()
+ .is_some_and(|candidate| candidate.to_string_lossy().starts_with("Gyan.FFmpeg_"))
+ {
+ visit(&package, name, 6, &mut matches);
+ }
+ }
+ matches.sort();
+ matches.into_iter().next()
+}
+
+#[cfg(windows)]
+pub(crate) fn resolve_winget_ffmpeg_executable(name: &str) -> Option {
+ let mut roots = Vec::new();
+ if let Some(local) = env::var_os("LOCALAPPDATA") {
+ roots.push(
+ PathBuf::from(local)
+ .join("Microsoft")
+ .join("WinGet")
+ .join("Packages"),
+ );
+ }
+ for variable in ["PROGRAMFILES", "PROGRAMFILES(X86)", "ProgramW6432"] {
+ if let Some(program_files) = env::var_os(variable) {
+ roots.push(PathBuf::from(program_files).join("WinGet").join("Packages"));
+ }
+ }
+ roots.into_iter().find_map(|root| {
+ find_winget_package_executable_in(&root, name)
+ .and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)))
+ })
+}
+
+#[cfg(all(test, windows))]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn resolves_ffmpeg_from_a_winget_package_when_its_alias_is_missing() {
+ let root = std::env::temp_dir().join(format!(
+ "vidxp-winget-ffmpeg-{}-{}",
+ std::process::id(),
+ std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .expect("clock")
+ .as_nanos()
+ ));
+ let executable = root
+ .join("Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe")
+ .join("ffmpeg-build")
+ .join("bin")
+ .join("ffmpeg.exe");
+ fs::create_dir_all(executable.parent().expect("executable parent")).expect("package");
+ fs::write(&executable, b"fixture").expect("executable");
+
+ assert_eq!(
+ find_winget_package_executable_in(&root, "ffmpeg.exe"),
+ Some(executable)
+ );
+ let _ = fs::remove_dir_all(root);
+ }
+
+ #[test]
+ fn ffmpeg_install_disables_hidden_package_manager_prompts() {
+ let plan =
+ system_install_plan(|name| (name == "winget").then(|| PathBuf::from("winget.exe")))
+ .expect("install plan");
+
+ assert!(plan.command.iter().any(|argument| argument == "--silent"));
+ assert!(
+ plan.command
+ .iter()
+ .any(|argument| argument == "--disable-interactivity")
+ );
+ }
+}
diff --git a/desktop/src-tauri/src/premiere_integration.rs b/desktop/src-tauri/src/premiere_integration.rs
new file mode 100644
index 00000000..da3d6862
--- /dev/null
+++ b/desktop/src-tauri/src/premiere_integration.rs
@@ -0,0 +1,373 @@
+use std::{
+ collections::BTreeSet,
+ path::{Path, PathBuf},
+ process::Command,
+};
+
+#[cfg(windows)]
+use std::env;
+#[cfg(target_os = "macos")]
+use std::fs;
+
+use serde::{Deserialize, Serialize};
+
+const CEP_ID: &str = "org.grayhat.vidxp-premiere.cep.search";
+const UXP_ID: &str = "org.grayhat.vidxp-premiere";
+const CEP_PACKAGE: &str = "vidxp-premiere-cep.zxp";
+const UXP_PACKAGE: &str = "vidxp-premiere-uxp.ccx";
+
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum PremiereHostKind {
+ Cep,
+ Uxp,
+ Unsupported,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+pub struct PremiereInstallation {
+ pub display_name: String,
+ pub version: String,
+ pub executable: String,
+ pub host_kind: PremiereHostKind,
+ pub compatible: bool,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct PremiereIntegrationState {
+ pub installations: Vec,
+ pub platform_supported: bool,
+ pub installer_available: bool,
+ pub cep_package_available: bool,
+ pub uxp_package_available: bool,
+ pub cep_installed: bool,
+ pub uxp_installed: bool,
+ pub detail: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct PremiereInstallResult {
+ pub installed_hosts: Vec,
+ pub opened_packages: Vec,
+ pub detail: String,
+}
+
+#[derive(Deserialize)]
+struct WindowsInstallation {
+ #[serde(rename = "Name")]
+ name: String,
+ #[serde(rename = "Version")]
+ version: String,
+ #[serde(rename = "Executable")]
+ executable: String,
+}
+
+pub fn state(resource_dir: &Path) -> PremiereIntegrationState {
+ let installations = discover_installations();
+ let installer = unified_plugin_installer();
+ let installed = installer
+ .as_ref()
+ .and_then(|path| installer_list(path).ok())
+ .unwrap_or_default();
+ let cep_package_available = package_path(resource_dir, PremiereHostKind::Cep).is_file();
+ let uxp_package_available = package_path(resource_dir, PremiereHostKind::Uxp).is_file();
+ let compatible_count = installations.iter().filter(|item| item.compatible).count();
+ let detail = if compatible_count == 0 {
+ "No compatible Premiere installation was found in its standard application folder. Both packages remain available for custom Adobe installations.".into()
+ } else {
+ format!(
+ "Found {compatible_count} compatible Premiere installation{}.",
+ if compatible_count == 1 { "" } else { "s" }
+ )
+ };
+ PremiereIntegrationState {
+ installations,
+ platform_supported: cfg!(any(windows, target_os = "macos")),
+ installer_available: installer.is_some(),
+ cep_package_available,
+ uxp_package_available,
+ cep_installed: installed.contains(CEP_ID),
+ uxp_installed: installed.contains(UXP_ID),
+ detail,
+ }
+}
+
+pub fn install(resource_dir: &Path) -> Result<(PremiereInstallResult, Vec), String> {
+ if !cfg!(any(windows, target_os = "macos")) {
+ return Err(
+ "Premiere extension installation is available on Windows and macOS only.".into(),
+ );
+ }
+ let installations = discover_installations();
+ let mut kinds = installations
+ .iter()
+ .filter(|item| item.compatible)
+ .map(|item| item.host_kind.clone())
+ .collect::>();
+ if kinds.is_empty() {
+ if !installations.is_empty() {
+ return Err("The detected Premiere installations are not supported by this VidXP extension build.".into());
+ }
+ kinds.insert(PremiereHostKind::Cep);
+ if cfg!(windows) {
+ kinds.insert(PremiereHostKind::Uxp);
+ }
+ }
+ let installer = unified_plugin_installer();
+ let mut installed_hosts = Vec::new();
+ let mut opened = Vec::new();
+ for kind in kinds {
+ let package = package_path(resource_dir, kind.clone());
+ if !package.is_file() {
+ return Err(format!(
+ "The bundled {} package is missing. Reinstall or update VidXP Desktop.",
+ package.display()
+ ));
+ }
+ if installer
+ .as_ref()
+ .is_some_and(|path| installer_install(path, &package).is_ok())
+ {
+ installed_hosts.push(kind);
+ } else {
+ opened.push(package);
+ }
+ }
+ let result = PremiereInstallResult {
+ installed_hosts,
+ opened_packages: opened
+ .iter()
+ .map(|path| path.display().to_string())
+ .collect(),
+ detail: if opened.is_empty() {
+ "The VidXP extension was installed. Restart Premiere, then open VidXP Search from the Window menu.".into()
+ } else {
+ "Adobe's background installer was unavailable or required interaction. Complete the Creative Cloud installation window, then restart Premiere.".into()
+ },
+ };
+ Ok((result, opened))
+}
+
+pub fn uninstall() -> Result<(), String> {
+ let installer = unified_plugin_installer()
+ .ok_or_else(|| "Adobe Creative Cloud's plugin installer was not found.".to_string())?;
+ let mut failures = Vec::new();
+ for id in [CEP_ID, UXP_ID] {
+ if let Err(error) = installer_remove(&installer, id) {
+ failures.push(error);
+ }
+ }
+ if failures.len() == 2 {
+ Err(failures.join(" "))
+ } else {
+ Ok(())
+ }
+}
+
+fn package_path(resource_dir: &Path, kind: PremiereHostKind) -> PathBuf {
+ let name = match kind {
+ PremiereHostKind::Cep => CEP_PACKAGE,
+ PremiereHostKind::Uxp => UXP_PACKAGE,
+ PremiereHostKind::Unsupported => unreachable!("unsupported hosts do not have packages"),
+ };
+ resource_dir.join("premiere").join(name)
+}
+
+fn host_kind(version: &str) -> PremiereHostKind {
+ let mut parts = version
+ .split('.')
+ .filter_map(|part| part.parse::().ok());
+ let major = parts.next().unwrap_or_default();
+ let minor = parts.next().unwrap_or_default();
+ if major > 25 || (major == 25 && minor >= 6) {
+ PremiereHostKind::Uxp
+ } else if major >= 23 {
+ PremiereHostKind::Cep
+ } else {
+ PremiereHostKind::Unsupported
+ }
+}
+
+fn discover_installations() -> Vec {
+ #[cfg(windows)]
+ return discover_windows_installations();
+ #[cfg(target_os = "macos")]
+ return discover_macos_installations();
+ #[cfg(not(any(windows, target_os = "macos")))]
+ Vec::new()
+}
+
+#[cfg(windows)]
+fn discover_windows_installations() -> Vec {
+ let script = r#"
+$items = @()
+$roots = @($env:ProgramFiles, ${env:ProgramFiles(x86)}) | Where-Object { $_ } | Select-Object -Unique
+foreach ($root in $roots) {
+ $adobe = Join-Path $root 'Adobe'
+ if (-not (Test-Path -LiteralPath $adobe)) { continue }
+ Get-ChildItem -LiteralPath $adobe -Directory -Filter 'Adobe Premiere Pro*' -ErrorAction SilentlyContinue | ForEach-Object {
+ $exe = Join-Path $_.FullName 'Adobe Premiere Pro.exe'
+ if (Test-Path -LiteralPath $exe) {
+ $info = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($exe)
+ $items += [pscustomobject]@{ Name = $_.Name; Version = $info.ProductVersion; Executable = $exe }
+ }
+ }
+}
+$items | ConvertTo-Json -Compress
+"#;
+ let output = Command::new("powershell.exe")
+ .args(["-NoProfile", "-NonInteractive", "-Command", script])
+ .output();
+ let Ok(output) = output else {
+ return Vec::new();
+ };
+ if !output.status.success() {
+ return Vec::new();
+ }
+ parse_windows_installations(&output.stdout)
+}
+
+#[cfg(windows)]
+fn parse_windows_installations(payload: &[u8]) -> Vec {
+ let value: serde_json::Value = match serde_json::from_slice(payload) {
+ Ok(value) => value,
+ Err(_) => return Vec::new(),
+ };
+ let values = match value {
+ serde_json::Value::Array(values) => values,
+ serde_json::Value::Object(_) => vec![value],
+ _ => Vec::new(),
+ };
+ values
+ .into_iter()
+ .filter_map(|value| {
+ let item: WindowsInstallation = serde_json::from_value(value).ok()?;
+ let kind = host_kind(&item.version);
+ let compatible = kind != PremiereHostKind::Unsupported
+ && !(cfg!(target_os = "macos") && kind == PremiereHostKind::Uxp);
+ Some(PremiereInstallation {
+ display_name: item.name,
+ version: item.version,
+ executable: item.executable,
+ compatible,
+ host_kind: kind,
+ })
+ })
+ .collect()
+}
+
+#[cfg(target_os = "macos")]
+fn discover_macos_installations() -> Vec {
+ let Ok(entries) = fs::read_dir("/Applications") else {
+ return Vec::new();
+ };
+ entries
+ .flatten()
+ .filter_map(|entry| {
+ let path = entry.path();
+ let name = path.file_name()?.to_str()?;
+ if !name.starts_with("Adobe Premiere Pro") || path.extension()?.to_str()? != "app" {
+ return None;
+ }
+ let plist = fs::read_to_string(path.join("Contents/Info.plist")).ok()?;
+ let version = plist_value(&plist, "CFBundleShortVersionString")?;
+ let kind = host_kind(&version);
+ let compatible = kind != PremiereHostKind::Unsupported
+ && !(cfg!(target_os = "macos") && kind == PremiereHostKind::Uxp);
+ Some(PremiereInstallation {
+ display_name: name.trim_end_matches(".app").into(),
+ version,
+ executable: path.display().to_string(),
+ compatible,
+ host_kind: kind,
+ })
+ })
+ .collect()
+}
+
+#[cfg(target_os = "macos")]
+fn plist_value(plist: &str, key: &str) -> Option {
+ let marker = format!("{key} ");
+ let remainder = plist.split_once(&marker)?.1;
+ let value = remainder
+ .split_once("")?
+ .1
+ .split_once(" ")?
+ .0;
+ Some(value.trim().into())
+}
+
+fn unified_plugin_installer() -> Option {
+ #[cfg(windows)]
+ {
+ let program_files = env::var_os("ProgramFiles")?;
+ let path = PathBuf::from(program_files).join("Common Files/Adobe/Adobe Desktop Common/RemoteComponents/UPI/UnifiedPluginInstallerAgent/UnifiedPluginInstallerAgent.exe");
+ path.is_file().then_some(path)
+ }
+ #[cfg(target_os = "macos")]
+ {
+ let path = PathBuf::from(
+ "/Library/Application Support/Adobe/Adobe Desktop Common/RemoteComponents/UPI/UnifiedPluginInstallerAgent/UnifiedPluginInstallerAgent.app/Contents/MacOS/UnifiedPluginInstallerAgent",
+ );
+ return path.is_file().then_some(path);
+ }
+ #[cfg(not(any(windows, target_os = "macos")))]
+ None
+}
+
+fn installer_list(installer: &Path) -> Result {
+ let argument = if cfg!(windows) { "/list" } else { "--list" };
+ checked_installer(installer, [argument, "all"])
+}
+
+fn installer_install(installer: &Path, package: &Path) -> Result {
+ let argument = if cfg!(windows) {
+ "/install"
+ } else {
+ "--install"
+ };
+ checked_installer(installer, [argument, package.to_string_lossy().as_ref()])
+}
+
+fn installer_remove(installer: &Path, id: &str) -> Result {
+ let argument = if cfg!(windows) { "/remove" } else { "--remove" };
+ checked_installer(installer, [argument, id])
+}
+
+fn checked_installer<'a>(
+ installer: &Path,
+ arguments: impl IntoIterator- ,
+) -> Result
{
+ let output = Command::new(installer)
+ .args(arguments)
+ .output()
+ .map_err(|error| format!("Could not start Adobe's plugin installer: {error}"))?;
+ let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
+ let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
+ if output.status.success() {
+ Ok(stdout)
+ } else {
+ Err(format!(
+ "Adobe's plugin installer failed{}.",
+ if stderr.is_empty() {
+ String::new()
+ } else {
+ format!(": {stderr}")
+ }
+ ))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn chooses_non_overlapping_host_generations() {
+ assert_eq!(host_kind("23.2.0.69"), PremiereHostKind::Cep);
+ assert_eq!(host_kind("25.5.0"), PremiereHostKind::Cep);
+ assert_eq!(host_kind("25.6.0"), PremiereHostKind::Uxp);
+ assert_eq!(host_kind("26.3.0"), PremiereHostKind::Uxp);
+ assert_eq!(host_kind("22.6.0"), PremiereHostKind::Unsupported);
+ }
+}
diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json
index fe905f2d..94f7fd70 100644
--- a/desktop/src-tauri/tauri.conf.json
+++ b/desktop/src-tauri/tauri.conf.json
@@ -36,10 +36,12 @@
"targets": "all",
"publisher": "Grayhat Developers PVT Ltd",
"copyright": "Copyright © 2026 Grayhat Developers PVT Ltd",
- "resources": [
- "../THIRD_PARTY_NOTICES.txt",
- "../../LICENSE"
- ],
+ "resources": {
+ "../THIRD_PARTY_NOTICES.txt": "THIRD_PARTY_NOTICES.txt",
+ "../../LICENSE": "LICENSE",
+ "../../premiere/packages/vidxp-premiere-cep.zxp": "premiere/vidxp-premiere-cep.zxp",
+ "../../premiere/packages/vidxp-premiere-uxp.ccx": "premiere/vidxp-premiere-uxp.ccx"
+ },
"externalBin": [
"binaries/uv"
],
diff --git a/desktop/src/App.test.tsx b/desktop/src/App.test.tsx
index 608c6785..bdc08ec8 100644
--- a/desktop/src/App.test.tsx
+++ b/desktop/src/App.test.tsx
@@ -8,12 +8,13 @@ const mocks = vi.hoisted(() => ({
targetSetupState: vi.fn(), recheckTargetState: vi.fn(), discoverLocalTargets: vi.fn(),
chooseLocalExecutable: vi.fn(), inspectLocalTarget: vi.fn(), activateLocalTarget: vi.fn(),
selectTargetProfile: vi.fn(), deleteTargetProfile: vi.fn(), confirmForgetTarget: vi.fn(), beginManagedSetup: vi.fn(),
- cancelManagedSetup: vi.fn(), installMediaRuntime: vi.fn(), installRuntime: vi.fn(),
+ cancelManagedSetup: vi.fn(), cancelManagedSetupOperation: vi.fn(), installMediaRuntime: vi.fn(), installRuntime: vi.fn(),
prepareManagedModels: vi.fn(), onManagedSetupProgress: vi.fn(),
runtimeManifest: vi.fn(), runtimeStatus: vi.fn(), launchUi: vi.fn(),
chooseModelDirectory: vi.fn(), modelDirectoryInventory: vi.fn(),
targetDoctor: vi.fn(), mcpClientConfig: vi.fn(), installCodexPlugin: vi.fn(), localServerStatus: vi.fn(), localWorkerStatus: vi.fn(), browserServiceStatus: vi.fn(),
startLocalServer: vi.fn(), startSharedServer: vi.fn(), stopLocalServer: vi.fn(), startSharedBrowser: vi.fn(), stopBrowserService: vi.fn(), startLocalWorker: vi.fn(), stopLocalWorker: vi.fn(), configureExternalInstallation: vi.fn(),
+ premiereIntegrationState: vi.fn(), installPremiereExtensions: vi.fn(), uninstallPremiereExtensions: vi.fn(),
}));
const windowMocks = vi.hoisted(() => ({
@@ -88,8 +89,9 @@ describe('desktop target lifecycle', () => {
});
mocks.beginManagedSetup.mockResolvedValue({ id: 'draft-1', previous_profile_id: null });
mocks.cancelManagedSetup.mockResolvedValue(emptyState);
+ mocks.cancelManagedSetupOperation.mockResolvedValue(undefined);
mocks.confirmForgetTarget.mockResolvedValue(true);
- mocks.runtimeManifest.mockResolvedValue({ package_version: '0.4.0', capabilities: { scene: { extra: 'scene', label: 'Visual scene search' } }, surfaces: {
+ mocks.runtimeManifest.mockResolvedValue({ package_version: '0.4.0', managed_runtime_estimated_size_bytes: 3 * 1024 ** 3, capabilities: { scene: { extra: 'scene', label: 'Visual scene search', description: 'Index and search visual scenes.', models: [{ cache_key: 'scene', download_size_bytes: 1539458338 }] } }, surfaces: {
worker: { extra: 'local-worker', label: 'Process videos on this computer', description: 'Run video work locally.', default: true },
browser: { extra: 'frontend', label: 'Browser interface', description: 'Open VidXP in your browser.', default: true },
mcp: { extra: 'mcp', label: 'AI assistant integration', description: 'Connect a compatible AI app.', default: false },
@@ -113,6 +115,13 @@ describe('desktop target lifecycle', () => {
installed_path: 'C:\\Users\\test\\.codex\\plugins\\vidxp',
detail: 'VidXP is installed in Codex with its MCP server and skills. Start a new Codex chat to use the updated plugin.',
});
+ mocks.premiereIntegrationState.mockResolvedValue({
+ installations: [], platform_supported: false, installer_available: false,
+ cep_package_available: true, uxp_package_available: true,
+ cep_installed: false, uxp_installed: false, detail: 'Unavailable.',
+ });
+ mocks.installPremiereExtensions.mockResolvedValue({ installed_hosts: [], opened_packages: [], detail: 'Installed.' });
+ mocks.uninstallPremiereExtensions.mockResolvedValue(undefined);
mocks.browserServiceStatus.mockResolvedValue({ state: 'stopped', running: false, shared: false, port: null, local_url: null, network_url: null, detail: 'Stopped.' });
mocks.startSharedBrowser.mockResolvedValue({ state: 'ready', running: true, shared: true, port: 8501, local_url: 'http://127.0.0.1:8501', network_url: 'http://192.168.1.20:8501', detail: 'Shared.' });
mocks.stopBrowserService.mockResolvedValue({ state: 'stopped', running: false, shared: false, port: null, local_url: null, network_url: null, detail: 'Stopped.' });
@@ -508,14 +517,91 @@ describe('desktop target lifecycle', () => {
}));
});
+ it('installs Premiere with its processing and private-service dependencies in one setup flow', async () => {
+ mocks.premiereIntegrationState.mockResolvedValue({
+ installations: [{ display_name: 'Adobe Premiere Pro 2023', version: '23.2.0.69', executable: 'C:\\Program Files\\Adobe\\Premiere.exe', host_kind: 'cep', compatible: true }],
+ platform_supported: true, installer_available: true,
+ cep_package_available: true, uxp_package_available: true,
+ cep_installed: false, uxp_installed: false, detail: 'Found Premiere.',
+ });
+ const premiereProfile = { ...managedProfile, surfaces: ['worker', 'browser', 'server'] };
+ mocks.installRuntime.mockResolvedValue({
+ install: { package_version: '0.4.0', capabilities: ['scene'], surfaces: premiereProfile.surfaces, model_directory: 'C:\\Models', prepared: true },
+ setup: { profiles: [premiereProfile], selected_profile_id: premiereProfile.id, issues: [] },
+ });
+ mocks.installPremiereExtensions.mockResolvedValue({ installed_hosts: ['cep'], opened_packages: [], detail: 'The VidXP extension was installed.' });
+ const user = userEvent.setup(); renderApp(); await enterManaged(user);
+
+ await user.click(screen.getByRole('checkbox', { name: /Premiere Pro extension/i }));
+ expect(screen.getByRole('checkbox', { name: /Process videos on this computer/i })).toBeChecked();
+ expect(screen.getByRole('checkbox', { name: /App integration service/i })).toBeChecked();
+ await user.click(screen.getByRole('button', { name: 'Install VidXP and Premiere' }));
+
+ await waitFor(() => expect(mocks.installRuntime).toHaveBeenCalledWith(expect.objectContaining({
+ surfaces: expect.arrayContaining(['worker', 'server']),
+ })));
+ await waitFor(() => expect(mocks.startLocalServer).toHaveBeenCalledTimes(1));
+ expect(mocks.installPremiereExtensions).toHaveBeenCalledTimes(1);
+ expect(await screen.findByText('VidXP and Premiere are ready')).toBeVisible();
+ });
+
+ it('turns Set up Premiere into a preselected managed update instead of a prerequisite warning', async () => {
+ const state = { profiles: [managedProfile], selected_profile_id: managedProfile.id, issues: [] };
+ mocks.targetSetupState.mockResolvedValue(state);
+ mocks.recheckTargetState.mockResolvedValue(state);
+ mocks.runtimeStatus.mockResolvedValue({ state: 'ready', ready: true, runtime_profile: 'runtime-a', package_version: '0.4.0', capabilities: ['scene'], surfaces: [], model_directory: 'C:\\Models', detail: 'Ready.' });
+ mocks.premiereIntegrationState.mockResolvedValue({
+ installations: [{ display_name: 'Adobe Premiere Pro 2023', version: '23.2.0.69', executable: 'C:\\Program Files\\Adobe\\Premiere.exe', host_kind: 'cep', compatible: true }],
+ platform_supported: true, installer_available: true,
+ cep_package_available: true, uxp_package_available: true,
+ cep_installed: false, uxp_installed: false, detail: 'Found Premiere.',
+ });
+ const user = userEvent.setup(); renderApp();
+
+ await user.click(await screen.findByRole('button', { name: 'Set up Premiere' }));
+
+ expect(await screen.findByRole('checkbox', { name: /Premiere Pro extension/i })).toBeChecked();
+ expect(screen.getByRole('checkbox', { name: /Process videos on this computer/i })).toBeChecked();
+ expect(screen.getByRole('checkbox', { name: /App integration service/i })).toBeChecked();
+ expect(screen.getByRole('button', { name: 'Apply and install Premiere' })).toBeEnabled();
+ });
+
it('passes the scoped draft through first-time installation and does not auto-open the browser', async () => {
const user = userEvent.setup(); renderApp(); await enterManaged(user);
await user.click(await screen.findByRole('button', { name: 'Install VidXP' }));
await waitFor(() => expect(mocks.installRuntime).toHaveBeenCalledWith(expect.objectContaining({ draft_id: 'draft-1' })));
- expect(mocks.installMediaRuntime).toHaveBeenCalledWith('draft-1');
+ expect(mocks.installMediaRuntime).toHaveBeenCalledWith('draft-1', 8);
expect(mocks.launchUi).not.toHaveBeenCalled();
});
+ it('derives the storage plan from every selected capability model', async () => {
+ mocks.runtimeManifest.mockResolvedValue({
+ package_version: '0.4.0',
+ managed_runtime_estimated_size_bytes: 3 * 1024 ** 3,
+ capabilities: {
+ scene: {
+ extra: 'scene', label: 'Visual scene search', description: 'Index and search visual scenes.',
+ models: [{ cache_key: 'scene-model', download_size_bytes: 1539458338 }],
+ },
+ sound: {
+ extra: 'sound', label: 'Sound event search', description: 'Index and search sound events.',
+ models: [
+ { cache_key: 'sound-model', download_size_bytes: 980404741 },
+ { cache_key: 'sound-vocab', download_size_bytes: 1355622 },
+ ],
+ },
+ },
+ surfaces: {
+ worker: { extra: 'local-worker', label: 'Process videos on this computer', description: 'Run video work locally.', default: true },
+ },
+ });
+ const user = userEvent.setup(); renderApp(); await enterManaged(user);
+
+ expect(screen.getByText(/Selected model downloads total up to 2.35 GiB/)).toBeVisible();
+ expect(screen.getByText(/Visual scene search: 1.43 GiB · Sound event search: 936.3 MiB/)).toBeVisible();
+ expect(screen.getByText(/Plan for approximately 5.35 GiB locally/)).toBeVisible();
+ });
+
it('keeps a managed installation failure visible in the setup dialog until it is acknowledged', async () => {
mocks.installRuntime.mockRejectedValueOnce('The installed runtime failed its compatibility check.');
const user = userEvent.setup(); renderApp(); await enterManaged(user);
@@ -544,6 +630,7 @@ describe('desktop target lifecycle', () => {
expect(screen.getByRole('dialog', { name: 'Setting up VidXP' })).toBeVisible();
expect(screen.getByText('Step 1 of 8')).toBeVisible();
expect(screen.getByText('Checking FFmpeg and required video codecs')).toBeVisible();
+ expect(screen.getByRole('button', { name: 'Cancel setup' })).toBeEnabled();
reportProgress?.({ draft_id: 'draft-1', current: 4, total: 8, stage: 'dependencies', message: 'Installing the selected search features' });
expect(await screen.findByText('Step 4 of 8')).toBeVisible();
expect(screen.getByText('Installing the selected search features')).toBeVisible();
@@ -573,6 +660,21 @@ describe('desktop target lifecycle', () => {
media.resolve({ ready: true });
});
+ it('cancels a running managed setup without discarding the draft', async () => {
+ const media = deferred<{ ready: boolean }>();
+ mocks.installMediaRuntime.mockReturnValue(media.promise);
+ const user = userEvent.setup(); renderApp(); await enterManaged(user);
+
+ await user.click(screen.getByRole('button', { name: 'Install VidXP' }));
+ await user.click(screen.getByRole('button', { name: 'Cancel setup' }));
+
+ expect(mocks.cancelManagedSetupOperation).toHaveBeenCalledWith('draft-1');
+ expect(await screen.findByText('Stopping setup safely')).toBeVisible();
+ media.reject('Windows Package Manager FFmpeg installation failed: the operation was cancelled');
+ expect(await screen.findByRole('dialog', { name: 'Setup could not finish' })).toBeVisible();
+ expect(screen.getByRole('alert', { name: 'VidXP was not installed' })).toHaveTextContent('Setup was cancelled');
+ });
+
it('coalesces duplicate managed Continue actions', async () => {
const pending = deferred<{ id: string; previous_profile_id: null }>();
mocks.beginManagedSetup.mockReturnValue(pending.promise);
diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx
index 578b4bb4..f7e533c6 100644
--- a/desktop/src/App.tsx
+++ b/desktop/src/App.tsx
@@ -27,12 +27,20 @@ import { useExclusiveOperation } from './useAsyncAction';
type Stage = 'loading' | 'choice' | 'local' | 'managed-confirm' | 'managed' | 'summary';
type AppOperation = 'startup-check' | 'recheck' | 'begin-managed' | 'cancel-managed' | 'select-profile' | 'forget-profile' | 'open-browser';
+interface CompletionNotice {
+ color: 'teal' | 'yellow';
+ title: string;
+ detail: string;
+}
+
interface LifecycleState {
stage: Stage;
choice: TargetKind | null;
setup: TargetSetupState | null;
draft: ManagedSetupDraft | null;
failure: string | null;
+ completionNotice: CompletionNotice | null;
+ premiereSetupRequested: boolean;
operation: AppOperation | null;
operationProfile: string | null;
}
@@ -44,10 +52,11 @@ type Action =
| { type: 'loadFailed'; failure: string }
| { type: 'operationStarted'; operation: AppOperation; profileId?: string }
| { type: 'operationFailed'; failure: string }
- | { type: 'operationSettled'; setup?: TargetSetupState; stage?: Stage; draft?: ManagedSetupDraft | null };
+ | { type: 'operationSettled'; setup?: TargetSetupState; stage?: Stage; draft?: ManagedSetupDraft | null; completionNotice?: CompletionNotice | null; premiereSetupRequested?: boolean };
const initialState: LifecycleState = {
stage: 'loading', choice: null, setup: null, draft: null, failure: null,
+ completionNotice: null, premiereSetupRequested: false,
operation: null, operationProfile: null,
};
@@ -71,6 +80,8 @@ function reducer(state: LifecycleState, action: Action): LifecycleState {
setup: action.setup ?? state.setup,
stage: action.stage ?? state.stage,
draft: action.draft === undefined ? state.draft : action.draft,
+ completionNotice: action.completionNotice === undefined ? state.completionNotice : action.completionNotice,
+ premiereSetupRequested: action.premiereSetupRequested ?? state.premiereSetupRequested,
operation: null,
operationProfile: null,
failure: null,
@@ -131,12 +142,12 @@ export function App() {
};
}, [recheck]);
- async function beginManaged() {
+ async function beginManaged(premiereSetupRequested = false) {
const current = startOperation('begin-managed');
if (current === null) return;
try {
const draft = await beginManagedSetup();
- settleOperation(current, { type: 'operationSettled', draft, stage: 'managed' });
+ settleOperation(current, { type: 'operationSettled', draft, stage: 'managed', premiereSetupRequested });
} catch (error) {
settleOperation(current, {
type: 'operationFailed',
@@ -154,6 +165,7 @@ export function App() {
settleOperation(current, {
type: 'operationSettled', setup, draft: null,
stage: selectedProfile(setup) ? 'summary' : 'choice',
+ premiereSetupRequested: false,
});
} catch (error) {
settleOperation(current, {
@@ -213,6 +225,7 @@ export function App() {
{state.failure &&
} color="red" title="Desktop issue" role="alert" mb="lg">{state.failure}}
+ {state.completionNotice &&
dispatch({ type: 'operationSettled', completionNotice: null })}>{state.completionNotice.detail} }
{state.setup?.issues.map((issue) =>
{issue.message} )}
{state.stage === 'loading' &&
Loading your VidXP setup…
}
{state.stage === 'choice' && <>
@@ -228,8 +241,8 @@ export function App() {
>}
{state.stage === 'local' &&
dispatch({ type: 'navigate', stage: 'choice' })} onActivated={(setup) => dispatch({ type: 'operationSettled', setup, stage: 'summary' })} />}
{state.stage === 'managed-confirm' && } disabled={operationPending} onClick={() => dispatch({ type: 'navigate', stage: 'choice' })}>BackSET UP VIDXP
Install and manage VidXP on this computer? You choose the features. VidXP checks the new setup before switching to it, so your current installation stays available. dispatch({ type: 'navigate', stage: 'choice' })}>Cancel void beginManaged()}>Choose features }
- {state.stage === 'managed' && state.draft && dispatch({ type: 'operationSettled', setup, draft: null, stage: 'summary' })} />}
- {state.stage === 'summary' && profile && recheck()} onManageManaged={() => void beginManaged()} onSetupChanged={(setup) => dispatch({ type: 'operationSettled', setup, stage: 'summary' })} onChooseAnother={() => dispatch({ type: 'navigate', stage: 'choice', choice: null })} onOpen={openBrowser} />}
+ {state.stage === 'managed' && state.draft && dispatch({ type: 'operationSettled', setup, draft: null, stage: 'summary', completionNotice, premiereSetupRequested: false })} />}
+ {state.stage === 'summary' && profile && recheck()} onManageManaged={() => void beginManaged()} onSetUpPremiere={() => void beginManaged(true)} onSetupChanged={(setup) => dispatch({ type: 'operationSettled', setup, stage: 'summary' })} onChooseAnother={() => dispatch({ type: 'navigate', stage: 'choice', choice: null })} onOpen={openBrowser} />}
Your VidXP settings stay on this computer. Desktop only stops services that it starts.
diff --git a/desktop/src/components/ManagedSetup.tsx b/desktop/src/components/ManagedSetup.tsx
index 59b9d86c..1c1a24f4 100644
--- a/desktop/src/components/ManagedSetup.tsx
+++ b/desktop/src/components/ManagedSetup.tsx
@@ -17,16 +17,22 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import {
chooseModelDirectory,
+ cancelManagedSetupOperation,
displayPath,
errorMessage,
+ installPremiereExtensions,
installMediaRuntime,
installRuntime,
launchUi,
+ localServerStatus,
modelDirectoryInventory,
onManagedSetupProgress,
+ premiereIntegrationState,
prepareManagedModels,
runtimeManifest,
runtimeStatus,
+ startLocalServer,
+ type PremiereIntegrationState,
type RuntimeManifest,
type RuntimeStatus,
type ModelDirectoryInventory,
@@ -38,17 +44,20 @@ import { useExclusiveOperation } from '../useAsyncAction';
interface ManagedSetupProps {
draftId: string;
selectedManagedRuntimeProfile: string | null;
+ premiereRequested: boolean;
onBack: () => Promise;
- onCommitted: (setup: TargetSetupState) => void;
+ onCommitted: (setup: TargetSetupState, notice?: { color: 'teal' | 'yellow'; title: string; detail: string }) => void;
}
type ManagedOperation = 'load' | 'folder' | 'reset' | 'install' | 'prepare' | 'launch';
-export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, onCommitted }: ManagedSetupProps) {
+export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereRequested, onBack, onCommitted }: ManagedSetupProps) {
const [manifest, setManifest] = useState(null);
const [status, setStatus] = useState(null);
const [capabilities, setCapabilities] = useState([]);
const [surfaces, setSurfaces] = useState([]);
+ const [premiere, setPremiere] = useState(null);
+ const [premiereEnabled, setPremiereEnabled] = useState(premiereRequested);
const [prepareDuringInstall, setPrepareDuringInstall] = useState(true);
const [modelDirectory, setModelDirectory] = useState('');
const [inventory, setInventory] = useState(null);
@@ -56,14 +65,18 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
const [message, setMessage] = useState('Loading VidXP options…');
const [failure, setFailure] = useState(null);
const [installFailure, setInstallFailure] = useState(null);
+ const [cancelFailure, setCancelFailure] = useState(null);
+ const [cancelRequested, setCancelRequested] = useState(false);
const [setupProgress, setSetupProgress] = useState(null);
const [setupElapsed, setSetupElapsed] = useState(0);
const operations = useExclusiveOperation();
const failureAlert = useRef(null);
+ const cancelRequestedRef = useRef(false);
const initialLoad = useRef | null>(null);
const beginOperation = useCallback((kind: ManagedOperation): number | null => {
@@ -83,26 +96,31 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
if (operationId === null) return;
setFailure(null);
try {
- const request = initialLoad.current ?? Promise.all([runtimeManifest(), runtimeStatus()])
- .then(async ([nextManifest, nextStatus]) => ({
+ const request = initialLoad.current ?? Promise.all([runtimeManifest(), runtimeStatus(), premiereIntegrationState()])
+ .then(async ([nextManifest, nextStatus, nextPremiere]) => ({
manifest: nextManifest,
status: nextStatus,
inventory: await modelDirectoryInventory(nextStatus.model_directory),
+ premiere: nextPremiere,
}));
initialLoad.current = request;
- const { manifest: nextManifest, status: nextStatus, inventory: nextInventory } = await request;
+ const { manifest: nextManifest, status: nextStatus, inventory: nextInventory, premiere: nextPremiere } = await request;
if (!operations.current(operationId)) return;
setManifest(nextManifest);
setStatus(nextStatus);
+ setPremiere(nextPremiere);
const recoverable = Boolean(nextStatus.runtime_profile);
+ const shouldEnablePremiere = premiereRequested || Boolean(nextPremiere.cep_installed || nextPremiere.uxp_installed);
setCapabilities(recoverable ? nextStatus.capabilities : Object.keys(nextManifest.capabilities));
- setSurfaces(
+ const nextSurfaces = (
recoverable
? nextStatus.surfaces
: Object.entries(nextManifest.surfaces)
.filter(([, surface]) => surface.default)
- .map(([id]) => id),
+ .map(([id]) => id)
);
+ setSurfaces(shouldEnablePremiere ? [...new Set([...nextSurfaces, 'worker', 'server'])] : nextSurfaces);
+ setPremiereEnabled(shouldEnablePremiere);
setModelDirectory(nextStatus.model_directory);
setPrepareDuringInstall(!recoverable);
setInventory(nextInventory);
@@ -114,7 +132,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
} finally {
settleOperation(operationId);
}
- }, [beginOperation, operations, settleOperation]);
+ }, [beginOperation, operations, premiereRequested, settleOperation]);
async function refreshInventory(directory: string): Promise {
try {
@@ -172,6 +190,14 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
if (id === 'worker' && checked && manifest) {
setCapabilities(Object.keys(manifest.capabilities));
}
+ if (!checked && (id === 'worker' || id === 'server')) setPremiereEnabled(false);
+ }
+
+ function togglePremiere(checked: boolean) {
+ setPremiereEnabled(checked);
+ if (!checked) return;
+ setSurfaces([...new Set([...surfaces, 'worker', 'server'])]);
+ if (manifest) setCapabilities(Object.keys(manifest.capabilities));
}
function toggleCapability(id: string, checked: boolean) {
@@ -206,13 +232,17 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
if (operationId === null) return;
const captured = {
capabilities: [...capabilities],
- surfaces: [...surfaces],
+ surfaces: premiereEnabled ? [...new Set([...surfaces, 'worker', 'server'])] : [...surfaces],
+ premiere: premiereEnabled,
prepare_models: prepareDuringInstall,
model_directory: modelDirectory || undefined,
draft_id: draftId,
};
setFailure(null);
setInstallFailure(null);
+ setCancelFailure(null);
+ setCancelRequested(false);
+ cancelRequestedRef.current = false;
setSetupProgress({
draft_id: draftId,
current: 1,
@@ -222,7 +252,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
});
try {
setMessage('Checking FFmpeg and required codecs…');
- await installMediaRuntime(draftId);
+ await installMediaRuntime(draftId, captured.prepare_models ? 8 : 7);
if (status?.state === 'broken' && status.runtime_profile && !dirty) {
const repaired = await runtimeStatus();
if (repaired.ready) {
@@ -237,18 +267,66 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
: 'Installing VidXP…',
);
const result = await installRuntime(captured);
+ let premiereNotice: { color: 'teal' | 'yellow'; title: string; detail: string } | undefined;
+ if (captured.premiere) {
+ setMessage('Starting VidXP privately and installing the Premiere extension…');
+ try {
+ const server = await localServerStatus();
+ if (!server.running) await startLocalServer();
+ const extension = await installPremiereExtensions();
+ premiereNotice = {
+ color: extension.opened_packages.length > 0 ? 'yellow' : 'teal',
+ title: extension.opened_packages.length > 0 ? 'Finish Premiere setup in Creative Cloud' : 'VidXP and Premiere are ready',
+ detail: extension.detail,
+ };
+ } catch (error) {
+ premiereNotice = {
+ color: 'yellow',
+ title: 'VidXP is ready; Premiere setup needs attention',
+ detail: errorMessage(error, 'The Premiere extension could not be installed. Use Install for Premiere from the VidXP summary screen to retry.'),
+ };
+ }
+ }
setMessage(result.install.prepared ? 'VidXP and the selected search features are ready.' : 'VidXP is installed. Search files can be downloaded later.');
- onCommitted(result.setup);
+ onCommitted(result.setup, premiereNotice);
} catch (error) {
- const detail = errorMessage(error, 'Setup did not finish. Your previous VidXP installation is unchanged.');
+ const detail = cancelRequestedRef.current
+ ? 'Setup was cancelled. Your previous VidXP installation is unchanged.'
+ : errorMessage(error, 'Setup did not finish. Your previous VidXP installation is unchanged.');
setFailure(detail);
setInstallFailure(detail);
} finally {
+ cancelRequestedRef.current = false;
+ setCancelRequested(false);
settleOperation(operationId);
setSetupProgress(null);
}
}
+ async function cancelInstall() {
+ if (cancelRequestedRef.current) return;
+ cancelRequestedRef.current = true;
+ setCancelRequested(true);
+ setCancelFailure(null);
+ setSetupProgress((current) => ({
+ draft_id: draftId,
+ current: current?.current ?? 1,
+ total: current?.total ?? (prepareDuringInstall ? 8 : 7),
+ stage: current?.stage ?? 'cancelling',
+ message: 'Stopping setup safely',
+ model_message: current?.model_message,
+ model_current: current?.model_current,
+ model_total: current?.model_total,
+ }));
+ try {
+ await cancelManagedSetupOperation(draftId);
+ } catch (error) {
+ cancelRequestedRef.current = false;
+ setCancelRequested(false);
+ setCancelFailure(errorMessage(error, 'Setup could not be stopped.'));
+ }
+ }
+
async function launch() {
const operationId = beginOperation('launch');
if (operationId === null) return;
@@ -294,13 +372,16 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
|| !sameValues(surfaces, status?.surfaces ?? [])
|| modelDirectory !== status?.model_directory
);
+ const premiereNeedsInstall = premiereEnabled && !premiere?.cep_installed && !premiere?.uxp_installed;
async function resetDraft() {
if (!recoverableConfiguration || !status) return;
const operationId = beginOperation('reset');
if (operationId === null) return;
setCapabilities(status.capabilities);
- setSurfaces(status.surfaces);
+ const keepPremiere = premiereRequested || Boolean(premiere?.cep_installed || premiere?.uxp_installed);
+ setPremiereEnabled(keepPremiere);
+ setSurfaces(keepPremiere ? [...new Set([...status.surfaces, 'worker', 'server'])] : status.surfaces);
setModelDirectory(status.model_directory);
setInventory(null);
setFailure(null);
@@ -315,6 +396,29 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
const attentionTitle = /ffmpeg|ffprobe/i.test(message) ? 'Video tools need attention' : 'VidXP needs attention';
const progressCurrent = setupProgress?.current ?? 1;
const progressTotal = setupProgress?.total ?? (prepareDuringInstall ? 8 : 7);
+ const selectedModelDownloads = new Map();
+ for (const capability of capabilities) {
+ for (const model of manifest?.capabilities[capability]?.models ?? []) {
+ selectedModelDownloads.set(
+ model.cache_key,
+ Math.max(selectedModelDownloads.get(model.cache_key) ?? 0, model.download_size_bytes),
+ );
+ }
+ }
+ const selectedModelBytes = [...selectedModelDownloads.values()].reduce((total, bytes) => total + bytes, 0);
+ const managedRuntimeBytes = manifest?.managed_runtime_estimated_size_bytes ?? 0;
+ const plannedSetupBytes = managedRuntimeBytes + selectedModelBytes;
+ const capabilityModelSummary = capabilities
+ .map((id) => {
+ const capability = manifest?.capabilities[id];
+ if (!capability) return null;
+ const models = new Map();
+ for (const model of capability.models ?? []) models.set(model.cache_key, model.download_size_bytes);
+ const bytes = [...models.values()].reduce((total, size) => total + size, 0);
+ return `${capability.label}: ${formatBytes(bytes)}`;
+ })
+ .filter((summary): summary is string => summary !== null)
+ .join(' · ');
function dismissInstallFailure() {
setInstallFailure(null);
@@ -399,6 +503,15 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
Interfaces and integrations
+ {premiere?.platform_supported && (
+ togglePremiere(event.currentTarget.checked)}
+ label="Premiere Pro extension"
+ description="Install the matching Adobe extension. VidXP includes local processing and its private connection automatically."
+ />
+ )}
{Object.entries(manifest.surfaces).filter(([id]) => id !== 'worker').map(([id, surface]) => (
toggleSurface(id, event.currentTarget.checked)} label={surface.label} description={surface.description} />
))}
@@ -411,7 +524,9 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
} loading={operation === 'folder'} disabled={isBusy} onClick={() => void chooseFolder()}>Change location…
- The managed runtime can use approximately 3 GiB. Models add 37 MiB to 4.11 GiB depending on the selected search features. A full local setup uses approximately 7.1 GiB, plus temporary installation space, indexes, and videos.
+ The managed runtime can use approximately {formatBytes(managedRuntimeBytes)}. Selected model downloads total up to {formatBytes(selectedModelBytes)}.
+ {capabilityModelSummary}
+ Plan for approximately {formatBytes(plannedSetupBytes)} locally, plus temporary installation space, indexes, and videos. Valid cached model files are reused.
{operation === 'load' || operation === 'folder' || operation === 'reset' ? (
@@ -463,12 +578,12 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, onBack, o
{recoverableConfiguration ? (
void resetDraft()}>Reset changes
- void install()}>{status?.state === 'broken' && !dirty ? 'Repair VidXP' : 'Apply update'}
+ void install()}>{premiereEnabled ? 'Apply and install Premiere' : status?.state === 'broken' && !dirty ? 'Repair VidXP' : 'Apply update'}
void prepareModels()}>Check downloaded models
} loading={operation === 'launch'} disabled={!status?.ready || dirty || !displayedRuntimeSelected || isBusy} onClick={() => void launch()}>Open VidXP
) : (
- void install()}>{corruptPointer ? 'Rebuild VidXP' : 'Install VidXP'}
+ void install()}>{premiereEnabled ? 'Install VidXP and Premiere' : corruptPointer ? 'Rebuild VidXP' : 'Install VidXP'}
)}
{setupProgress?.message ?? 'Starting managed setup'}
The existing installation remains active until every step has completed and the replacement passes validation.
+ {cancelFailure && {cancelFailure} }
+
+ void cancelInstall()}>
+ {cancelRequested ? 'Stopping…' : 'Cancel setup'}
+
+
)}
diff --git a/desktop/src/components/PremiereIntegration.tsx b/desktop/src/components/PremiereIntegration.tsx
new file mode 100644
index 00000000..8e008f01
--- /dev/null
+++ b/desktop/src/components/PremiereIntegration.tsx
@@ -0,0 +1,112 @@
+import { Alert, Badge, Button, Code, Group, Loader, Stack, Text, Title } from '@mantine/core';
+import { IconDownload, IconRefresh, IconTrash } from '@tabler/icons-react';
+import { useEffect, useState } from 'react';
+
+import {
+ errorMessage,
+ installPremiereExtensions,
+ localServerStatus,
+ premiereIntegrationState,
+ startLocalServer,
+ uninstallPremiereExtensions,
+ type PremiereInstallResult,
+ type PremiereIntegrationState as IntegrationState,
+} from '../tauri';
+
+interface PremiereIntegrationProps {
+ operationPending?: boolean;
+ serverEnabled: boolean;
+ canConfigureDependency: boolean;
+ onConfigureDependency: () => void;
+}
+
+export function PremiereIntegration({ operationPending, serverEnabled, canConfigureDependency, onConfigureDependency }: PremiereIntegrationProps) {
+ const [state, setState] = useState(null);
+ const [busy, setBusy] = useState<'refresh' | 'install' | 'remove' | null>('refresh');
+ const [failure, setFailure] = useState(null);
+ const [result, setResult] = useState(null);
+
+ async function refresh() {
+ setBusy('refresh');
+ setFailure(null);
+ try {
+ setState(await premiereIntegrationState());
+ } catch (error) {
+ setFailure(errorMessage(error, 'VidXP could not inspect Premiere installations.'));
+ } finally {
+ setBusy(null);
+ }
+ }
+
+ useEffect(() => { void refresh(); }, []);
+
+ async function install() {
+ setBusy('install');
+ setFailure(null);
+ setResult(null);
+ try {
+ const server = await localServerStatus();
+ if (!server.running) await startLocalServer();
+ setResult(await installPremiereExtensions());
+ setState(await premiereIntegrationState());
+ } catch (error) {
+ setFailure(errorMessage(error, 'VidXP could not install the Premiere extension.'));
+ } finally {
+ setBusy(null);
+ }
+ }
+
+ async function remove() {
+ setBusy('remove');
+ setFailure(null);
+ setResult(null);
+ try {
+ await uninstallPremiereExtensions();
+ setState(await premiereIntegrationState());
+ } catch (error) {
+ setFailure(errorMessage(error, 'VidXP could not remove the Premiere extension.'));
+ } finally {
+ setBusy(null);
+ }
+ }
+
+ const installed = Boolean(state?.cep_installed || state?.uxp_installed);
+ const unavailablePackage = state && (!state.cep_package_available || !state.uxp_package_available);
+
+ return (
+
+
+
+
Premiere Pro extension
+ Desktop installs the matching extension for every compatible Premiere version it finds. No repository, Node.js, or developer mode is required.
+
+ } loading={busy === 'refresh'} disabled={Boolean(operationPending || (busy && busy !== 'refresh'))} onClick={() => void refresh()}>Refresh
+
+
+ {busy === 'refresh' && !state ?
: state &&
+ {!state.platform_supported
+ ? Desktop can install the Premiere extension on Windows and macOS.
+ : state.installations.length === 0
+ ? {state.detail} Installing is still safe: the CEP and UXP packages have non-overlapping host ranges.
+ : state.installations.map((installation) =>
+ {installation.display_name} Version {installation.version} · {installation.host_kind === 'cep' ? 'CEP extension' : installation.host_kind === 'uxp' ? 'UXP extension' : 'Not supported'} {installation.executable}
+ {installation.compatible ? 'Compatible' : 'Unsupported'}
+ )}
+
+
+ Premiere 23–25.5 {state.cep_installed ? 'installed' : 'available'}
+ Premiere 25.6+ {state.uxp_installed ? 'installed' : 'available'}
+
+ {!serverEnabled && {canConfigureDependency ? 'VidXP will add local processing and its private app service when you continue.' : 'This externally managed VidXP installation needs the App integration service. Add that feature with its setup, then return here.'} }
+ {!state.installer_available && state.platform_supported && Adobe's background plugin installer was not found. VidXP will open the packaged extension so Creative Cloud can finish the installation. }
+ {unavailablePackage && Reinstall or update VidXP Desktop. Release installers include both Premiere packages. }
+ {result && {result.detail} }
+ {failure && {failure} }
+
+ {installed && } loading={busy === 'remove'} disabled={Boolean(operationPending || (busy && busy !== 'remove'))} onClick={() => void remove()}>Remove extension}
+ } loading={busy === 'install'} disabled={!state.platform_supported || Boolean(unavailablePackage) || Boolean(operationPending || (busy && busy !== 'install'))} onClick={() => serverEnabled ? void install() : onConfigureDependency()}>{serverEnabled ? installed ? 'Reinstall for Premiere' : 'Install for Premiere' : canConfigureDependency ? 'Set up Premiere' : 'Open setup options'}
+
+ }
+
+ );
+}
diff --git a/desktop/src/components/TargetSummary.tsx b/desktop/src/components/TargetSummary.tsx
index 3311ae85..684052e4 100644
--- a/desktop/src/components/TargetSummary.tsx
+++ b/desktop/src/components/TargetSummary.tsx
@@ -2,6 +2,8 @@ import { Alert, Badge, Button, Checkbox, Code, Group, Loader, Modal, Stack, Text
import { IconActivityHeartbeat, IconCopy, IconExternalLink, IconPlugConnected, IconPlayerPlay, IconPlayerStop, IconRefresh, IconSettings, IconShare, IconTerminal2 } from '@tabler/icons-react';
import { useEffect, useRef, useState } from 'react';
+import { PremiereIntegration } from './PremiereIntegration';
+
import {
errorMessage,
browserServiceStatus,
@@ -38,26 +40,18 @@ interface TargetSummaryProps {
opening?: boolean;
onRecheck: () => Promise;
onManageManaged: () => void;
+ onSetUpPremiere: () => void;
onSetupChanged: (setup: TargetSetupState) => void;
onChooseAnother: () => void;
onOpen: () => Promise;
}
-const CAPABILITY_LABELS: Record = {
- actor: 'Actor recognition',
- dialogue: 'Dialogue search',
- media: 'Video tools',
- scene: 'Visual scene search',
- sound: 'Sound event search',
- videoprism: 'Temporal video search',
-};
-
interface WorkerFailure {
title: string;
detail: string;
}
-export function TargetSummary({ profile, validationError, checking, operationPending, opening, onRecheck, onManageManaged, onSetupChanged, onChooseAnother, onOpen }: TargetSummaryProps) {
+export function TargetSummary({ profile, validationError, checking, operationPending, opening, onRecheck, onManageManaged, onSetUpPremiere, onSetupChanged, onChooseAnother, onOpen }: TargetSummaryProps) {
const executable = profile.display_executable;
const [doctor, setDoctor] = useState(null);
const [server, setServer] = useState(null);
@@ -89,7 +83,17 @@ export function TargetSummary({ profile, validationError, checking, operationPen
const serverAvailable = runtimeCompatible && profile.surfaces.includes('server');
const failedChecks = doctor?.checks.filter((check) => !check.ok) ?? [];
- const capabilityLabel = (capability: string) => CAPABILITY_LABELS[capability] ?? capability;
+ const capabilityLabel = (capability: string) => capability === 'media'
+ ? 'Video tools'
+ : externalManifest?.capabilities[capability]?.label ?? capability;
+
+ useEffect(() => {
+ let active = true;
+ void runtimeManifest().then((manifest) => {
+ if (active) setExternalManifest(manifest);
+ }).catch(() => undefined);
+ return () => { active = false; };
+ }, []);
useEffect(() => {
setDoctor(null);
@@ -231,7 +235,7 @@ export function TargetSummary({ profile, validationError, checking, operationPen
setExternalFailure(null);
setExternalTechnical(null);
try {
- const manifest = await runtimeManifest();
+ const manifest = externalManifest ?? await runtimeManifest();
setExternalManifest(manifest);
if (needsRuntimeUpdate) {
const defaultSurfaces = Object.entries(manifest.surfaces).filter(([, surface]) => surface.default).map(([id]) => id);
@@ -396,6 +400,13 @@ export function TargetSummary({ profile, validationError, checking, operationPen
+ void openExternalSetup()}
+ />
+
Health and background services
Check whether VidXP is usable and control only the services you enabled.
diff --git a/desktop/src/desktopPackagingContracts.test.ts b/desktop/src/desktopPackagingContracts.test.ts
index cdbb9b76..70dde383 100644
--- a/desktop/src/desktopPackagingContracts.test.ts
+++ b/desktop/src/desktopPackagingContracts.test.ts
@@ -13,10 +13,12 @@ function read(path: string) {
describe('Desktop packaging and documentation contracts', () => {
it('packages the project license and complete third-party notices', () => {
const config = JSON.parse(read('desktop/src-tauri/tauri.conf.json'));
- expect(config.bundle.resources).toEqual(expect.arrayContaining([
- '../THIRD_PARTY_NOTICES.txt',
- '../../LICENSE',
- ]));
+ expect(config.bundle.resources).toMatchObject({
+ '../THIRD_PARTY_NOTICES.txt': 'THIRD_PARTY_NOTICES.txt',
+ '../../LICENSE': 'LICENSE',
+ '../../premiere/packages/vidxp-premiere-cep.zxp': 'premiere/vidxp-premiere-cep.zxp',
+ '../../premiere/packages/vidxp-premiere-uxp.ccx': 'premiere/vidxp-premiere-uxp.ccx',
+ });
const notices = read('desktop/THIRD_PARTY_NOTICES.txt');
expect(notices).toContain('VIDXP PROJECT LICENSE');
diff --git a/desktop/src/tauri.test.ts b/desktop/src/tauri.test.ts
index 658dbd6c..422388c3 100644
--- a/desktop/src/tauri.test.ts
+++ b/desktop/src/tauri.test.ts
@@ -7,6 +7,7 @@ vi.mock('@tauri-apps/api/event', () => ({ listen }));
import {
beginManagedSetup,
+ cancelManagedSetupOperation,
displayPath,
installRuntime,
installCodexPlugin,
@@ -90,6 +91,14 @@ describe('desktop IPC adapter', () => {
expect(invoke).toHaveBeenNthCalledWith(2, 'install_runtime', { request });
});
+ it('cancels only the active managed setup draft', async () => {
+ invoke.mockResolvedValue(undefined);
+
+ await cancelManagedSetupOperation('draft-1');
+
+ expect(invoke).toHaveBeenCalledWith('cancel_managed_setup_operation', { draftId: 'draft-1' });
+ });
+
it('maps managed setup progress events to their payload', async () => {
const stop = vi.fn();
listen.mockResolvedValue(stop);
diff --git a/desktop/src/tauri.ts b/desktop/src/tauri.ts
index 78df41ee..591a231a 100644
--- a/desktop/src/tauri.ts
+++ b/desktop/src/tauri.ts
@@ -134,7 +134,8 @@ export interface LocalTargetInspection extends Omit
;
surfaces: Record;
}
@@ -264,6 +266,33 @@ export interface CodexPluginInstallResult {
detail: string;
}
+export type PremiereHostKind = 'cep' | 'uxp' | 'unsupported';
+
+export interface PremiereInstallation {
+ display_name: string;
+ version: string;
+ executable: string;
+ host_kind: PremiereHostKind;
+ compatible: boolean;
+}
+
+export interface PremiereIntegrationState {
+ installations: PremiereInstallation[];
+ platform_supported: boolean;
+ installer_available: boolean;
+ cep_package_available: boolean;
+ uxp_package_available: boolean;
+ cep_installed: boolean;
+ uxp_installed: boolean;
+ detail: string;
+}
+
+export interface PremiereInstallResult {
+ installed_hosts: PremiereHostKind[];
+ opened_packages: string[];
+ detail: string;
+}
+
interface WireInstallTransitionResult {
install: InstallRuntimeResult;
setup: WireTargetState;
@@ -355,14 +384,18 @@ export function cancelManagedSetup(draftId: string): Promise {
return invoke('cancel_managed_setup', { draftId }).then(normalizeState);
}
+export function cancelManagedSetupOperation(draftId: string): Promise {
+ return invoke('cancel_managed_setup_operation', { draftId });
+}
+
export function runtimeManifest(): Promise { return invoke('runtime_manifest'); }
export function runtimeStatus(): Promise { return invoke('runtime_status'); }
export function modelDirectoryInventory(directory?: string): Promise {
return invoke('model_directory_inventory', { directory: directory || null });
}
export function chooseModelDirectory(): Promise { return invoke('choose_model_directory'); }
-export function installMediaRuntime(draftId: string): Promise {
- return invoke('install_media_runtime', { draftId });
+export function installMediaRuntime(draftId: string, totalSteps: number): Promise {
+ return invoke('install_media_runtime', { draftId, totalSteps });
}
export function installRuntime(request: InstallRuntimeRequest): Promise {
return invoke('install_runtime', { request }).then((result) => ({
@@ -385,6 +418,9 @@ export function configureExternalInstallation(capabilities: string[], surfaces:
}
export function mcpClientConfig(): Promise { return invoke('mcp_client_config'); }
export function installCodexPlugin(): Promise { return invoke('install_codex_plugin'); }
+export function premiereIntegrationState(): Promise { return invoke('premiere_integration_state'); }
+export function installPremiereExtensions(): Promise { return invoke('install_premiere_extensions'); }
+export function uninstallPremiereExtensions(): Promise { return invoke('uninstall_premiere_extensions'); }
export function localWorkerStatus(): Promise { return invoke('local_worker_status'); }
export function startLocalWorker(): Promise { return invoke('start_local_worker'); }
export function stopLocalWorker(): Promise { return invoke('stop_local_worker'); }
diff --git a/docs/adding-a-capability.md b/docs/adding-a-capability.md
index a2bdf47c..fd541dcf 100644
--- a/docs/adding-a-capability.md
+++ b/docs/adding-a-capability.md
@@ -47,6 +47,7 @@ Start in `definition.py`. It must export one frozen, Pydantic-validated
Every capability declares:
- a stable internal `name`;
+- a product-facing `label`;
- a short human-readable `description`;
- the package `extra` that installs its dependencies;
- its Pydantic configuration model; and
@@ -138,7 +139,8 @@ meaning may make existing indexes invalid. When it does, require a rebuild and
keep the previous index active until the replacement passes validation. State
the rebuild requirement in both the release note and user documentation.
-If a model contract is displayed by Desktop, regenerate the checked-in catalog:
+Regenerate the checked-in Desktop catalogs after changing a capability label,
+description, package extra, or model contract:
```bash
npm --prefix desktop run model-catalog:write
@@ -152,7 +154,13 @@ Review the generated diff rather than editing the catalog by hand.
After the definition and executor are ready, connect them with a
`CapabilityPlugin` named `PLUGIN`. Register that plugin explicitly in
`src/vidxp/capabilities/registry.py`. For an ordinary capability, the registry
-is the only central runtime file that should change.
+is the only central Python runtime file that should change.
+
+Desktop derives the capability's package extra, modality, product label,
+description, and model download plan from the generated capability catalog.
+Do not add the capability to `desktop/runtime-manifest.json` or a UI label map.
+Add a Desktop test that verifies the generated manifest produces the expected
+package specification for the new capability.
Generic commands discover capability names and operations from the registry.
Most capabilities therefore need no CLI code. Add `cli.py` only when the
diff --git a/docs/desktop.md b/docs/desktop.md
index 15b7c299..d09af8eb 100644
--- a/docs/desktop.md
+++ b/docs/desktop.md
@@ -54,7 +54,8 @@ The main Desktop files are:
| `desktop/src-tauri/src/` | Tauri commands, setup lifecycle, activation, and process supervision |
| `desktop/runtime-manifest.json` | Pinned Python and VidXP runtime versions |
| `desktop/sidecars.json` | Pinned `uv` sidecar versions and archive checksums |
-| `desktop/model-cache-catalog.json` | Generated catalog of model downloads shown during setup |
+| `desktop/capability-catalog.json` | Generated capability labels, installation extras, and model download plans |
+| `desktop/model-cache-catalog.json` | Generated model-cache recognition catalog |
| `desktop/scripts/` | Sidecar, model-catalog, notice, branding, and package scripts |
| `desktop/THIRD_PARTY_NOTICES.txt` | Generated notices shipped with the installers |
@@ -126,11 +127,13 @@ Use `npm --prefix desktop run sidecar:windows` instead of `sidecar:unix` on
Windows. Report the exact commands you ran and any platform package you could
not build or inspect.
-Two checked-in files must stay synchronized with their source contracts:
+Three checked-in files must stay synchronized with their source contracts:
-- After changing capability or model contracts, run
+- After changing capability labels, descriptions, extras, or model contracts,
+ run
`npm --prefix desktop run model-catalog:write`, review the diff, and run the
- corresponding `:check` command.
+ corresponding `:check` command. This updates both Desktop catalogs from the
+ canonical capability registry.
- After changing a production dependency or license, run
`npm --prefix desktop run notices:write`, review the inventory, and run the
corresponding `:check` command.
@@ -189,6 +192,12 @@ The user-facing feature choices map to package extras as follows:
These package names are implementation details and should not replace the
product labels in the interface.
+The capability registry owns capability labels, descriptions, installation
+extras, and model specifications. Desktop embeds a generated capability
+catalog because it must show setup choices before a VidXP runtime exists. The
+build merges that generated catalog with Desktop-owned surface and runtime
+metadata; React must not maintain a parallel capability list or storage table.
+
The installer does not bundle FFmpeg. If it is missing, setup may offer the
supported WinGet command on Windows or Homebrew command on macOS, but it must
wait for user confirmation before running either one. On Linux, setup shows an
@@ -215,8 +224,18 @@ VidXP currently publishes:
| macOS Apple Silicon | DMG |
| Linux x86-64 | AppImage |
-The release workflow builds all three packages from the stamped release commit
-and attaches them to one GitHub release.
+The release workflow builds all three Desktop packages from the stamped
+release commit and attaches them to one GitHub release. Before those builds it
+also creates the signed CEP `.zxp` and UXP `.ccx` Premiere packages. Every
+Desktop installer embeds both packages as resources, so installation happens
+on the user's computer without source code or build tooling.
+
+Desktop detects standard Premiere installations, assigns versions 23.0–25.5
+to CEP and 25.6 or newer to UXP, and calls Adobe's Unified Plugin Installer
+Agent. If Adobe requires an interactive confirmation, Desktop opens the
+already-built package with Creative Cloud. The package host ranges are
+non-overlapping, so both may remain installed on a workstation with multiple
+Premiere generations.
Beta and stable macOS DMGs are signed with a Developer ID certificate and the
hardened runtime. The workflow then notarizes, staples, and verifies each DMG
diff --git a/docs/integrations/premiere-pro.md b/docs/integrations/premiere-pro.md
new file mode 100644
index 00000000..4acb48a1
--- /dev/null
+++ b/docs/integrations/premiere-pro.md
@@ -0,0 +1,106 @@
+# Search Premiere Pro media with VidXP
+
+The VidXP Premiere Pro extension lets an editor select clips or bins from the
+open project, index their existing source files, and search the resulting
+library without leaving Premiere. It discovers dialogue, sound, scene, actor,
+and future search features from the connected VidXP runtime instead of keeping
+a fixed capability list in the extension.
+
+## Install the extension
+
+Install VidXP Desktop from the official
+[GitHub release](https://github.com/grayhatdevelopers/vidxp/releases). No Git
+checkout, Node.js installation, local build, Adobe developer mode, or Premiere
+upgrade is required for Premiere Pro 23.2.
+
+1. In Desktop setup, select **Premiere Pro extension** and the search features
+ you want. Desktop automatically includes local video processing and its
+ private app connection.
+2. Complete an Adobe Creative Cloud confirmation window if one appears.
+3. Restart Premiere Pro.
+
+For an existing Desktop-managed setup, select **Set up Premiere** on its summary
+screen. The Premiere requirements are preselected and Desktop installs the Adobe
+package after updating VidXP. Use **Install for Premiere** only to reinstall or
+retry the Adobe package without changing VidXP features.
+
+Desktop ships both Adobe extension packages and chooses from the installed
+Premiere versions:
+
+| Premiere version | Extension | Open the panel from |
+|---|---|---|
+| 23.0–25.5 | CEP 11 | **Window > Extensions (Legacy) > VidXP Search** |
+| 25.6 or newer | UXP | **Window > UXP Plugins > VidXP Search** |
+
+The package ranges do not overlap. A workstation with an older and a current
+Premiere installation can keep both packages installed without duplicate
+panels in either host.
+
+Desktop uses Adobe Creative Cloud's official Unified Plugin Installer Agent.
+If the background installer is unavailable or needs user interaction, Desktop
+opens the bundled `.zxp` or `.ccx` package so Creative Cloud can finish the
+installation. It never builds extension code on the user's computer.
+
+## Connect VidXP
+
+The extension and VidXP must run on the same computer because Premiere gives
+the panel paths to media already present in the project. VidXP indexes those
+source files in place; it does not upload or duplicate them.
+
+Premiere setup starts the private app service. Local video processing starts
+when VidXP needs it; if you stop either service later, start it again from the
+Desktop summary. Copy the displayed API address into the Premiere panel and
+connect.
+
+Desktop can choose an available local port, so use its displayed address
+instead of assuming the default `http://127.0.0.1:32191`.
+
+## Index Premiere media
+
+1. Open a Premiere project and connect the panel.
+2. Search the project tree or choose **Use current selection** to mirror the
+ Project panel selection.
+3. Select clips or bins. A Premiere bin expands to its file-backed descendants;
+ it is not treated as a filesystem directory.
+4. Choose any indexing features reported by VidXP and start indexing.
+5. Keep the panel open while it reports batch progress. A dismissible notice
+ reports completion and identifies individual failures.
+
+Offline clips, sequences, generated items without a media path, and duplicate
+source paths are not submitted. Large selections are split into durable VidXP
+ingestion sessions.
+
+## Search indexed moments
+
+Enter a description, choose one indexed video or the complete active library,
+and select any searchable features reported by VidXP. Results show the source
+video, time range, contributing features, and fused score.
+
+Timeline navigation, Source Monitor actions, marker creation, and snippet
+insertion remain future host-adapter operations. They can be added without
+changing the VidXP client or shared search workflow.
+
+## Current release limits
+
+- Windows is the first supported packaging target. Premiere Pro 23.2 must pass
+ the CEP host checklist before the release is promoted beyond preview.
+- Adobe blocks ordinary `http://` URLs in Premiere UXP on macOS. The 25.6+
+ macOS panel needs a trusted loopback HTTPS transport before it is supported.
+- CEP on macOS uses its native Node transport, but installation and media-path
+ behavior still require host validation.
+- Proxy, subclip, Productions, UNC, mounted-volume, offline-media, and React 19
+ control behavior remain explicit manual release gates.
+- Completion appears inside the panel because neither host generation exposes
+ a dependable native Premiere notification API for this workflow.
+
+Contributors should read the [extension architecture](../../premiere/README.md)
+and run the [manual host checklist](../../premiere/docs/MANUAL_TEST_CHECKLIST.md).
+
+## Adobe references
+
+- [Premiere UXP introduction](https://developer.adobe.com/premiere-pro/uxp/introduction/)
+- [Package a UXP plugin](https://developer.adobe.com/premiere-pro/uxp/plugins/distribution/package/)
+- [Install a UXP plugin](https://developer.adobe.com/premiere-pro/uxp/plugins/distribution/install/)
+- [Premiere UXP network operations](https://developer.adobe.com/premiere-pro/uxp/resources/recipes/network/)
+- [Adobe CEP PProPanel sample](https://github.com/Adobe-CEP/Samples/tree/master/PProPanel)
+- [Adobe CEP 11 cookbook](https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_11.x/Documentation/CEP%2011.1%20HTML%20Extension%20Cookbook.md)
diff --git a/docs/local-api.md b/docs/local-api.md
index 9c0a26cc..b48aed5f 100644
--- a/docs/local-api.md
+++ b/docs/local-api.md
@@ -52,6 +52,24 @@ If port `32191` is already in use, choose another one:
vidxp-api --port 32192
```
+### Ingest media already available on this computer
+
+A local application can register media without copying the video through an
+HTTP request. Send one to ten absolute file paths to
+`POST /api/v1/media/local-ingestions`, then poll the URL from its `Location`
+header until the session is terminal. Set `modalities` to the indexable names
+returned by `GET /api/v1/capabilities`; omit it to use every enabled indexing
+feature.
+
+This operation is available only when `vidxp-api` runs in local mode. Every
+path must be readable by the VidXP process and, when trusted import roots are
+configured, must be inside one of those roots. The response never returns a
+source path. Use the upload operations instead when the application and VidXP
+do not share a filesystem.
+
+The [Premiere Pro extension preview](integrations/premiere-pro.md) uses this
+workflow for media already loaded in a Premiere project.
+
## Connect a local AI assistant
An assistant that can start a program on the same computer does not need the
diff --git a/premiere/README.md b/premiere/README.md
new file mode 100644
index 00000000..c782d80c
--- /dev/null
+++ b/premiere/README.md
@@ -0,0 +1,102 @@
+# VidXP Premiere Pro extension
+
+This directory builds the VidXP panel for both Premiere extension generations.
+The local Uplift repository was used only as a UXP packaging and host-boundary
+reference; this is not an Uplift integration.
+
+## Architecture
+
+```text
+Shared React workflow (`src/ui`)
+├── typed VidXP client (`src/services/vidxp`)
+├── shared media-library rules (`src/premiere/library.ts`)
+├── Bolt UXP build + Premiere adapter (`uxp.config.ts`, `index.tsx`, `adapter.ts`)
+└── CEP bootstrap + Premiere adapter (`cep/`, `cep-adapter.ts`)
+ ├── ES3 ExtendScript project bridge (`cep/jsx/host.jsx`)
+ └── CEP Node loopback transport (`cep-fetch.ts`)
+```
+
+The UI receives a `PremiereAdapter`; it does not import a host API. The UXP
+adapter alone imports Premiere's `premierepro` module. The CEP adapter alone
+uses `evalScript`, and its ExtendScript bridge returns bounded JSON. Indexing,
+polling, search, status, capability discovery, and selection rules are shared.
+
+UXP renders Adobe's built-in Spectrum widgets through the typed control
+wrapper. CEP renders native HTML controls through that same wrapper because
+Spectrum UXP widgets do not exist in CEP. React remains at version 19 for both
+builds, with CEP compiled for Chromium 88.
+
+Capability names and roles come from `GET /api/v1/capabilities`; neither host
+adapter hardcodes dialogue, sound, scene, action, or future capability names.
+
+## Build and package
+
+```bash
+npm ci
+npm run check
+npm run package
+```
+
+The UXP target follows Bolt UXP's React scaffold: Vite owns the HTML entry,
+`@vitejs/plugin-react` compiles React, and
+[Bolt UXP](https://github.com/hyperbrew/bolt-uxp)'s `vite-uxp-plugin` owns
+manifest generation, UXP-compatible transforms and polyfills, hot reload, CCX
+creation, and package installation actions. Bolt's Premiere-specific host color
+variables are initialized before React mounts and update when the host theme
+changes. The separate CEP target shares the application code but retains its
+own Vite and ZXP signing path because Bolt UXP does not build CEP extensions.
+
+`npm run check` creates the UXP development bundle under `dist/` and the CEP
+bundle under `dist/cep`.
+`npm run package` additionally creates these ignored release inputs:
+
+- `packages/vidxp-premiere-cep.zxp`, signed and timestamped for Premiere
+ 23.0–25.5;
+- `packages/vidxp-premiere-uxp.ccx` for Premiere 25.6 or newer.
+
+The Desktop release workflow performs this packaging once and embeds both
+artifacts in every Desktop installer. End users do not run these commands.
+
+For UXP-only development, add `dist/manifest.json` to UXP Developer Tool or use
+the checked-in VS Code attach configuration. Run `npm run dev` for Bolt UXP hot
+reload. After `npm run package:uxp`, you can install or remove that development
+CCX with Bolt's actions:
+
+```bash
+npm run ccx-install
+npm run ccx-uninstall
+```
+
+CEP development uses an unsigned `dist/cep` build and therefore requires the
+CEP debugging setup documented by Adobe. Neither developer path is an end-user
+installation method.
+
+Automated tests cover the shared VidXP client, library rules, React build, both
+package builds, Desktop resource contract, and Desktop version routing. They do
+not exercise Premiere, Creative Cloud, real media paths, a running VidXP
+service, or model inference. Run the
+[manual checklist](docs/MANUAL_TEST_CHECKLIST.md) for those boundaries.
+
+## POC decisions
+
+- Premiere file paths are passed to VidXP's local ingestion endpoint; source
+ video bytes are not copied into extension storage.
+- Bolt's optional webview, hybrid-plugin, and multi-host modes are disabled.
+ The panel has one Premiere UXP context, and Bolt's hybrid helper targets
+ Photoshop and InDesign rather than Premiere.
+- `bolt-uxp-utils` is not used while the UXP package supports Premiere 25.6.
+ The utility package requires Premiere 26.3 or newer, so the typed adapter
+ calls Premiere's host API directly until the minimum supported version moves.
+- CEP uses its Node HTTP client for loopback requests instead of broadening the
+ API's browser CORS policy.
+- Desktop owns runtime setup, service startup, extension detection,
+ installation, removal, and user-visible installation status.
+- Package host ranges do not overlap: CEP is 23.0–25.5 and UXP begins at 25.6.
+- Bins expand recursively and source paths are deduplicated before submission.
+- In-panel progress and completion notices are the primary workflow status.
+- Timeline navigation and snippet insertion remain future adapter methods.
+- Windows is the first release-validation target. Premiere UXP blocks plain
+ loopback HTTP on macOS, so 25.6+ macOS support needs trusted local HTTPS.
+
+The user setup is in
+[`docs/integrations/premiere-pro.md`](../docs/integrations/premiere-pro.md).
diff --git a/premiere/cep/CSXS/manifest.xml b/premiere/cep/CSXS/manifest.xml
new file mode 100644
index 00000000..0816fe95
--- /dev/null
+++ b/premiere/cep/CSXS/manifest.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./index.html
+ ./jsx/host.jsx
+
+ --enable-nodejs
+ --mixed-context
+
+
+
+ true
+
+
+ Panel
+ VidXP Search
+
+
+ 380
+ 720
+
+
+ 320
+ 480
+
+
+ 1800
+ 1800
+
+
+
+
+
+
+
diff --git a/premiere/cep/index.html b/premiere/cep/index.html
new file mode 100644
index 00000000..fee98124
--- /dev/null
+++ b/premiere/cep/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ VidXP Search
+
+
+
+
+
+
+
diff --git a/premiere/cep/index.tsx b/premiere/cep/index.tsx
new file mode 100644
index 00000000..7b0e58ca
--- /dev/null
+++ b/premiere/cep/index.tsx
@@ -0,0 +1,19 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+
+import { createCepPremiereAdapter } from "../src/premiere/cep-adapter";
+import { createCepFetch } from "../src/services/vidxp/cep-fetch";
+import { App } from "../src/ui/App";
+import { installPremiereTheme } from "../src/ui/theme";
+
+Reflect.set(window, "__VIDXP_CEP__", true);
+installPremiereTheme(document, process.platform);
+
+const rootElement = document.getElementById("root");
+if (!rootElement) throw new Error("The VidXP panel root element was not found.");
+
+createRoot(rootElement).render(
+
+
+ ,
+);
diff --git a/premiere/cep/jsx/host.jsx b/premiere/cep/jsx/host.jsx
new file mode 100644
index 00000000..963d539e
--- /dev/null
+++ b/premiere/cep/jsx/host.jsx
@@ -0,0 +1,121 @@
+/* VidXP's Premiere 23.x bridge. Keep this file ES3-compatible for ExtendScript. */
+$._VIDXP = {
+ stringify: function (value) {
+ if (value === null) return "null";
+ var type = typeof value;
+ if (type === "string") {
+ return '"' + value
+ .replace(/\\/g, "\\\\")
+ .replace(/\"/g, '\\"')
+ .replace(/\r/g, "\\r")
+ .replace(/\n/g, "\\n")
+ .replace(/\t/g, "\\t") + '"';
+ }
+ if (type === "number" || type === "boolean") return String(value);
+ var entries = [];
+ var index;
+ if (value instanceof Array) {
+ for (index = 0; index < value.length; index += 1) {
+ entries.push($._VIDXP.stringify(value[index]));
+ }
+ return "[" + entries.join(",") + "]";
+ }
+ for (var key in value) {
+ if (value.hasOwnProperty(key)) {
+ entries.push($._VIDXP.stringify(key) + ":" + $._VIDXP.stringify(value[key]));
+ }
+ }
+ return "{" + entries.join(",") + "}";
+ },
+
+ result: function (operation) {
+ try {
+ return $._VIDXP.stringify({ ok: true, value: operation() });
+ } catch (error) {
+ return $._VIDXP.stringify({
+ ok: false,
+ error: error && error.message ? error.message : String(error)
+ });
+ }
+ },
+
+ readItem: function (item) {
+ if (item.type === ProjectItemType.BIN || item.type === ProjectItemType.ROOT) {
+ var children = [];
+ for (var childIndex = 0; childIndex < item.children.numItems; childIndex += 1) {
+ var child = $._VIDXP.readItem(item.children[childIndex]);
+ if (child !== null) children.push(child);
+ }
+ return {
+ kind: "bin",
+ id: item.nodeId,
+ name: item.name,
+ children: children
+ };
+ }
+ if (item.type !== ProjectItemType.CLIP && item.type !== ProjectItemType.FILE) return null;
+ if (item.isSequence()) return null;
+ if (item.isOffline()) {
+ return {
+ kind: "clip",
+ id: item.nodeId,
+ name: item.name,
+ availability: "offline",
+ detail: "Media is offline in Premiere."
+ };
+ }
+ var mediaPath = item.getMediaPath();
+ if (!mediaPath) {
+ return {
+ kind: "clip",
+ id: item.nodeId,
+ name: item.name,
+ availability: "unavailable",
+ detail: "Premiere did not return a file-backed media path."
+ };
+ }
+ return {
+ kind: "clip",
+ id: item.nodeId,
+ name: item.name,
+ nativePath: mediaPath,
+ availability: "ready"
+ };
+ },
+
+ getLibrary: function () {
+ return $._VIDXP.result(function () {
+ if (!app.project) return null;
+ var root = app.project.rootItem;
+ var items = [];
+ for (var index = 0; index < root.children.numItems; index += 1) {
+ var node = $._VIDXP.readItem(root.children[index]);
+ if (node !== null) items.push(node);
+ }
+ return {
+ projectName: app.project.name,
+ sequenceName: app.project.activeSequence ? app.project.activeSequence.name : null,
+ items: items
+ };
+ });
+ },
+
+ getSelection: function () {
+ return $._VIDXP.result(function () {
+ var ids = [];
+ var seen = {};
+ var viewIds = app.getProjectViewIDs();
+ for (var viewIndex = 0; viewIndex < viewIds.length; viewIndex += 1) {
+ var selected = app.getProjectViewSelection(viewIds[viewIndex]);
+ for (var itemIndex = 0; itemIndex < selected.length; itemIndex += 1) {
+ var id = selected[itemIndex].nodeId;
+ if (!seen[id]) {
+ seen[id] = true;
+ ids.push(id);
+ }
+ }
+ }
+ return ids;
+ });
+ }
+};
diff --git a/premiere/docs/MANUAL_TEST_CHECKLIST.md b/premiere/docs/MANUAL_TEST_CHECKLIST.md
new file mode 100644
index 00000000..c4d7de6b
--- /dev/null
+++ b/premiere/docs/MANUAL_TEST_CHECKLIST.md
@@ -0,0 +1,92 @@
+# Premiere Pro manual test checklist
+
+Automated tests do not exercise Premiere, CEP, UXP, Creative Cloud, media
+paths, a real VidXP process, or model inference. Complete this checklist on a
+clean Windows test machine with Premiere Pro 23.2 and Premiere Pro 25.6 or
+newer before claiming host support.
+
+## Package installation
+
+- Install a release VidXP Desktop build; do not clone the repository or enable
+ Adobe developer modes for this test.
+- Confirm Desktop detects Premiere 23.2 as CEP and Premiere 25.6+ as UXP.
+- Select **Install for Premiere** and confirm Adobe's installer accepts the
+ signed `.zxp` and `.ccx` packages.
+- Restart both Premiere versions. Confirm 23.2 shows one panel under **Window >
+ Extensions (Legacy)** and 25.6+ shows one panel under **Window > UXP
+ Plugins**.
+- Refresh Desktop and confirm both installed statuses are reported.
+- Remove both extensions from Desktop, restart Premiere, and confirm both
+ panels are absent. Reinstall before continuing.
+
+## Load and layout
+
+- Run every remaining item once in Premiere 23.2 and once in Premiere 25.6+.
+- Dock, float, resize, close, and reopen the panel in supported Premiere themes.
+- Confirm UXP's built-in Spectrum controls and CEP's native controls render
+ correctly and consistently with the active Premiere theme.
+- Confirm Tab, Space, and Enter work as expected and focus remains visible.
+- Confirm controlled values do not reset and actions fire only once after UDT
+ reloads and React Strict Mode remounts.
+- Confirm the native bearer-token password input and media-scope selector remain
+ usable on Windows and macOS.
+- Confirm scrolling, disclosure details, and the 320-pixel minimum layout remain
+ usable without CSS Grid.
+
+## Premiere media discovery
+
+- Open a project containing nested bins, online clips, offline clips, a
+ sequence, a subclip, a proxy, and duplicate project items for one source.
+- Confirm refresh shows the active project and sequence.
+- Confirm nested bins and file-backed media appear without sequences.
+- Confirm offline and pathless media cannot be selected.
+- Select clips and bins in the panel and confirm the count is deduplicated.
+- Select clips and bins in Premiere's Project panel, choose
+ **Use current selection**, and confirm the same media becomes selected.
+- Repeat with a Windows UNC path and, on macOS, a mounted-volume path.
+
+## VidXP connection
+
+- In Desktop, start local processing and the app integration service locally.
+- Copy Desktop's dynamic API address into the extension and connect.
+- Confirm CEP connects through its native loopback transport without adding a
+ `null` browser origin to VidXP's CORS allowlist.
+- Confirm the panel shows the capabilities and indexed media reported by that
+ runtime, including any capability added after this extension was built.
+- Stop the service and confirm the panel reports a safe, actionable error.
+- Try a wrong bearer token against shared mode and confirm the token is not
+ shown in the error or console.
+
+## Ingestion and status
+
+- Index one short real video and confirm registration, real model inference,
+ active-snapshot update, and the completion notice.
+- Confirm the indexing action disables while work is active and enables again
+ after success or failure.
+- Index more than ten selected clips and confirm the panel progresses through
+ multiple batches without duplicate media paths.
+- Include one missing, unsupported, or disallowed source and confirm the other
+ files can finish while the failed item is identified.
+- Confirm Premiere remains responsive and the panel reports meaningful progress
+ during a long index.
+- Close and reopen the panel during a job. Record the current POC behavior; job
+ recovery across panel reload is not yet implemented.
+
+## Search
+
+- Search all indexed media with each searchable capability individually and in
+ combination.
+- Search one selected VidXP media item and confirm every result belongs to it.
+- Confirm each result shows the correct source name, time range, capability
+ labels, rank, and score.
+- Search for no-match text, stop the worker, and use an unprepared capability;
+ confirm empty, unavailable, and model-remediation states are readable.
+
+## Safety and cleanup
+
+- Confirm no source video is copied into `premiere/`, CEP or UXP plugin data,
+ or host temporary storage.
+- Confirm no API token, source path, media file, index, build output, or local
+ UXP Developer Tool setting appears in `git status`.
+- Stop services started for the test and remove both packages from Desktop when
+ finished.
diff --git a/premiere/eslint.config.mjs b/premiere/eslint.config.mjs
new file mode 100644
index 00000000..cdf3439b
--- /dev/null
+++ b/premiere/eslint.config.mjs
@@ -0,0 +1,30 @@
+import eslint from "@eslint/js";
+import premierepro from "@adobe/eslint-plugin-premierepro";
+import reactHooks from "eslint-plugin-react-hooks";
+import { defineConfig, globalIgnores } from "eslint/config";
+import typescript from "typescript-eslint";
+
+export default defineConfig(
+ globalIgnores(["dist/**", "ccx/**", "coverage/**"]),
+ {
+ files: ["index.tsx", "*.config.ts", "src/**/*.{ts,tsx}", "tests/**/*.ts"],
+ extends: [
+ eslint.configs.recommended,
+ ...typescript.configs.recommendedTypeChecked,
+ premierepro.configs.recommendedTypeChecked,
+ reactHooks.configs.flat.recommended,
+ ],
+ languageOptions: {
+ parserOptions: {
+ projectService: true,
+ tsconfigRootDir: import.meta.dirname,
+ },
+ },
+ },
+ {
+ files: ["tests/**/*.ts"],
+ rules: {
+ "@typescript-eslint/no-explicit-any": "off",
+ },
+ },
+);
diff --git a/premiere/index.html b/premiere/index.html
new file mode 100644
index 00000000..f75beeb9
--- /dev/null
+++ b/premiere/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ VidXP Search
+
+
+
+
+
+
diff --git a/premiere/index.tsx b/premiere/index.tsx
new file mode 100644
index 00000000..ff447f60
--- /dev/null
+++ b/premiere/index.tsx
@@ -0,0 +1,42 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+
+import "./src/ui/styles.css";
+import { App } from "./src/ui/App";
+import { createPremiereAdapter } from "./src/premiere/adapter";
+import { installPremiereTheme } from "./src/ui/theme";
+
+// UXP supplies this module inside Premiere at runtime.
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const { entrypoints } = require("uxp") as typeof import("uxp");
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const os = require("os") as typeof import("os");
+
+installPremiereTheme(document, os.platform());
+
+const rootElement = document.getElementById("root");
+
+if (!rootElement) {
+ throw new Error("The VidXP panel root element was not found.");
+}
+
+createRoot(rootElement).render(
+
+
+ ,
+);
+
+entrypoints.setup({
+ panels: {
+ // @ts-expect-error Adobe currently declares panels as an array even though
+ // runtime panel entrypoints are keyed by their manifest ID.
+ vidxpSearch: {
+ show() {
+ // React owns the panel DOM for the plugin context lifetime.
+ },
+ hide() {
+ // Correctness does not depend on this host lifecycle hook.
+ },
+ },
+ },
+});
diff --git a/premiere/package-lock.json b/premiere/package-lock.json
new file mode 100644
index 00000000..2d73ca56
--- /dev/null
+++ b/premiere/package-lock.json
@@ -0,0 +1,6764 @@
+{
+ "name": "vidxp-premiere-extension",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "vidxp-premiere-extension",
+ "version": "0.1.0",
+ "dependencies": {
+ "react": "19.2.8",
+ "react-dom": "19.2.8"
+ },
+ "devDependencies": {
+ "@adobe/cc-ext-uxp-types": "7.3.1",
+ "@adobe/eslint-plugin-premierepro": "26.3.0",
+ "@adobe/premierepro": "26.3.0",
+ "@eslint/js": "9.39.2",
+ "@types/node": "24.13.3",
+ "@types/react": "19.2.18",
+ "@types/react-dom": "19.2.5",
+ "@vitejs/plugin-react": "6.1.1",
+ "concurrently": "10.0.5",
+ "cross-env": "10.1.0",
+ "eslint": "9.39.2",
+ "eslint-plugin-react-hooks": "7.0.1",
+ "rimraf": "6.1.3",
+ "typescript": "6.0.3",
+ "typescript-eslint": "8.65.0",
+ "vite": "8.2.2",
+ "vite-uxp-plugin": "1.3.8",
+ "vitest": "4.1.11",
+ "zxp-signer": "1.0.6"
+ },
+ "engines": {
+ "node": "^22.19.0 || ^24.15.0 || ^26.0.0",
+ "npm": ">=11.6.0"
+ }
+ },
+ "node_modules/@adobe/cc-ext-uxp-types": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@adobe/cc-ext-uxp-types/-/cc-ext-uxp-types-7.3.1.tgz",
+ "integrity": "sha512-HLWYoqvDXOmVr9d7l4/hY7h+Ae6IJC1Rm8uIqUK28E8nlT36A23YUsAfnamh24LZC/Xkq5jsYNORW7wX44QEKg==",
+ "dev": true
+ },
+ "node_modules/@adobe/eslint-plugin-premierepro": {
+ "version": "26.3.0",
+ "resolved": "https://registry.npmjs.org/@adobe/eslint-plugin-premierepro/-/eslint-plugin-premierepro-26.3.0.tgz",
+ "integrity": "sha512-3PhOO4aVkK8eXRDusCvCELJBSkD/WA8LoL634F4b2q9UBTKKS2ZJmsH0ooAmQwUaPRiK/w9NtwzxJkVPtkMmHw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.58.1"
+ },
+ "peerDependencies": {
+ "@adobe/premierepro": "~26.3.0",
+ "@typescript-eslint/parser": "^8.0.0",
+ "eslint": "^9.0.0",
+ "typescript": ">=5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/parser": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@adobe/premierepro": {
+ "version": "26.3.0",
+ "resolved": "https://registry.npmjs.org/@adobe/premierepro/-/premierepro-26.3.0.tgz",
+ "integrity": "sha512-J84zEX8R4L5EU5EVVs3AWkd4LRoXPKueo28jPNfwwDAo69TOSNsAblImtsAmJR4HGDDazsvHkMQE3JBJqIcB9Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+ "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@babel/types": "^7.29.8",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.8",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.8",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.8",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@clack/core": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.3.5.tgz",
+ "integrity": "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picocolors": "^1.0.0",
+ "sisteransi": "^1.0.5"
+ }
+ },
+ "node_modules/@clack/prompts": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.7.0.tgz",
+ "integrity": "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==",
+ "bundleDependencies": [
+ "is-unicode-supported"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@clack/core": "^0.3.3",
+ "is-unicode-supported": "*",
+ "picocolors": "^1.0.0",
+ "sisteransi": "^1.0.5"
+ }
+ },
+ "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
+ "version": "1.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@epic-web/invariant": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
+ "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
+ "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.3.0",
+ "minimatch": "^3.1.5",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.147.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz",
+ "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm-eabi": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz",
+ "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz",
+ "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz",
+ "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz",
+ "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz",
+ "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz",
+ "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz",
+ "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz",
+ "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz",
+ "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz",
+ "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz",
+ "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz",
+ "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz",
+ "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz",
+ "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz",
+ "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ts-morph/common": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.18.1.tgz",
+ "integrity": "sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "^3.2.12",
+ "minimatch": "^5.1.0",
+ "mkdirp": "^1.0.4",
+ "path-browserify": "^1.0.1"
+ }
+ },
+ "node_modules/@ts-morph/common/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ts-morph/common/node_modules/brace-expansion": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@ts-morph/common/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz",
+ "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
+ "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/prettier": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz",
+ "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==",
+ "deprecated": "This is a stub types definition. prettier provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier": "*"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.18",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
+ "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.5.tgz",
+ "integrity": "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/resolve-dir": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/@types/resolve-dir/-/resolve-dir-0.0.0.tgz",
+ "integrity": "sha512-OuK0+SZ5RMR1nlLlsmDrHZJV58aLwXu2ET4tKZEkQzizSCpIotDmUz3RDlTA3VYW3XH8xaa/CjXvG/YJkOgHfA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz",
+ "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/type-utils": "8.65.0",
+ "@typescript-eslint/utils": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.65.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
+ "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.65.0",
+ "@typescript-eslint/types": "^8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
+ "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
+ "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
+ "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.65.0",
+ "@typescript-eslint/tsconfig-utils": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
+ "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
+ "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz",
+ "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
+ "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.65.0",
+ "@typescript-eslint/types": "^8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
+ "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
+ "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
+ "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.65.0",
+ "@typescript-eslint/tsconfig-utils": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
+ "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.68.0.tgz",
+ "integrity": "sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.68.0",
+ "@typescript-eslint/types": "^8.68.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz",
+ "integrity": "sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.68.0",
+ "@typescript-eslint/visitor-keys": "8.68.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz",
+ "integrity": "sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz",
+ "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0",
+ "@typescript-eslint/utils": "8.65.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
+ "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.65.0",
+ "@typescript-eslint/types": "^8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
+ "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
+ "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
+ "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.65.0",
+ "@typescript-eslint/tsconfig-utils": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
+ "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
+ "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz",
+ "integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz",
+ "integrity": "sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.68.0",
+ "@typescript-eslint/tsconfig-utils": "8.68.0",
+ "@typescript-eslint/types": "8.68.0",
+ "@typescript-eslint/visitor-keys": "8.68.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.68.0.tgz",
+ "integrity": "sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.68.0",
+ "@typescript-eslint/types": "8.68.0",
+ "@typescript-eslint/typescript-estree": "8.68.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.68.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz",
+ "integrity": "sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.68.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz",
+ "integrity": "sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "oxc-transform-react": "^0.145.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "oxc-transform-react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz",
+ "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz",
+ "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.11",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz",
+ "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz",
+ "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.11",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz",
+ "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz",
+ "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz",
+ "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.11",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
+ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.5",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
+ "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.2",
+ "async": "^3.2.4",
+ "buffer-crc32": "^1.0.0",
+ "readable-stream": "^4.0.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.0.0",
+ "graceful-fs": "^4.2.0",
+ "is-stream": "^2.0.1",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/archiver-utils/node_modules/brace-expansion": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/archiver-utils/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/b4a": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+ "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/bare-events": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz",
+ "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-fs": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz",
+ "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.5.4",
+ "bare-path": "^3.0.0",
+ "bare-stream": "^2.6.4",
+ "bare-url": "^2.2.2",
+ "fast-fifo": "^1.3.2"
+ },
+ "engines": {
+ "bare": ">=1.28.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
+ "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-stream": {
+ "version": "2.13.4",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz",
+ "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.8.1",
+ "streamx": "^2.25.0",
+ "teex": "^1.0.1"
+ },
+ "peerDependencies": {
+ "bare-abort-controller": "*",
+ "bare-buffer": "*",
+ "bare-events": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ },
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-events": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-url": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.2.tgz",
+ "integrity": "sha512-L13PCJzKG8RGvx8V1/DdMi12ERhC3tprr7/8a94BxpmnRsFqxh5XZNdhtMxu5HPkRshYOOWRGY8lDP7ZhpG9Cg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.19",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz",
+ "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/code-block-writer": {
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz",
+ "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^6.0.0",
+ "is-stream": "^2.0.1",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concurrently": {
+ "version": "10.0.5",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.5.tgz",
+ "integrity": "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "5.6.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.9.0",
+ "supports-color": "10.2.2",
+ "tree-kill": "1.2.2",
+ "yargs": "18.0.0"
+ },
+ "bin": {
+ "conc": "dist/bin/index.js",
+ "concurrently": "dist/bin/index.js"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-env": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
+ "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@epic-web/invariant": "^1.0.0",
+ "cross-spawn": "^7.0.6"
+ },
+ "bin": {
+ "cross-env": "dist/bin/cross-env.js",
+ "cross-env-shell": "dist/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
+ "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.415",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz",
+ "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz",
+ "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/execa/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+ "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.2.tgz",
+ "integrity": "sha512-UpGiiODyCGprM8EPP6JodP6jC9Rws6TCuiDOD+nn0CJhR8guI3g/ozo4ugL0vJ+Yz1UtJuuRPqvQuybVOF1VQA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz",
+ "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/global-prefix/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse-passwd": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.difference": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+ "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.union": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+ "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/meta-bolt": {
+ "version": "0.0.17",
+ "resolved": "https://registry.npmjs.org/meta-bolt/-/meta-bolt-0.0.17.tgz",
+ "integrity": "sha512-GIiQbuHJ5wtHEy+D1RjNiIyB+Z1bjEYOsVYzAFBWowKxXf/aS9PnHfiM584Igv6s0rez2VxIODKIdc8L7M1sMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@clack/prompts": "^0.7.0",
+ "@types/prettier": "^3.0.0",
+ "@types/resolve-dir": "^0.0.0",
+ "archiver": "^7.0.1",
+ "cliui": "^8.0.1",
+ "commander": "^12.1.0",
+ "execa": "^5.1.1",
+ "fast-glob": "^3.3.2",
+ "fs-extra": "^10.0.1",
+ "jsonc-parser": "^3.2.1",
+ "picocolors": "^1.0.0",
+ "prettier": "^3.1.0",
+ "radash": "^11.0.0",
+ "resolve-dir": "^1.0.1",
+ "ts-morph": "^17.0.1",
+ "ts-node": "^10.7.0",
+ "typescript": "^4.9.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "create-meta-bolt": "dist/index.js"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/meta-bolt/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/yargs": {
+ "version": "17.7.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
+ "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/meta-bolt/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.8"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
+ "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz",
+ "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
+ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.26",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+ "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.17",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.9.6",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz",
+ "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/radash": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/radash/-/radash-11.0.0.tgz",
+ "integrity": "sha512-CRWxTFTDff0IELGJ/zz58yY4BDgyI14qSM5OLNKbCItJrff7m7dXbVF0kWYVCXQtPb3SXIVhXvAImH6eT7VLSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
+ "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
+ "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readdir-glob/node_modules/brace-expansion": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz",
+ "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "glob": "^13.0.3",
+ "package-json-from-dist": "^1.0.1"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz",
+ "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.147.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm-eabi": "1.2.6",
+ "@rolldown/binding-android-arm64": "1.2.6",
+ "@rolldown/binding-darwin-arm64": "1.2.6",
+ "@rolldown/binding-darwin-x64": "1.2.6",
+ "@rolldown/binding-freebsd-x64": "1.2.6",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.6",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.6",
+ "@rolldown/binding-linux-arm64-musl": "1.2.6",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.6",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.6",
+ "@rolldown/binding-linux-x64-gnu": "1.2.6",
+ "@rolldown/binding-linux-x64-musl": "1.2.6",
+ "@rolldown/binding-openharmony-arm64": "1.2.6",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.6",
+ "@rolldown/binding-win32-x64-msvc": "1.2.6"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shell-quote": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz",
+ "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
+ "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/streamx": {
+ "version": "2.28.1",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz",
+ "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz",
+ "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "bare-fs": "^4.5.5",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/teex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+ "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "streamx": "^2.12.5"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+ "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz",
+ "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz",
+ "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/ts-morph": {
+ "version": "17.0.1",
+ "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-17.0.1.tgz",
+ "integrity": "sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ts-morph/common": "~0.18.0",
+ "code-block-writer": "^11.0.3"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz",
+ "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.65.0",
+ "@typescript-eslint/parser": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0",
+ "@typescript-eslint/utils": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
+ "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.65.0",
+ "@typescript-eslint/types": "^8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
+ "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
+ "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
+ "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.65.0",
+ "@typescript-eslint/tsconfig-utils": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
+ "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
+ "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
+ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz",
+ "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.5",
+ "postcss": "^8.5.26",
+ "rolldown": "~1.2.4",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.4.0 || ^0.5.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-uxp-plugin": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/vite-uxp-plugin/-/vite-uxp-plugin-1.3.8.tgz",
+ "integrity": "sha512-lUxWk80n5VurOcluJBzDhoEcPFtbjvj9ZhfXDiUvC1bE6K3zc2CzWPN1i5IW0bazooArygoZj9p7l5QYH+gX3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver": "5.3.0",
+ "magic-string": "^0.30.21",
+ "meta-bolt": "^0.0.17",
+ "picocolors": "^1.1.1",
+ "typescript": "^5.2.2",
+ "ws": "^8.14.2"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/archiver": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
+ "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^2.1.0",
+ "async": "^3.2.0",
+ "buffer-crc32": "^0.2.1",
+ "readable-stream": "^3.6.0",
+ "readdir-glob": "^1.0.0",
+ "tar-stream": "^2.2.0",
+ "zip-stream": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/archiver-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+ "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/archiver-utils/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite-uxp-plugin/node_modules/brace-expansion": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/compress-commons": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
+ "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "^0.2.13",
+ "crc32-stream": "^4.0.2",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/crc32-stream": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
+ "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^3.4.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite-uxp-plugin/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/zip-stream": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
+ "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^3.0.4",
+ "compress-commons": "^4.1.2",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/vite-uxp-plugin/node_modules/zip-stream/node_modules/archiver-utils": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
+ "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.2.3",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz",
+ "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.11",
+ "@vitest/mocker": "4.1.11",
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/runner": "4.1.11",
+ "@vitest/snapshot": "4.1.11",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.11",
+ "@vitest/browser-preview": "4.1.11",
+ "@vitest/browser-webdriverio": "4.1.11",
+ "@vitest/coverage-istanbul": "4.1.11",
+ "@vitest/coverage-v8": "4.1.11",
+ "@vitest/ui": "4.1.11",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+ "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.0",
+ "compress-commons": "^6.0.2",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
+ "node_modules/zxp-signer": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/zxp-signer/-/zxp-signer-1.0.6.tgz",
+ "integrity": "sha512-4SxJds9qPdSvGhQ59R/+jD2yLYqkc3gLTOUrRS7CN0Sbb+H/VOZR2DWg5I7OBQCXU31n+WTOF5mGGEhmO7aLhA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ }
+ }
+}
diff --git a/premiere/package.json b/premiere/package.json
new file mode 100644
index 00000000..ef63df92
--- /dev/null
+++ b/premiere/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "vidxp-premiere-extension",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "packageManager": "npm@11.6.0",
+ "engines": {
+ "node": "^22.19.0 || ^24.15.0 || ^26.0.0",
+ "npm": ">=11.6.0"
+ },
+ "scripts": {
+ "clean": "rimraf dist ccx coverage",
+ "build": "npm run clean && npm run build:uxp && npm run build:cep",
+ "build:uxp": "cross-env BOLT_MODE=build vite build --mode uxp",
+ "build:cep": "vite build --mode cep && node scripts/prepare-cep.mjs",
+ "package:uxp": "cross-env BOLT_MODE=package vite build --mode uxp",
+ "package": "npm run clean && npm run package:uxp && npm run build:cep && node scripts/package-extensions.mjs",
+ "ccx-install": "cross-env BOLT_ACTION=ccx-install vite --mode uxp",
+ "ccx-uninstall": "cross-env BOLT_ACTION=ccx-uninstall vite --mode uxp",
+ "dev": "concurrently -k -n build,types \"cross-env BOLT_MODE=dev vite build --mode uxp --watch\" \"tsc --watch --noEmit --preserveWatchOutput\"",
+ "lint": "eslint . --max-warnings 0",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest",
+ "test:run": "vitest run",
+ "check": "npm run typecheck && npm run lint && npm run test:run && npm run build"
+ },
+ "dependencies": {
+ "react": "19.2.8",
+ "react-dom": "19.2.8"
+ },
+ "devDependencies": {
+ "@adobe/cc-ext-uxp-types": "7.3.1",
+ "@adobe/eslint-plugin-premierepro": "26.3.0",
+ "@adobe/premierepro": "26.3.0",
+ "@eslint/js": "9.39.2",
+ "@types/node": "24.13.3",
+ "@types/react": "19.2.18",
+ "@types/react-dom": "19.2.5",
+ "@vitejs/plugin-react": "6.1.1",
+ "concurrently": "10.0.5",
+ "cross-env": "10.1.0",
+ "eslint": "9.39.2",
+ "eslint-plugin-react-hooks": "7.0.1",
+ "rimraf": "6.1.3",
+ "typescript": "6.0.3",
+ "typescript-eslint": "8.65.0",
+ "vite": "8.2.2",
+ "vite-uxp-plugin": "1.3.8",
+ "vitest": "4.1.11",
+ "zxp-signer": "1.0.6"
+ }
+}
diff --git a/premiere/scripts/package-extensions.mjs b/premiere/scripts/package-extensions.mjs
new file mode 100644
index 00000000..19ac10be
--- /dev/null
+++ b/premiere/scripts/package-extensions.mjs
@@ -0,0 +1,55 @@
+import { copyFileSync, mkdirSync, readdirSync, rmSync } from "node:fs";
+import { createRequire } from "node:module";
+import { resolve } from "node:path";
+import { spawnSync } from "node:child_process";
+
+const require = createRequire(import.meta.url);
+const root = resolve(import.meta.dirname, "..");
+const packages = resolve(root, "packages");
+const uxpPackage = resolve(packages, "vidxp-premiere-uxp.ccx");
+const cepPackage = resolve(packages, "vidxp-premiere-cep.zxp");
+const certificate = resolve(packages, "vidxp-premiere-build-certificate.p12");
+
+mkdirSync(packages, { recursive: true });
+const generatedUxpPackages = readdirSync(resolve(root, "ccx"))
+ .filter((name) => name.endsWith(".ccx"));
+if (generatedUxpPackages.length !== 1) {
+ throw new Error(`Expected one Bolt UXP package, found ${generatedUxpPackages.length}.`);
+}
+copyFileSync(
+ resolve(root, "ccx", generatedUxpPackages[0]),
+ uxpPackage,
+);
+
+const signerRoot = resolve(require.resolve("zxp-signer/package.json"), "..");
+const signerPlatform = process.platform === "win32"
+ ? process.arch === "x64" ? "win64" : "Win32"
+ : "osx";
+const signer = resolve(signerRoot, "bin", "4.1.3", signerPlatform, `ZXPSignCmd${process.platform === "win32" ? ".exe" : ""}`);
+const certificatePassword = process.env.ZXP_CERT_PASSWORD || "vidxp-release-build";
+runSigner([
+ "-selfSignedCert",
+ process.env.ZXP_CERT_COUNTRY || "PK",
+ process.env.ZXP_CERT_PROVINCE || "Punjab",
+ process.env.ZXP_CERT_ORG || "Grayhat Developers PVT Ltd",
+ process.env.ZXP_CERT_NAME || "org.grayhat.vidxp-premiere.cep",
+ certificatePassword,
+ certificate,
+]);
+const result = runSigner([
+ "-sign",
+ resolve(root, "dist", "cep"),
+ cepPackage,
+ certificate,
+ certificatePassword,
+ "-tsa",
+ process.env.ZXP_TIMESTAMP || "http://timestamp.digicert.com/",
+], false);
+rmSync(certificate, { force: true });
+if (result.status !== 0) process.exit(result.status ?? 1);
+
+function runSigner(arguments_, exitOnFailure = true) {
+ const result = spawnSync(signer, arguments_, { stdio: "inherit" });
+ if (exitOnFailure && result.status !== 0) process.exit(result.status ?? 1);
+ return result;
+}
diff --git a/premiere/scripts/prepare-cep.mjs b/premiere/scripts/prepare-cep.mjs
new file mode 100644
index 00000000..a811404f
--- /dev/null
+++ b/premiere/scripts/prepare-cep.mjs
@@ -0,0 +1,12 @@
+import { cpSync, mkdirSync } from "node:fs";
+import { resolve } from "node:path";
+
+const root = resolve(import.meta.dirname, "..");
+const output = resolve(root, "dist", "cep");
+
+mkdirSync(resolve(output, "CSXS"), { recursive: true });
+mkdirSync(resolve(output, "jsx"), { recursive: true });
+cpSync(resolve(root, "cep", "index.html"), resolve(output, "index.html"));
+cpSync(resolve(root, "cep", "CSXS", "manifest.xml"), resolve(output, "CSXS", "manifest.xml"));
+cpSync(resolve(root, "cep", "jsx", "host.jsx"), resolve(output, "jsx", "host.jsx"));
+cpSync(resolve(root, "src", "ui", "styles.css"), resolve(output, "styles.css"));
diff --git a/premiere/src/premiere/adapter.ts b/premiere/src/premiere/adapter.ts
new file mode 100644
index 00000000..dd6a3422
--- /dev/null
+++ b/premiere/src/premiere/adapter.ts
@@ -0,0 +1,104 @@
+import type {
+ FolderItem,
+ premierepro,
+ ProjectItem,
+} from "@adobe/premierepro";
+
+import type {
+ PremiereAdapter,
+ PremiereClip,
+ PremiereLibrary,
+ PremiereMediaNode,
+} from "./types";
+
+// Premiere supplies this module at runtime and Vite leaves it external.
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const ppro = require("premierepro") as premierepro;
+
+export function createPremiereAdapter(): PremiereAdapter {
+ return {
+ async getLibrary(): Promise {
+ const project = await ppro.Project.getActiveProject();
+ if (!project) return undefined;
+
+ const [sequence, root] = await Promise.all([
+ project.getActiveSequence(),
+ project.getRootItem(),
+ ]);
+ const items = await readFolder(root);
+
+ return {
+ projectName: project.name,
+ sequenceName: sequence?.name,
+ items,
+ };
+ },
+
+ async getSelectedProjectItemIds(): Promise {
+ const project = await ppro.Project.getActiveProject();
+ if (!project) return [];
+ const selection = await ppro.ProjectUtils.getSelection(project);
+ const items = await selection.getItems();
+ return items.map((item) => item.getId());
+ },
+ };
+}
+
+async function readFolder(folder: FolderItem): Promise {
+ const children = await folder.getItems();
+ const nodes = await Promise.all(children.map(readProjectItem));
+ return nodes.filter((node): node is PremiereMediaNode => node !== undefined);
+}
+
+async function readProjectItem(
+ item: ProjectItem,
+): Promise {
+ const id = item.getId();
+ if (
+ item.type === ppro.ProjectItem.TYPE_BIN ||
+ item.type === ppro.ProjectItem.TYPE_ROOT
+ ) {
+ const folder = ppro.FolderItem.cast(item);
+ return {
+ kind: "bin",
+ id,
+ name: item.name,
+ children: await readFolder(folder),
+ };
+ }
+
+ try {
+ const clip = ppro.ClipProjectItem.cast(item);
+ if (await clip.isSequence()) return undefined;
+ if (await clip.isOffline()) {
+ return unavailableClip(id, item.name, "offline", "Media is offline in Premiere.");
+ }
+ const nativePath = (await clip.getMediaFilePath()).trim();
+ if (!nativePath) {
+ return unavailableClip(
+ id,
+ item.name,
+ "unavailable",
+ "Premiere did not return a file-backed media path.",
+ );
+ }
+ return {
+ kind: "clip",
+ id,
+ name: item.name,
+ nativePath,
+ availability: "ready",
+ };
+ } catch {
+ return undefined;
+ }
+}
+
+function unavailableClip(
+ id: string,
+ name: string,
+ availability: PremiereClip["availability"],
+ detail: string,
+): PremiereClip {
+ return { kind: "clip", id, name, availability, detail };
+}
diff --git a/premiere/src/premiere/cep-adapter.ts b/premiere/src/premiere/cep-adapter.ts
new file mode 100644
index 00000000..20073194
--- /dev/null
+++ b/premiere/src/premiere/cep-adapter.ts
@@ -0,0 +1,43 @@
+import type { PremiereAdapter, PremiereLibrary } from "./types";
+
+interface CepBridge {
+ evalScript(script: string, callback: (result: string) => void): void;
+}
+
+interface BridgeEnvelope {
+ ok: boolean;
+ value?: T;
+ error?: string;
+}
+
+export function createCepPremiereAdapter(): PremiereAdapter {
+ return {
+ getLibrary: () => evaluate("$._VIDXP.getLibrary()"),
+ getSelectedProjectItemIds: () =>
+ evaluate("$._VIDXP.getSelection()"),
+ };
+}
+
+function evaluate(script: string): Promise {
+ const bridge = Reflect.get(window, "__adobe_cep__") as CepBridge | undefined;
+ if (!bridge) return Promise.reject(new Error("Premiere's CEP bridge is unavailable."));
+
+ return new Promise((resolve, reject) => {
+ bridge.evalScript(script, (rawResult) => {
+ if (!rawResult || rawResult === "EvalScript error.") {
+ reject(new Error("Premiere could not run the VidXP host bridge."));
+ return;
+ }
+ try {
+ const envelope = JSON.parse(rawResult) as BridgeEnvelope;
+ if (!envelope.ok) {
+ reject(new Error(envelope.error || "Premiere returned an unknown error."));
+ return;
+ }
+ resolve(envelope.value as T);
+ } catch {
+ reject(new Error("Premiere returned an invalid VidXP host response."));
+ }
+ });
+ });
+}
diff --git a/premiere/src/premiere/library.ts b/premiere/src/premiere/library.ts
new file mode 100644
index 00000000..a55fdb3c
--- /dev/null
+++ b/premiere/src/premiere/library.ts
@@ -0,0 +1,69 @@
+import type {
+ PremiereBin,
+ PremiereClip,
+ PremiereMediaNode,
+} from "./types";
+
+export function collectSelectedClips(
+ items: PremiereMediaNode[],
+ selectedIds: ReadonlySet,
+): PremiereClip[] {
+ const clips = new Map();
+
+ function visit(node: PremiereMediaNode, ancestorSelected: boolean) {
+ const selected = ancestorSelected || selectedIds.has(node.id);
+ if (node.kind === "bin") {
+ node.children.forEach((child) => visit(child, selected));
+ return;
+ }
+ if (selected && node.availability === "ready" && node.nativePath) {
+ clips.set(node.nativePath, node);
+ }
+ }
+
+ items.forEach((item) => visit(item, false));
+ return [...clips.values()];
+}
+
+export function countReadyClips(items: PremiereMediaNode[]): number {
+ return items.reduce(
+ (total, item) =>
+ total +
+ (item.kind === "bin"
+ ? countReadyClips(item.children)
+ : item.availability === "ready"
+ ? 1
+ : 0),
+ 0,
+ );
+}
+
+export function filterLibrary(
+ items: PremiereMediaNode[],
+ rawQuery: string,
+): PremiereMediaNode[] {
+ const query = rawQuery.trim().toLocaleLowerCase();
+ if (!query) return items;
+
+ return items.flatMap((node): PremiereMediaNode[] => {
+ if (node.kind === "clip") {
+ const searchable = `${node.name} ${node.nativePath ?? ""}`.toLocaleLowerCase();
+ return searchable.includes(query) ? [node] : [];
+ }
+ const children = filterLibrary(node.children, query);
+ return node.name.toLocaleLowerCase().includes(query) || children.length > 0
+ ? [{ ...node, children } satisfies PremiereBin]
+ : [];
+ });
+}
+
+export function chunkPaths(paths: string[], maximum = 10): string[][] {
+ if (!Number.isInteger(maximum) || maximum < 1) {
+ throw new Error("The ingestion batch size must be a positive integer.");
+ }
+ const batches: string[][] = [];
+ for (let index = 0; index < paths.length; index += maximum) {
+ batches.push(paths.slice(index, index + maximum));
+ }
+ return batches;
+}
diff --git a/premiere/src/premiere/types.ts b/premiere/src/premiere/types.ts
new file mode 100644
index 00000000..8a9b3f21
--- /dev/null
+++ b/premiere/src/premiere/types.ts
@@ -0,0 +1,28 @@
+export interface PremiereLibrary {
+ projectName: string;
+ sequenceName?: string;
+ items: PremiereMediaNode[];
+}
+
+export type PremiereMediaNode = PremiereBin | PremiereClip;
+
+export interface PremiereBin {
+ kind: "bin";
+ id: string;
+ name: string;
+ children: PremiereMediaNode[];
+}
+
+export interface PremiereClip {
+ kind: "clip";
+ id: string;
+ name: string;
+ nativePath?: string;
+ availability: "ready" | "offline" | "unavailable";
+ detail?: string;
+}
+
+export interface PremiereAdapter {
+ getLibrary(): Promise;
+ getSelectedProjectItemIds(): Promise;
+}
diff --git a/premiere/src/services/vidxp/cep-fetch.ts b/premiere/src/services/vidxp/cep-fetch.ts
new file mode 100644
index 00000000..af9f2737
--- /dev/null
+++ b/premiere/src/services/vidxp/cep-fetch.ts
@@ -0,0 +1,75 @@
+import type { VidXPFetch } from "./client";
+
+interface CepNodeRuntime {
+ require(name: "http" | "https"): NodeHttpModule;
+}
+
+interface NodeHttpModule {
+ request(
+ url: string,
+ options: { headers: Record; method: string },
+ callback: (response: NodeResponse) => void,
+ ): NodeRequest;
+}
+
+interface NodeResponse {
+ headers: Record;
+ statusCode?: number;
+ statusMessage?: string;
+ on(event: "data", listener: (chunk: string) => void): void;
+ on(event: "end", listener: () => void): void;
+ setEncoding(encoding: "utf8"): void;
+}
+
+interface NodeRequest {
+ destroy(error?: Error): void;
+ end(body?: string): void;
+ on(event: "error", listener: (error: Error) => void): void;
+}
+
+export function createCepFetch(): VidXPFetch {
+ return (input, init = {}) => {
+ const requestInit = init as RequestInit;
+ const url = input instanceof Request ? input.url : String(input);
+ const runtime = Reflect.get(window, "cep_node") as CepNodeRuntime | undefined;
+ if (!runtime) return Promise.reject(new Error("Premiere's CEP network runtime is unavailable."));
+ const protocol = new URL(url).protocol;
+ const transport = runtime.require(protocol === "https:" ? "https" : "http");
+ const headers = Object.fromEntries(new Headers(requestInit.headers).entries());
+
+ return new Promise((resolve, reject) => {
+ const request = transport.request(
+ url,
+ { headers, method: requestInit.method || "GET" },
+ (response) => {
+ response.setEncoding("utf8");
+ let body = "";
+ response.on("data", (chunk) => {
+ body += chunk;
+ });
+ response.on("end", () => {
+ const responseHeaders = new Headers();
+ for (const [name, value] of Object.entries(response.headers)) {
+ if (Array.isArray(value)) value.forEach((item) => responseHeaders.append(name, item));
+ else if (value !== undefined) responseHeaders.set(name, value);
+ }
+ resolve(new Response(body, {
+ headers: responseHeaders,
+ status: response.statusCode || 500,
+ statusText: response.statusMessage,
+ }));
+ });
+ },
+ );
+ request.on("error", reject);
+ requestInit.signal?.addEventListener("abort", () => request.destroy(abortError()), { once: true });
+ request.end(typeof requestInit.body === "string" ? requestInit.body : undefined);
+ });
+ };
+}
+
+function abortError(): Error {
+ const error = new Error("Operation cancelled");
+ error.name = "AbortError";
+ return error;
+}
diff --git a/premiere/src/services/vidxp/client.ts b/premiere/src/services/vidxp/client.ts
new file mode 100644
index 00000000..bbcdc454
--- /dev/null
+++ b/premiere/src/services/vidxp/client.ts
@@ -0,0 +1,226 @@
+import type {
+ CapabilitySummary,
+ MediaIngestionStatus,
+ VidXPJob,
+ WorkspaceOverview,
+} from "./types";
+
+export type VidXPFetch = typeof fetch;
+type Sleep = (milliseconds: number) => Promise;
+
+export interface VidXPClientOptions {
+ baseUrl: string;
+ bearerToken?: string;
+ fetchImpl?: VidXPFetch;
+ sleep?: Sleep;
+}
+
+export interface SearchRequest {
+ query: string;
+ modalities: string[];
+ mediaId?: string;
+ topK?: number;
+}
+
+export class VidXPApiError extends Error {
+ constructor(
+ message: string,
+ readonly status: number,
+ readonly code?: string,
+ ) {
+ super(message);
+ this.name = "VidXPApiError";
+ }
+}
+
+export class VidXPClient {
+ private readonly baseUrl: string;
+ private readonly bearerToken?: string;
+ private readonly fetchImpl: VidXPFetch;
+ private readonly sleep: Sleep;
+
+ constructor(options: VidXPClientOptions) {
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
+ this.bearerToken = options.bearerToken?.trim() || undefined;
+ this.fetchImpl = options.fetchImpl ?? fetch;
+ this.sleep = options.sleep ?? delay;
+ }
+
+ async health(): Promise {
+ await this.request<{ status: string }>("/health");
+ }
+
+ async listCapabilities(): Promise {
+ const response = await this.request<{ items: CapabilitySummary[] }>(
+ "/api/v1/capabilities",
+ );
+ return response.items;
+ }
+
+ workspace(): Promise {
+ return this.request("/api/v1/workspace?page_size=100");
+ }
+
+ createLocalIngestion(
+ paths: string[],
+ modalities: string[],
+ idempotencyKey: string,
+ ): Promise {
+ return this.request(
+ "/api/v1/media/local-ingestions",
+ {
+ method: "POST",
+ headers: { "Idempotency-Key": idempotencyKey },
+ body: JSON.stringify({
+ paths,
+ modalities,
+ index_after_import: true,
+ }),
+ },
+ );
+ }
+
+ getLocalIngestion(ingestionId: string): Promise {
+ return this.request(
+ `/api/v1/media/local-ingestions/${encodeURIComponent(ingestionId)}`,
+ );
+ }
+
+ async waitForLocalIngestion(
+ initial: MediaIngestionStatus,
+ onProgress: (status: MediaIngestionStatus) => void,
+ signal?: AbortSignal,
+ ): Promise {
+ let status = initial;
+ while (!status.terminal) {
+ onProgress(status);
+ await this.wait(status.poll_after_seconds, signal);
+ status = await this.getLocalIngestion(status.session_id);
+ }
+ onProgress(status);
+ return status;
+ }
+
+ submitSearch(request: SearchRequest, idempotencyKey: string): Promise {
+ const query = request.query.trim();
+ if (!query) throw new Error("Enter something to search for.");
+ return this.request("/api/v1/jobs/search", {
+ method: "POST",
+ headers: { "Idempotency-Key": idempotencyKey },
+ body: JSON.stringify({
+ query,
+ modalities: request.modalities,
+ media_id: request.mediaId || null,
+ top_k: request.topK ?? 20,
+ }),
+ });
+ }
+
+ getJob(jobId: string): Promise {
+ return this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}`);
+ }
+
+ async waitForJob(
+ initial: VidXPJob,
+ onProgress: (job: VidXPJob) => void,
+ signal?: AbortSignal,
+ ): Promise {
+ let job = initial;
+ while (!job.terminal) {
+ onProgress(job);
+ await this.wait(job.poll_after_seconds, signal);
+ job = await this.getJob(job.job_id);
+ }
+ onProgress(job);
+ if (job.state !== "succeeded") {
+ throw new VidXPApiError(
+ job.error?.message ?? "The VidXP job did not complete.",
+ 409,
+ job.error?.code,
+ );
+ }
+ return job;
+ }
+
+ private async wait(seconds: number, signal?: AbortSignal): Promise {
+ if (signal?.aborted) throw abortError();
+ await this.sleep(Math.max(250, Math.min(10_000, seconds * 1000)));
+ if (signal?.aborted) throw abortError();
+ }
+
+ private async request(path: string, init: RequestInit = {}): Promise {
+ const headers = new Headers(init.headers);
+ headers.set("Accept", "application/json");
+ if (init.body !== undefined) headers.set("Content-Type", "application/json");
+ if (this.bearerToken) {
+ headers.set("Authorization", `Bearer ${this.bearerToken}`);
+ }
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
+ ...init,
+ headers,
+ });
+ if (!response.ok) throw await apiError(response);
+ return (await response.json()) as T;
+ }
+}
+
+export function createIdempotencyKey(): string {
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
+ return crypto.randomUUID();
+ }
+ const timestamp = Date.now().toString(16).padStart(16, "0");
+ const random = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
+ .toString(16)
+ .padStart(16, "0");
+ return `${timestamp}${random}`;
+}
+
+function normalizeBaseUrl(value: string): string {
+ const candidate = value.trim();
+ if (!candidate) throw new Error("Enter the VidXP API address shown by Desktop.");
+ let parsed: URL;
+ try {
+ parsed = new URL(candidate);
+ } catch {
+ throw new Error("Enter a complete VidXP API address, including http:// or https://.");
+ }
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
+ throw new Error("The VidXP API address must use HTTP or HTTPS.");
+ }
+ parsed.pathname = "";
+ parsed.search = "";
+ parsed.hash = "";
+ return parsed.toString().replace(/\/$/, "");
+}
+
+async function apiError(response: Response): Promise {
+ let message = `VidXP returned HTTP ${response.status}.`;
+ let code: string | undefined;
+ try {
+ const payload = (await response.json()) as {
+ error?: {
+ code?: string;
+ message?: string;
+ details?: { remediation?: string };
+ };
+ };
+ if (payload.error?.message) message = payload.error.message;
+ if (payload.error?.details?.remediation) {
+ message += ` ${payload.error.details.remediation}`;
+ }
+ code = payload.error?.code;
+ } catch {
+ // Keep the bounded status-only fallback; response bodies may contain internals.
+ }
+ return new VidXPApiError(message, response.status, code);
+}
+
+function delay(milliseconds: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
+}
+
+function abortError(): Error {
+ const error = new Error("Operation cancelled");
+ error.name = "AbortError";
+ return error;
+}
diff --git a/premiere/src/services/vidxp/types.ts b/premiere/src/services/vidxp/types.ts
new file mode 100644
index 00000000..e85a39a2
--- /dev/null
+++ b/premiere/src/services/vidxp/types.ts
@@ -0,0 +1,112 @@
+export interface CapabilitySummary {
+ name: string;
+ description: string;
+ supports_indexing: boolean;
+ prepares_models: boolean;
+ roles: string[];
+}
+
+export interface WorkspaceMedia {
+ media_id: string;
+ original_filename: string;
+ duration_seconds?: number;
+ state: string;
+ in_active_snapshot: boolean;
+}
+
+export interface WorkspaceOverview {
+ capabilities: CapabilitySummary[];
+ media: WorkspaceMedia[];
+ media_total: number;
+ next_cursor?: string;
+ index: {
+ state: string;
+ stage: string;
+ message: string;
+ summary?: {
+ media_count: number;
+ modalities: string[];
+ };
+ };
+ next_actions: string[];
+}
+
+export interface ErrorDetail {
+ code: string;
+ message: string;
+ retryable?: boolean;
+ details?: {
+ remediation?: string;
+ };
+}
+
+export interface MediaUploadStatus {
+ intent_id: string;
+ original_filename: string;
+ phase: string;
+ media_id?: string;
+ searchable: boolean;
+ terminal: boolean;
+ status: string;
+ error?: ErrorDetail;
+}
+
+export interface MediaIngestionStatus {
+ session_id: string;
+ aggregate_state: string;
+ index_modalities: string[];
+ file_count: number;
+ searchable_file_count: number;
+ failed_file_count: number;
+ index_failed_file_count: number;
+ items: MediaUploadStatus[];
+ terminal: boolean;
+ poll_after_seconds: number;
+ status: string;
+ next_action: string;
+}
+
+export interface JobProgress {
+ stage: string;
+ message: string;
+ current?: number;
+ total?: number;
+}
+
+export interface SearchHit {
+ modality: string;
+ score: number;
+ metadata: Record;
+}
+
+export interface FusedMoment {
+ moment_id?: string;
+ rank: number;
+ score: number;
+ media_id: string;
+ start: number;
+ end: number;
+ modalities: string[];
+ hits: SearchHit[];
+}
+
+export interface FusedSearchResult {
+ query_id: string;
+ query: string;
+ modalities: string[];
+ moments: FusedMoment[];
+}
+
+export interface VidXPJob {
+ job_id: string;
+ kind: string;
+ state: string;
+ progress?: JobProgress;
+ result?: {
+ kind: string;
+ result: FusedSearchResult;
+ };
+ error?: ErrorDetail;
+ terminal: boolean;
+ poll_after_seconds: number;
+}
diff --git a/premiere/src/ui/App.tsx b/premiere/src/ui/App.tsx
new file mode 100644
index 00000000..27fe6e64
--- /dev/null
+++ b/premiere/src/ui/App.tsx
@@ -0,0 +1,650 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+
+import {
+ chunkPaths,
+ collectSelectedClips,
+ countReadyClips,
+ filterLibrary,
+} from "../premiere/library";
+import type {
+ PremiereAdapter,
+ PremiereLibrary,
+ PremiereMediaNode,
+} from "../premiere/types";
+import {
+ createIdempotencyKey,
+ VidXPClient,
+ type VidXPFetch,
+} from "../services/vidxp/client";
+import type {
+ CapabilitySummary,
+ FusedMoment,
+ WorkspaceOverview,
+} from "../services/vidxp/types";
+import {
+ SpectrumActionButton,
+ SpectrumButton,
+ SpectrumCheckbox,
+ SpectrumTextArea,
+ SpectrumTextField,
+} from "./components/Spectrum";
+
+type ConnectionState =
+ | { status: "disconnected" }
+ | { status: "connecting" }
+ | { status: "ready" }
+ | { status: "error"; message: string };
+
+type OperationState =
+ | { status: "idle" }
+ | { status: "indexing" | "searching"; message: string }
+ | { status: "error"; message: string };
+
+interface Notice {
+ tone: "success" | "warning";
+ title: string;
+ message: string;
+}
+
+const DEFAULT_API_ADDRESS = "http://127.0.0.1:32191";
+
+interface AppProps {
+ fetchImpl?: VidXPFetch;
+ premiere: PremiereAdapter;
+}
+
+export function App({ fetchImpl, premiere }: AppProps) {
+ const abortController = useRef(undefined);
+ const [apiAddress, setApiAddress] = useState(DEFAULT_API_ADDRESS);
+ const [bearerToken, setBearerToken] = useState("");
+ const [client, setClient] = useState();
+ const [connection, setConnection] = useState({
+ status: "disconnected",
+ });
+ const [library, setLibrary] = useState();
+ const [libraryError, setLibraryError] = useState();
+ const [libraryFilter, setLibraryFilter] = useState("");
+ const [selectedIds, setSelectedIds] = useState([]);
+ const [capabilities, setCapabilities] = useState([]);
+ const [indexModalities, setIndexModalities] = useState([]);
+ const [searchModalities, setSearchModalities] = useState([]);
+ const [workspace, setWorkspace] = useState();
+ const [operation, setOperation] = useState({ status: "idle" });
+ const [notice, setNotice] = useState();
+ const [query, setQuery] = useState("");
+ const [mediaScope, setMediaScope] = useState("");
+ const [moments, setMoments] = useState([]);
+
+ const loadLibrary = useCallback(async () => {
+ setLibraryError(undefined);
+ try {
+ const nextLibrary = await premiere.getLibrary();
+ setLibrary(nextLibrary);
+ if (!nextLibrary) setSelectedIds([]);
+ } catch (error) {
+ setLibrary(undefined);
+ setLibraryError(messageOf(error));
+ }
+ }, [premiere]);
+
+ useEffect(() => {
+ void loadLibrary();
+ return () => abortController.current?.abort();
+ }, [loadLibrary]);
+
+ const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);
+ const selectedClips = useMemo(
+ () => collectSelectedClips(library?.items ?? [], selectedSet),
+ [library, selectedSet],
+ );
+ const visibleLibrary = useMemo(
+ () => filterLibrary(library?.items ?? [], libraryFilter),
+ [library, libraryFilter],
+ );
+ const indexableCapabilities = capabilities.filter(
+ (capability) => capability.supports_indexing,
+ );
+ const searchableCapabilities = capabilities.filter((capability) =>
+ capability.roles.includes("searchable"),
+ );
+ const busy = operation.status === "indexing" || operation.status === "searching";
+
+ async function connect() {
+ setConnection({ status: "connecting" });
+ setNotice(undefined);
+ try {
+ const nextClient = new VidXPClient({
+ baseUrl: apiAddress,
+ bearerToken,
+ fetchImpl,
+ });
+ const [, nextCapabilities, nextWorkspace] = await Promise.all([
+ nextClient.health(),
+ nextClient.listCapabilities(),
+ nextClient.workspace(),
+ ]);
+ setClient(nextClient);
+ setCapabilities(nextCapabilities);
+ setIndexModalities(
+ nextCapabilities
+ .filter((capability) => capability.supports_indexing)
+ .map((capability) => capability.name),
+ );
+ setSearchModalities(
+ nextCapabilities
+ .filter((capability) => capability.roles.includes("searchable"))
+ .map((capability) => capability.name),
+ );
+ setWorkspace(nextWorkspace);
+ setConnection({ status: "ready" });
+ } catch (error) {
+ setClient(undefined);
+ setConnection({ status: "error", message: messageOf(error) });
+ }
+ }
+
+ async function selectCurrentPremiereItems() {
+ try {
+ setSelectedIds(await premiere.getSelectedProjectItemIds());
+ } catch (error) {
+ setLibraryError(messageOf(error));
+ }
+ }
+
+ async function indexSelection() {
+ if (!client || selectedClips.length === 0 || indexModalities.length === 0) return;
+ const controller = beginOperation();
+ setNotice(undefined);
+ const batches = chunkPaths(selectedClips.map((clip) => clip.nativePath!));
+ let searchable = 0;
+ const failures: string[] = [];
+
+ try {
+ for (const [batchIndex, paths] of batches.entries()) {
+ const initial = await client.createLocalIngestion(
+ paths,
+ indexModalities,
+ createIdempotencyKey(),
+ );
+ const final = await client.waitForLocalIngestion(
+ initial,
+ (status) => {
+ setOperation({
+ status: "indexing",
+ message: `Batch ${batchIndex + 1} of ${batches.length} · ${status.status}`,
+ });
+ },
+ controller.signal,
+ );
+ searchable += final.searchable_file_count;
+ failures.push(
+ ...final.items
+ .filter((item) => item.error)
+ .map(
+ (item) =>
+ `${item.original_filename}: ${item.error?.message ?? "Indexing failed."}`,
+ ),
+ );
+ }
+ setWorkspace(await client.workspace());
+ setNotice({
+ tone: failures.length > 0 ? "warning" : "success",
+ title: failures.length > 0 ? "Indexing finished with issues" : "Indexing complete",
+ message:
+ failures.length > 0
+ ? `${searchable} media item(s) are searchable. ${failures.join(" ")}`
+ : `${searchable} media item(s) from Premiere are now searchable.`,
+ });
+ setOperation({ status: "idle" });
+ } catch (error) {
+ if (!controller.signal.aborted) {
+ setOperation({ status: "error", message: messageOf(error) });
+ }
+ }
+ }
+
+ async function search() {
+ if (!client || !query.trim()) return;
+ const controller = beginOperation();
+ setNotice(undefined);
+ try {
+ const initial = await client.submitSearch(
+ {
+ query,
+ modalities: searchModalities,
+ mediaId: mediaScope || undefined,
+ topK: 20,
+ },
+ createIdempotencyKey(),
+ );
+ const completed = await client.waitForJob(
+ initial,
+ (job) => {
+ setOperation({
+ status: "searching",
+ message: job.progress?.message ?? "VidXP is searching indexed media…",
+ });
+ },
+ controller.signal,
+ );
+ const result = completed.result?.result;
+ if (!result) throw new Error("VidXP completed the search without a result payload.");
+ setMoments(result.moments);
+ if (result.moments.length === 0) {
+ setNotice({
+ tone: "warning",
+ title: "No matching moments",
+ message: "Try a broader description, another search feature, or the complete indexed library.",
+ });
+ }
+ setOperation({ status: "idle" });
+ } catch (error) {
+ if (!controller.signal.aborted) {
+ setMoments([]);
+ setOperation({ status: "error", message: messageOf(error) });
+ }
+ }
+ }
+
+ function beginOperation(): AbortController {
+ abortController.current?.abort();
+ const controller = new AbortController();
+ abortController.current = controller;
+ return controller;
+ }
+
+ function toggleSelected(id: string) {
+ setSelectedIds((current) =>
+ current.includes(id)
+ ? current.filter((candidate) => candidate !== id)
+ : [...current, id],
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
VidXP connection
+
Use the API address shown by VidXP Desktop’s app integration service.
+
+
+
+
+
+ Shared-server authentication
+
+ Bearer token
+ {/* UXP's Spectrum password field is unreadable on macOS. */}
+ setBearerToken(event.currentTarget.value)}
+ />
+
+ The token stays in panel memory and is not written to the Premiere project.
+
+ void connect()}
+ disabled={busy}
+ >
+ {connection.status === "connecting" ? "Connecting…" : "Connect"}
+
+ {connection.status === "error" && (
+ {connection.message}
+ )}
+
+
+
+
+
+
Premiere media
+
+ {library
+ ? `${library.projectName}${library.sequenceName ? ` · ${library.sequenceName}` : ""}`
+ : "Open a Premiere project to browse its bins."}
+
+
+
void loadLibrary()}
+ >
+ Refresh
+
+
+ {libraryError && {libraryError}
}
+ {library && (
+ <>
+
+
+ void selectCurrentPremiereItems()}
+ >
+ Use current selection
+
+
+
+ {visibleLibrary.length > 0 ? (
+ visibleLibrary.map((node) => (
+
+ ))
+ ) : (
+
No matching media.
+ )}
+
+
+ {selectedClips.length} selected · {countReadyClips(library.items)} file-backed media items available
+
+ >
+ )}
+
+
+
+
+
+
Index selected media
+
VidXP expands selected bins, deduplicates clips, and indexes in batches of ten.
+
+
+ setIndexModalities(next)}
+ emptyMessage="Connect to VidXP to discover indexable features."
+ />
+ void indexSelection()}
+ >
+ {operation.status === "indexing"
+ ? "Indexing…"
+ : `Index ${selectedClips.length || "selected"} media item${selectedClips.length === 1 ? "" : "s"}`}
+
+
+
+
+
+
+
Search moments
+
Search one indexed video or the complete active VidXP library.
+
+
+
+
+ Scope
+ setMediaScope(event.currentTarget.value)}>
+ All indexed media
+ {workspace?.media
+ .filter((media) => media.in_active_snapshot)
+ .map((media) => (
+
+ {media.original_filename}
+
+ ))}
+
+
+ setSearchModalities(next)}
+ emptyMessage="No searchable features are available yet."
+ />
+ void search()}
+ >
+ {operation.status === "searching" ? "Searching…" : "Search indexed media"}
+
+
+
+ {operation.status !== "idle" && (
+
+
+
+
{operation.status === "error" ? "Operation failed" : "VidXP is working"}
+
{operation.message}
+
+
+ )}
+
+ {notice && (
+
+
+
{notice.title}
+
{notice.message}
+
+ setNotice(undefined)}
+ >
+ ×
+
+
+ )}
+
+
+
+ );
+}
+
+function ConnectionBadge({ state }: { state: ConnectionState }) {
+ const label =
+ state.status === "ready"
+ ? "Connected"
+ : state.status === "connecting"
+ ? "Checking"
+ : state.status === "error"
+ ? "Unavailable"
+ : "Not connected";
+ return {label} ;
+}
+
+function MediaTreeNode({
+ node,
+ selected,
+ inherited,
+ onToggle,
+}: {
+ node: PremiereMediaNode;
+ selected: ReadonlySet;
+ inherited: boolean;
+ onToggle: (id: string) => void;
+}) {
+ const effectiveSelected = inherited || selected.has(node.id);
+ if (node.kind === "clip") {
+ const disabled = node.availability !== "ready";
+ return (
+
+ onToggle(node.id)}
+ >
+ ▰
+ {node.name}
+ {disabled && {node.availability} }
+
+ );
+ }
+ return (
+
+
+ event.stopPropagation()}
+ onCheckedChange={() => onToggle(node.id)}
+ >
+ ▸
+ {node.name}
+ {countReadyClips(node.children)}
+
+
+ {node.children.map((child) => (
+
+ ))}
+
+
+ );
+}
+
+function CapabilityChoices({
+ capabilities,
+ selected,
+ onChange,
+ emptyMessage,
+}: {
+ capabilities: CapabilitySummary[];
+ selected: string[];
+ onChange: (next: string[]) => void;
+ emptyMessage: string;
+}) {
+ if (capabilities.length === 0) return {emptyMessage}
;
+ return (
+
+ {capabilities.map((capability) => (
+
+
+ onChange(
+ selected.includes(capability.name)
+ ? selected.filter((name) => name !== capability.name)
+ : [...selected, capability.name],
+ )
+ }
+ >
+ {capability.name}
+
+
+ {capability.description}
+
+
+ ))}
+
+ );
+}
+
+function SearchResults({
+ moments,
+ workspace,
+}: {
+ moments: FusedMoment[];
+ workspace?: WorkspaceOverview;
+}) {
+ if (moments.length === 0) return null;
+ const names = new Map(
+ workspace?.media.map((media) => [media.media_id, media.original_filename]) ?? [],
+ );
+ return (
+
+
+
+
Matching moments
+
{moments.length} ranked result{moments.length === 1 ? "" : "s"}
+
+
+
+ {moments.map((moment) => (
+
+ {moment.rank}
+
+
{names.get(moment.media_id) ?? moment.media_id}
+
{formatTime(moment.start)} – {formatTime(moment.end)}
+
+ {moment.modalities.map((modality) => {modality} )}
+
+
+ {moment.score.toFixed(3)}
+
+ ))}
+
+
+ );
+}
+
+function formatTime(seconds: number): string {
+ const whole = Math.max(0, Math.floor(seconds));
+ const hours = Math.floor(whole / 3600);
+ const minutes = Math.floor((whole % 3600) / 60);
+ const remainder = whole % 60;
+ return hours > 0
+ ? `${hours}:${minutes.toString().padStart(2, "0")}:${remainder.toString().padStart(2, "0")}`
+ : `${minutes}:${remainder.toString().padStart(2, "0")}`;
+}
+
+function messageOf(error: unknown): string {
+ return error instanceof Error ? error.message : String(error);
+}
diff --git a/premiere/src/ui/components/Spectrum.tsx b/premiere/src/ui/components/Spectrum.tsx
new file mode 100644
index 00000000..0b16686b
--- /dev/null
+++ b/premiere/src/ui/components/Spectrum.tsx
@@ -0,0 +1,334 @@
+import {
+ useEffect,
+ useRef,
+ type ReactNode,
+ type RefObject,
+} from "react";
+
+type ButtonVariant =
+ | "cta"
+ | "primary"
+ | "secondary"
+ | "warning"
+ | "overBackground";
+
+interface CommonControlProps {
+ ariaLabel?: string;
+ className?: string;
+ disabled?: boolean;
+}
+
+interface SpectrumButtonProps extends CommonControlProps {
+ children: ReactNode;
+ onPress?: (event: Event) => void;
+ quiet?: boolean;
+ variant?: ButtonVariant;
+}
+
+interface SpectrumActionButtonProps extends CommonControlProps {
+ children: ReactNode;
+ onPress?: (event: Event) => void;
+ quiet?: boolean;
+ selected?: boolean;
+}
+
+interface SpectrumTextFieldProps extends CommonControlProps {
+ label?: string;
+ onValueChange: (value: string) => void;
+ placeholder?: string;
+ quiet?: boolean;
+ type?: "text" | "number" | "search";
+ value: string;
+}
+
+interface SpectrumTextAreaProps extends CommonControlProps {
+ label?: string;
+ onValueChange: (value: string) => void;
+ placeholder?: string;
+ quiet?: boolean;
+ value: string;
+}
+
+interface SpectrumCheckboxProps extends CommonControlProps {
+ checked: boolean;
+ children?: ReactNode;
+ indeterminate?: boolean;
+ onCheckedChange: (checked: boolean) => void;
+ onPress?: (event: Event) => void;
+}
+
+export function SpectrumButton(props: SpectrumButtonProps) {
+ if (isCepRuntime()) return ;
+ return ;
+}
+
+export function SpectrumActionButton(props: SpectrumActionButtonProps) {
+ if (isCepRuntime()) return ;
+ return ;
+}
+
+function NativeButton({
+ ariaLabel,
+ children,
+ className,
+ disabled = false,
+ onPress,
+ quiet = false,
+ selected = false,
+}: SpectrumActionButtonProps) {
+ return (
+ onPress?.(event.nativeEvent)}
+ type="button"
+ >
+ {children}
+
+ );
+}
+
+function SpectrumButtonElement({
+ tag,
+ ariaLabel,
+ children,
+ className,
+ disabled = false,
+ onPress,
+ quiet = false,
+ selected = false,
+ variant,
+}: SpectrumButtonProps & {
+ selected?: boolean;
+ tag: "sp-action-button" | "sp-button";
+}) {
+ const ref = useRef(null);
+ useUxpProperty(ref, "disabled", disabled);
+ useUxpProperty(ref, "quiet", quiet);
+ useUxpProperty(ref, "selected", selected);
+ useUxpEvent(ref, "click", onPress);
+
+ return tag === "sp-button" ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ );
+}
+
+export function SpectrumTextField(props: SpectrumTextFieldProps) {
+ return isCepRuntime() ? : ;
+}
+
+function NativeTextField({
+ ariaLabel,
+ className,
+ disabled = false,
+ label,
+ onValueChange,
+ placeholder,
+ type = "text",
+ value,
+}: SpectrumTextFieldProps) {
+ return (
+
+ {label ? {label} : undefined}
+ onValueChange(event.currentTarget.value)}
+ placeholder={placeholder} type={type} value={value} />
+
+ );
+}
+
+function UxpTextField({
+ ariaLabel, className, disabled = false, label, onValueChange, placeholder,
+ quiet = false, type = "text", value,
+}: SpectrumTextFieldProps) {
+ const ref = useRef(null);
+ useUxpProperty(ref, "disabled", disabled);
+ useUxpProperty(ref, "quiet", quiet);
+ useUxpProperty(ref, "value", value);
+ useUxpEvent(ref, "input", (event) => {
+ onValueChange(readEventProperty(event, "value", ""));
+ });
+
+ return (
+
+ {label ? {label} : undefined}
+
+ );
+}
+
+export function SpectrumTextArea(props: SpectrumTextAreaProps) {
+ return isCepRuntime() ? : ;
+}
+
+function NativeTextArea({
+ ariaLabel,
+ className,
+ disabled = false,
+ label,
+ onValueChange,
+ placeholder,
+ quiet = false,
+ value,
+}: SpectrumTextAreaProps) {
+ void quiet;
+ return (
+
+ {label ? {label} : undefined}
+
+ );
+}
+
+function UxpTextArea({
+ ariaLabel, className, disabled = false, label, onValueChange, placeholder,
+ quiet = false, value,
+}: SpectrumTextAreaProps) {
+ const ref = useRef(null);
+ useUxpProperty(ref, "disabled", disabled);
+ useUxpProperty(ref, "quiet", quiet);
+ useUxpProperty(ref, "value", value);
+ useUxpEvent(ref, "input", (event) => {
+ onValueChange(readEventProperty(event, "value", ""));
+ });
+
+ return (
+
+ {label ? {label} : undefined}
+
+ );
+}
+
+export function SpectrumCheckbox(props: SpectrumCheckboxProps) {
+ return isCepRuntime() ? : ;
+}
+
+function NativeCheckbox({
+ ariaLabel,
+ checked,
+ children,
+ className,
+ disabled = false,
+ indeterminate = false,
+ onCheckedChange,
+ onPress,
+}: SpectrumCheckboxProps) {
+ return (
+
+ onCheckedChange(event.currentTarget.checked)}
+ onClick={(event) => onPress?.(event.nativeEvent)}
+ ref={(element) => { if (element) element.indeterminate = indeterminate; }}
+ type="checkbox" />
+ {children ? {children} : undefined}
+
+ );
+}
+
+function UxpCheckbox({
+ ariaLabel, checked, children, className, disabled = false,
+ indeterminate = false, onCheckedChange, onPress,
+}: SpectrumCheckboxProps) {
+ const ref = useRef(null);
+ useUxpProperty(ref, "checked", checked);
+ useUxpProperty(ref, "disabled", disabled);
+ useUxpProperty(ref, "indeterminate", indeterminate);
+ useUxpEvent(ref, "change", (event) => {
+ onCheckedChange(readEventProperty(event, "checked", false));
+ });
+ useUxpEvent(ref, "click", onPress);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function isCepRuntime(): boolean {
+ return Reflect.get(window, "__VIDXP_CEP__") === true;
+}
+
+function booleanAttribute(value: boolean): true | undefined {
+ return value ? true : undefined;
+}
+
+function readEventProperty(event: Event, property: string, fallback: T): T {
+ const target = event.currentTarget;
+ if (!target) return fallback;
+ const value = Reflect.get(target, property) as T | undefined;
+ return value ?? fallback;
+}
+
+function useUxpEvent(
+ ref: RefObject,
+ eventName: "change" | "click" | "input",
+ handler?: (event: Event) => void,
+) {
+ useEffect(() => {
+ const element = ref.current;
+ if (!element || !handler) return;
+ element.addEventListener(eventName, handler);
+ return () => element.removeEventListener(eventName, handler);
+ }, [eventName, handler, ref]);
+}
+
+function useUxpProperty(
+ ref: RefObject,
+ property: string,
+ value: T,
+) {
+ useEffect(() => {
+ const element = ref.current;
+ if (element) Reflect.set(element, property, value);
+ }, [property, ref, value]);
+}
diff --git a/premiere/src/ui/spectrum-elements.d.ts b/premiere/src/ui/spectrum-elements.d.ts
new file mode 100644
index 00000000..1a037de2
--- /dev/null
+++ b/premiere/src/ui/spectrum-elements.d.ts
@@ -0,0 +1,29 @@
+import type { HTMLAttributes, RefAttributes } from "react";
+
+interface SpectrumElementAttributes
+ extends Omit, "className">,
+ RefAttributes {
+ checked?: true;
+ class?: string;
+ disabled?: true;
+ indeterminate?: true;
+ placeholder?: string;
+ quiet?: true;
+ selected?: true;
+ type?: string;
+ value?: number | string;
+ variant?: string;
+}
+
+declare module "react/jsx-runtime" {
+ namespace JSX {
+ interface IntrinsicElements {
+ "sp-action-button": SpectrumElementAttributes;
+ "sp-button": SpectrumElementAttributes;
+ "sp-checkbox": SpectrumElementAttributes;
+ "sp-label": SpectrumElementAttributes;
+ "sp-textarea": SpectrumElementAttributes;
+ "sp-textfield": SpectrumElementAttributes;
+ }
+ }
+}
diff --git a/premiere/src/ui/styles.css b/premiere/src/ui/styles.css
new file mode 100644
index 00000000..987d5288
--- /dev/null
+++ b/premiere/src/ui/styles.css
@@ -0,0 +1,502 @@
+:root {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --accent-bright: #9b88ff;
+ --success: #4ac58b;
+ --warning: #efb84d;
+ --danger: #f06a72;
+}
+
+body.theme-dark {
+ color-scheme: dark;
+ --background: var(--uxp-host-background-color, #191919);
+ --surface: #252525;
+ --field-background: #2d2d2d;
+ --border: var(--uxp-host-border-color, #454545);
+ --text: var(--uxp-host-text-color, #f4f4f4);
+ --muted: var(--uxp-host-text-color-secondary, #aaa);
+}
+
+body.theme-light {
+ color-scheme: light;
+ --background: var(--uxp-host-background-color, #f5f5f5);
+ --surface: #fff;
+ --field-background: #fff;
+ --border: var(--uxp-host-border-color, #b8b8b8);
+ --text: var(--uxp-host-text-color, #242424);
+ --muted: var(--uxp-host-text-color-secondary, #626262);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-width: 300px;
+ min-height: 100vh;
+ background: var(--background, #191919);
+ color: var(--text, #f4f4f4);
+}
+
+sp-button,
+sp-textarea,
+sp-textfield {
+ width: 100%;
+}
+
+sp-checkbox {
+ flex: none;
+}
+
+.native-button,
+.native-control input,
+.native-control textarea {
+ width: 100%;
+ padding: 8px 9px;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--field-background);
+ color: inherit;
+ font: inherit;
+}
+
+.native-button {
+ cursor: pointer;
+ background: #6652c6;
+ font-weight: 650;
+}
+
+.native-button.quiet {
+ width: auto;
+ background: transparent;
+}
+
+.native-button:disabled {
+ cursor: default;
+ opacity: .55;
+}
+
+.native-control {
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.native-control > span {
+ font-size: 11px;
+ font-weight: 650;
+}
+
+.native-control textarea {
+ resize: vertical;
+}
+
+.native-checkbox {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ font-size: 11px;
+}
+
+.native-checkbox input {
+ flex: none;
+ margin: 0;
+}
+
+.native-secret,
+.field select {
+ width: 100%;
+ padding: 8px 9px;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--field-background);
+ color: inherit;
+ font: inherit;
+}
+
+.full-width-action {
+ width: 100%;
+}
+
+.inline-action,
+.dismiss-action {
+ flex: none;
+ width: auto;
+}
+
+.dismiss-action {
+ min-width: 26px;
+ font-size: 16px;
+}
+
+.query-field {
+ min-height: 82px;
+}
+
+.panel {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 14px;
+}
+
+.panel-header {
+ padding: 4px 2px 2px;
+}
+
+.panel-header h1 {
+ margin: 2px 0 0;
+ font-size: 22px;
+ letter-spacing: -.02em;
+}
+
+.panel-header p,
+.section-heading p,
+.advanced p,
+.status-card p,
+.notice p {
+ margin: 4px 0 0;
+ color: var(--muted);
+ font-size: 11px;
+ line-height: 1.45;
+}
+
+.eyebrow {
+ color: var(--accent-bright);
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: .08em;
+ text-transform: uppercase;
+}
+
+.section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 12px;
+ border: 1px solid var(--border);
+ border-radius: 9px;
+ background: var(--surface);
+}
+
+.section-heading {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 10px;
+}
+
+.section-heading h2 {
+ margin: 0;
+ font-size: 13px;
+}
+
+.field {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.field > span {
+ font-size: 11px;
+ font-weight: 650;
+}
+
+.badge {
+ flex: none;
+ padding: 3px 7px;
+ border-radius: 999px;
+ background: #393939;
+ color: var(--muted);
+ font-size: 9px;
+ font-weight: 700;
+ text-transform: uppercase;
+}
+
+.badge.ready {
+ background: #1e4434;
+ color: var(--success);
+}
+
+.badge.error {
+ color: var(--danger);
+}
+
+.badge.connecting {
+ color: var(--warning);
+}
+
+.advanced {
+ color: var(--muted);
+ font-size: 11px;
+}
+
+.advanced summary {
+ cursor: pointer;
+}
+
+.advanced .field {
+ margin-top: 8px;
+}
+
+.toolbar {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+}
+
+.toolbar > sp-textfield {
+ flex: 1;
+ min-width: 0;
+}
+
+.library {
+ max-height: 220px;
+ overflow: auto;
+ padding: 5px;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: #202020;
+}
+
+.tree-row,
+.tree-bin summary {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ min-height: 27px;
+ padding: 4px 5px;
+ border-radius: 4px;
+ font-size: 11px;
+}
+
+.tree-row:hover,
+.tree-bin summary:hover {
+ background: #303030;
+}
+
+.tree-row.disabled {
+ color: #777;
+}
+
+.tree-bin summary {
+ cursor: pointer;
+ list-style: none;
+}
+
+.tree-bin summary::-webkit-details-marker {
+ display: none;
+}
+
+.tree-bin[open] > summary .node-icon {
+ transform: rotate(90deg);
+}
+
+.tree-children {
+ margin-left: 18px;
+ padding-left: 3px;
+ border-left: 1px solid #3b3b3b;
+}
+
+.node-icon {
+ width: 11px;
+ color: #888;
+ transition: transform .12s ease;
+}
+
+.node-name {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.node-state {
+ margin-left: auto;
+ color: #777;
+ font-size: 9px;
+}
+
+.selection-summary,
+.empty {
+ margin: 0;
+ color: var(--muted);
+ font-size: 10px;
+}
+
+.capabilities {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.capability {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 7px;
+ border: 1px solid #3d3d3d;
+ border-radius: 6px;
+}
+
+.capability.selected {
+ border-color: #725fcd;
+ background: #302b49;
+}
+
+.capability > span {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 2px;
+ padding-top: 2px;
+}
+
+.capability small {
+ color: var(--muted);
+ font-size: 9px;
+ line-height: 1.35;
+}
+
+.status-card,
+.notice {
+ display: flex;
+ align-items: flex-start;
+ gap: 9px;
+ padding: 11px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.status-card strong,
+.notice strong {
+ font-size: 11px;
+}
+
+.status-card.error {
+ border-color: var(--danger);
+}
+
+.notice.success {
+ border-color: #3b9468;
+}
+
+.notice.warning {
+ border-color: #a78036;
+}
+
+.notice > div {
+ min-width: 0;
+ flex: 1;
+}
+
+.notice p {
+ overflow-wrap: anywhere;
+}
+
+.spinner {
+ width: 13px;
+ height: 13px;
+ flex: none;
+ margin-top: 1px;
+ border: 2px solid #555;
+ border-top-color: var(--accent-bright);
+ border-radius: 50%;
+ animation: spin .8s linear infinite;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ flex: none;
+ margin-top: 3px;
+ border-radius: 50%;
+ background: var(--danger);
+}
+
+.inline-error {
+ margin: 0;
+ color: var(--danger);
+ font-size: 10px;
+ line-height: 1.4;
+}
+
+.results ol {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.results li {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 8px;
+ border: 1px solid #3d3d3d;
+ border-radius: 6px;
+}
+
+.result-rank {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 22px;
+ height: 22px;
+ flex: none;
+ border-radius: 5px;
+ background: #373737;
+ color: var(--muted);
+ font-size: 10px;
+}
+
+.result-copy {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 3px;
+}
+
+.result-copy > strong {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: 11px;
+}
+
+.result-copy > span,
+.score {
+ color: var(--muted);
+ font-size: 10px;
+ font-variant-numeric: tabular-nums;
+}
+
+.score {
+ flex: none;
+}
+
+.result-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 3px;
+}
+
+.result-tags span {
+ padding: 2px 5px;
+ border-radius: 999px;
+ background: #383838;
+ color: #c9c9c9;
+ font-size: 8px;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/premiere/src/ui/theme.ts b/premiere/src/ui/theme.ts
new file mode 100644
index 00000000..4c5d882a
--- /dev/null
+++ b/premiere/src/ui/theme.ts
@@ -0,0 +1,126 @@
+export type PremiereThemeClass = "theme-dark" | "theme-light";
+
+type PremierePalette = Record<`--uxp-host-${string}`, string>;
+
+interface ThemeListenerCollection {
+ addListener(listener: (theme: string) => void): void;
+ removeListener(listener: (theme: string) => void): void;
+}
+
+interface PremiereThemeApi {
+ getCurrent(): string;
+ onUpdated: ThemeListenerCollection;
+}
+
+interface ThemeDocument {
+ documentElement?: {
+ dataset: Record;
+ style?: { setProperty(name: string, value: string): void };
+ };
+ body: {
+ classList: {
+ add(...tokens: string[]): void;
+ remove(...tokens: string[]): void;
+ };
+ };
+ theme?: PremiereThemeApi;
+}
+
+const premierePalettes = {
+ darkest: {
+ "--uxp-host-background-color": "#1D1D1D",
+ "--uxp-host-text-color": "#D0D0D0",
+ "--uxp-host-border-color": "#303030",
+ "--uxp-host-link-text-color": "#0098FA",
+ "--uxp-host-link-hover-text-color": "#3DACFE",
+ "--uxp-host-label-text-color": "#B0B0B0",
+ "--uxp-host-widget-hover-background-color": "#000000",
+ "--uxp-host-widget-hover-text-color": "#D0D0D0",
+ "--uxp-host-widget-hover-border-color": "#4B4B4B",
+ "--uxp-host-text-color-secondary": "#B0B0B0",
+ },
+ dark: {
+ "--uxp-host-background-color": "#323232",
+ "--uxp-host-text-color": "#D1D1D1",
+ "--uxp-host-border-color": "#3F3F3F",
+ "--uxp-host-link-text-color": "#2DA5FD",
+ "--uxp-host-link-hover-text-color": "#57AFF0",
+ "--uxp-host-label-text-color": "#B2B2B2",
+ "--uxp-host-widget-hover-background-color": "#1D1D1D",
+ "--uxp-host-widget-hover-text-color": "#D1D1D1",
+ "--uxp-host-widget-hover-border-color": "#545454",
+ "--uxp-host-text-color-secondary": "#B2B2B2",
+ },
+ light: {
+ "--uxp-host-background-color": "#F8F8F8",
+ "--uxp-host-text-color": "#464646",
+ "--uxp-host-border-color": "#E6E6E6",
+ "--uxp-host-link-text-color": "#0067E4",
+ "--uxp-host-link-hover-text-color": "#0056BD",
+ "--uxp-host-label-text-color": "#6D6D6D",
+ "--uxp-host-widget-hover-background-color": "#FFFFFF",
+ "--uxp-host-widget-hover-text-color": "#464646",
+ "--uxp-host-widget-hover-border-color": "#D5D5D5",
+ "--uxp-host-text-color-secondary": "#6D6D6D",
+ },
+} satisfies Record;
+
+const premiereMacLinkColors = {
+ darkest: ["#4096F3", "#5EAAF7"],
+ dark: ["#54A3F6", "#72B7F9"],
+ light: ["#147AF3", "#0265DC"],
+} as const;
+
+function normalizedTheme(theme: string): keyof typeof premierePalettes {
+ const value = theme.toLowerCase();
+ if (value.includes("light")) return "light";
+ return value.includes("darkest") ? "darkest" : "dark";
+}
+
+export function premiereThemeClass(theme: string): PremiereThemeClass {
+ return normalizedTheme(theme) === "light" ? "theme-light" : "theme-dark";
+}
+
+export function premiereThemePalette(
+ theme: string,
+ platform: string,
+): PremierePalette {
+ const name = normalizedTheme(theme);
+ const palette = { ...premierePalettes[name] };
+ if (platform === "darwin") {
+ const [link, linkHover] = premiereMacLinkColors[name];
+ palette["--uxp-host-link-text-color"] = link;
+ palette["--uxp-host-link-hover-text-color"] = linkHover;
+ }
+ return palette;
+}
+
+export function installPremiereTheme(
+ target: ThemeDocument,
+ platform = "unknown",
+): () => void {
+ const applyTheme = (theme: string) => {
+ target.body.classList.remove("theme-dark", "theme-light");
+ target.body.classList.add(premiereThemeClass(theme));
+ const root = target.documentElement;
+ if (root) {
+ root.dataset.theme = theme.toLowerCase();
+ root.dataset.platform = platform;
+ for (const [name, value] of Object.entries(
+ premiereThemePalette(theme, platform),
+ )) {
+ root.style?.setProperty(name, value);
+ }
+ }
+ };
+
+ const theme = target.theme;
+ if (!theme) {
+ applyTheme("dark");
+ return () => undefined;
+ }
+
+ applyTheme(theme.getCurrent());
+ theme.onUpdated.addListener(applyTheme);
+ return () => theme.onUpdated.removeListener(applyTheme);
+}
diff --git a/premiere/src/vite-env.d.ts b/premiere/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/premiere/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/premiere/tests/cep-adapter.test.ts b/premiere/tests/cep-adapter.test.ts
new file mode 100644
index 00000000..9db2a41f
--- /dev/null
+++ b/premiere/tests/cep-adapter.test.ts
@@ -0,0 +1,41 @@
+import { afterEach, describe, expect, it } from "vitest";
+
+import { createCepPremiereAdapter } from "../src/premiere/cep-adapter";
+
+describe("CEP Premiere adapter", () => {
+ afterEach(() => {
+ Reflect.deleteProperty(globalThis, "window");
+ });
+
+ it("reads the shared library contract from ExtendScript", async () => {
+ Reflect.set(globalThis, "window", {
+ __adobe_cep__: {
+ evalScript(script: string, callback: (value: string) => void) {
+ expect(script).toBe("$._VIDXP.getLibrary()");
+ callback(JSON.stringify({
+ ok: true,
+ value: { projectName: "Cut", items: [] },
+ }));
+ },
+ },
+ });
+
+ await expect(createCepPremiereAdapter().getLibrary()).resolves.toEqual({
+ projectName: "Cut",
+ items: [],
+ });
+ });
+
+ it("turns a bounded host error into a rejected operation", async () => {
+ Reflect.set(globalThis, "window", {
+ __adobe_cep__: {
+ evalScript(_script: string, callback: (value: string) => void) {
+ callback(JSON.stringify({ ok: false, error: "No project is open." }));
+ },
+ },
+ });
+
+ await expect(createCepPremiereAdapter().getSelectedProjectItemIds())
+ .rejects.toThrow("No project is open.");
+ });
+});
diff --git a/premiere/tests/library.test.ts b/premiere/tests/library.test.ts
new file mode 100644
index 00000000..37513953
--- /dev/null
+++ b/premiere/tests/library.test.ts
@@ -0,0 +1,72 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ chunkPaths,
+ collectSelectedClips,
+ filterLibrary,
+} from "../src/premiere/library";
+import type { PremiereMediaNode } from "../src/premiere/types";
+
+const library: PremiereMediaNode[] = [
+ {
+ kind: "bin",
+ id: "interviews",
+ name: "Interviews",
+ children: [
+ {
+ kind: "clip",
+ id: "clip-a",
+ name: "A camera.mp4",
+ nativePath: "C:/Media/a.mp4",
+ availability: "ready",
+ },
+ {
+ kind: "clip",
+ id: "clip-offline",
+ name: "Offline.mov",
+ availability: "offline",
+ },
+ ],
+ },
+ {
+ kind: "clip",
+ id: "clip-b",
+ name: "B-roll.mov",
+ nativePath: "C:/Media/b.mov",
+ availability: "ready",
+ },
+];
+
+describe("Premiere media library helpers", () => {
+ it("expands bins, skips unavailable items, and deduplicates source paths", () => {
+ const duplicate = {
+ ...library[1],
+ id: "duplicate-b",
+ };
+ const clips = collectSelectedClips(
+ [...library, duplicate],
+ new Set(["interviews", "clip-b", "duplicate-b"]),
+ );
+
+ expect(clips.map((clip) => clip.nativePath)).toEqual([
+ "C:/Media/a.mp4",
+ "C:/Media/b.mov",
+ ]);
+ });
+
+ it("keeps a matching bin with only matching descendants", () => {
+ expect(filterLibrary(library, "camera")).toEqual([
+ {
+ kind: "bin",
+ id: "interviews",
+ name: "Interviews",
+ children: [library[0].kind === "bin" ? library[0].children[0] : null],
+ },
+ ]);
+ });
+
+ it("chunks large Premiere selections for the ten-path ingestion contract", () => {
+ const paths = Array.from({ length: 23 }, (_, index) => `clip-${index}`);
+ expect(chunkPaths(paths).map((batch) => batch.length)).toEqual([10, 10, 3]);
+ });
+});
diff --git a/premiere/tests/theme.test.ts b/premiere/tests/theme.test.ts
new file mode 100644
index 00000000..6d1e253c
--- /dev/null
+++ b/premiere/tests/theme.test.ts
@@ -0,0 +1,61 @@
+import { describe, expect, it, vi } from "vitest";
+
+import {
+ installPremiereTheme,
+ premiereThemeClass,
+ premiereThemePalette,
+} from "../src/ui/theme";
+
+describe("Premiere theme integration", () => {
+ it("maps Premiere light themes and dark variants", () => {
+ expect(premiereThemeClass("light")).toBe("theme-light");
+ expect(premiereThemeClass("darkest")).toBe("theme-dark");
+ expect(premiereThemePalette("darkest", "win32")).toMatchObject({
+ "--uxp-host-background-color": "#1D1D1D",
+ "--uxp-host-link-text-color": "#0098FA",
+ });
+ expect(premiereThemePalette("darkest", "darwin")).toMatchObject({
+ "--uxp-host-link-text-color": "#4096F3",
+ });
+ });
+
+ it("applies updates and removes its listener during cleanup", () => {
+ const classes = new Set();
+ const dataset: Record = {};
+ const properties = new Map();
+ let listener: ((theme: string) => void) | undefined;
+ const addListener = vi.fn((next: (theme: string) => void) => {
+ listener = next;
+ });
+ const removeListener = vi.fn();
+ const cleanup = installPremiereTheme({
+ documentElement: {
+ dataset,
+ style: {
+ setProperty: (name, value) => properties.set(name, value),
+ },
+ },
+ body: {
+ classList: {
+ add: (...tokens) => tokens.forEach((token) => classes.add(token)),
+ remove: (...tokens) => tokens.forEach((token) => classes.delete(token)),
+ },
+ },
+ theme: {
+ getCurrent: () => "dark",
+ onUpdated: { addListener, removeListener },
+ },
+ }, "win32");
+
+ expect(classes).toEqual(new Set(["theme-dark"]));
+ expect(dataset).toEqual({ theme: "dark", platform: "win32" });
+ expect(properties.get("--uxp-host-background-color")).toBe("#323232");
+ listener?.("light");
+ expect(classes).toEqual(new Set(["theme-light"]));
+ expect(dataset.theme).toBe("light");
+ expect(properties.get("--uxp-host-background-color")).toBe("#F8F8F8");
+
+ cleanup();
+ expect(removeListener).toHaveBeenCalledOnce();
+ });
+});
diff --git a/premiere/tests/uxp-config.test.ts b/premiere/tests/uxp-config.test.ts
new file mode 100644
index 00000000..65ebdbf8
--- /dev/null
+++ b/premiere/tests/uxp-config.test.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it } from "vitest";
+
+import { config } from "../uxp.config";
+
+describe("Bolt UXP configuration", () => {
+ it("targets the supported Premiere UXP host with a production manifest", () => {
+ expect(config.manifest.manifestVersion).toBe(5);
+ expect(config.manifest.host).toEqual([
+ { app: "premierepro", minVersion: "25.6.0" },
+ ]);
+ expect(config.manifest.entrypoints).toContainEqual(
+ expect.objectContaining({ type: "panel", id: "vidxpSearch" }),
+ );
+ expect(config.manifest.requiredPermissions?.network?.domains).not.toContain(
+ "ws://localhost:8080",
+ );
+ expect(config.webviewUi).toBe(false);
+ expect(config.uniqueIds).toBe(false);
+ expect(config.debugger).toBe("udt");
+ });
+});
diff --git a/premiere/tests/vidxp-client.test.ts b/premiere/tests/vidxp-client.test.ts
new file mode 100644
index 00000000..0f8247c9
--- /dev/null
+++ b/premiere/tests/vidxp-client.test.ts
@@ -0,0 +1,124 @@
+import { describe, expect, it, vi } from "vitest";
+
+import {
+ VidXPApiError,
+ VidXPClient,
+ type VidXPFetch,
+} from "../src/services/vidxp/client";
+import type { MediaIngestionStatus, VidXPJob } from "../src/services/vidxp/types";
+
+const ingestion: MediaIngestionStatus = {
+ session_id: "ingestion-1",
+ aggregate_state: "processing",
+ index_modalities: ["scene"],
+ file_count: 1,
+ searchable_file_count: 0,
+ failed_file_count: 0,
+ index_failed_file_count: 0,
+ items: [],
+ terminal: false,
+ poll_after_seconds: 1,
+ status: "Importing",
+ next_action: "Poll",
+};
+
+describe("VidXPClient", () => {
+ it("submits Premiere paths to the local ingestion contract", async () => {
+ const fetchImpl = vi.fn().mockResolvedValue(jsonResponse(ingestion));
+ const client = new VidXPClient({
+ baseUrl: " http://127.0.0.1:32191/docs ",
+ bearerToken: " local-token ",
+ fetchImpl: fetchImpl as VidXPFetch,
+ });
+
+ await client.createLocalIngestion(
+ ["C:/Media/a.mp4"],
+ ["scene"],
+ "request-key",
+ );
+
+ const [url, init] = fetchImpl.mock.calls[0] as [string, RequestInit];
+ const headers = new Headers(init.headers);
+ expect(url).toBe("http://127.0.0.1:32191/api/v1/media/local-ingestions");
+ expect(init.method).toBe("POST");
+ expect(headers.get("Authorization")).toBe("Bearer local-token");
+ expect(headers.get("Idempotency-Key")).toBe("request-key");
+ if (typeof init.body !== "string") throw new Error("Expected a JSON request body.");
+ expect(JSON.parse(init.body)).toEqual({
+ paths: ["C:/Media/a.mp4"],
+ modalities: ["scene"],
+ index_after_import: true,
+ });
+ });
+
+ it("polls a search job and returns its typed result", async () => {
+ const completed: VidXPJob = {
+ job_id: "job-1",
+ kind: "search",
+ state: "succeeded",
+ terminal: true,
+ poll_after_seconds: 0,
+ result: {
+ kind: "search",
+ result: {
+ query_id: "query-1",
+ query: "door opens",
+ modalities: ["scene"],
+ moments: [],
+ },
+ },
+ };
+ const fetchImpl = vi.fn().mockResolvedValue(jsonResponse(completed));
+ const sleep = vi.fn().mockResolvedValue(undefined);
+ const client = new VidXPClient({
+ baseUrl: "http://localhost:32191",
+ fetchImpl: fetchImpl as VidXPFetch,
+ sleep,
+ });
+ const queued: VidXPJob = {
+ job_id: "job-1",
+ kind: "search",
+ state: "queued",
+ terminal: false,
+ poll_after_seconds: 1,
+ };
+
+ const result = await client.waitForJob(queued, vi.fn());
+
+ expect(sleep).toHaveBeenCalledWith(1000);
+ expect(result.result?.result.query_id).toBe("query-1");
+ });
+
+ it("surfaces safe API remediation without exposing the bearer token", async () => {
+ const fetchImpl = vi.fn().mockResolvedValue(
+ jsonResponse(
+ {
+ error: {
+ code: "models_unavailable",
+ message: "Models are not prepared.",
+ details: { remediation: "Prepare the selected features in Desktop." },
+ },
+ },
+ 503,
+ ),
+ );
+ const client = new VidXPClient({
+ baseUrl: "http://localhost:32191",
+ bearerToken: "secret-token",
+ fetchImpl: fetchImpl as VidXPFetch,
+ });
+
+ const error = await client.listCapabilities().catch((caught: unknown) => caught);
+
+ expect(error).toBeInstanceOf(VidXPApiError);
+ expect(String(error)).toContain("Prepare the selected features in Desktop.");
+ expect(String(error)).not.toContain("secret-token");
+ });
+});
+
+function jsonResponse(payload: unknown, status = 200): Response {
+ return new Response(JSON.stringify(payload), {
+ status,
+ headers: { "content-type": "application/json" },
+ });
+}
diff --git a/premiere/tsconfig.json b/premiere/tsconfig.json
new file mode 100644
index 00000000..48e6225e
--- /dev/null
+++ b/premiere/tsconfig.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["ESNext", "DOM"],
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "jsx": "react-jsx",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "types": [
+ "@adobe/cc-ext-uxp-types",
+ "@adobe/premierepro",
+ "@types/node",
+ "vitest/globals"
+ ]
+ },
+ "include": [
+ "index.tsx",
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "tests/**/*.ts",
+ "uxp.config.ts",
+ "vite.config.ts"
+ ],
+ "exclude": ["dist", "node_modules"]
+}
diff --git a/premiere/uxp.config.ts b/premiere/uxp.config.ts
new file mode 100644
index 00000000..7f19fdc0
--- /dev/null
+++ b/premiere/uxp.config.ts
@@ -0,0 +1,68 @@
+import { readFileSync } from "node:fs";
+import type { UXP_Config, UXP_Manifest } from "vite-uxp-plugin";
+
+const hotReloadPort = 8080;
+const development = process.env.BOLT_MODE === "dev";
+const packageJson = JSON.parse(
+ readFileSync(new URL("./package.json", import.meta.url), "utf8"),
+) as { version: string };
+
+const manifest: UXP_Manifest = {
+ manifestVersion: 5,
+ id: "org.grayhat.vidxp-premiere",
+ name: "VidXP Search",
+ version: packageJson.version,
+ main: "index.html",
+ host: [
+ {
+ app: "premierepro",
+ minVersion: "25.6.0",
+ },
+ ],
+ requiredPermissions: {
+ network: {
+ domains: [
+ "http://127.0.0.1",
+ "http://localhost",
+ "https://127.0.0.1",
+ "https://localhost",
+ ...(development ? [`ws://localhost:${hotReloadPort}`] : []),
+ ],
+ },
+ },
+ entrypoints: [
+ {
+ type: "panel",
+ id: "vidxpSearch",
+ label: {
+ default: "VidXP Search",
+ },
+ minimumSize: {
+ width: 320,
+ height: 480,
+ },
+ maximumSize: {
+ width: 1800,
+ height: 1800,
+ },
+ preferredDockedSize: {
+ width: 380,
+ height: 720,
+ },
+ preferredFloatingSize: {
+ width: 480,
+ height: 760,
+ },
+ },
+ ],
+};
+
+export const config: UXP_Config = {
+ manifest,
+ hotReloadPort,
+ webviewUi: false,
+ webviewReloadPort: 8082,
+ copyZipAssets: [],
+ uniqueIds: false,
+ debugger: "udt",
+};
diff --git a/premiere/vite.config.ts b/premiere/vite.config.ts
new file mode 100644
index 00000000..2905d15e
--- /dev/null
+++ b/premiere/vite.config.ts
@@ -0,0 +1,60 @@
+import { resolve } from "node:path";
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+import { runAction, uxp } from "vite-uxp-plugin";
+
+import { config as uxpConfig } from "./uxp.config.ts";
+
+const projectRoot = import.meta.dirname;
+const action = process.env.BOLT_ACTION;
+
+if (action) runAction(uxpConfig, action);
+
+export default defineConfig(({ mode }) => {
+ const cep = mode === "cep";
+ const boltMode = process.env.BOLT_MODE;
+ return {
+ plugins: cep ? [react()] : [uxp(uxpConfig, boltMode), react()],
+ publicDir: false,
+ base: "./",
+ build: cep
+ ? {
+ outDir: "dist/cep",
+ emptyOutDir: true,
+ minify: true,
+ sourcemap: true,
+ target: "chrome88",
+ rolldownOptions: {
+ input: resolve(projectRoot, "cep/index.tsx"),
+ output: {
+ format: "iife",
+ entryFileNames: "index.js",
+ assetFileNames: "[name][extname]",
+ },
+ },
+ }
+ : {
+ // Bolt's CCX generator packages this project-root directory.
+ outDir: "dist",
+ emptyOutDir: true,
+ minify: false,
+ sourcemap: boltMode === "package" ? false : "inline",
+ target: "esnext",
+ rolldownOptions: {
+ external: ["os", "premierepro", "uxp"],
+ output: {
+ format: "iife",
+ entryFileNames: "index.js",
+ assetFileNames: (asset) =>
+ asset.names.some((name) => name.endsWith(".css"))
+ ? "styles.css"
+ : "[name][extname]",
+ },
+ },
+ },
+ test: {
+ environment: "node",
+ include: ["tests/**/*.test.ts"],
+ },
+ };
+});
diff --git a/src/vidxp/api_routes/media.py b/src/vidxp/api_routes/media.py
index 504af0f7..4ab31bfd 100644
--- a/src/vidxp/api_routes/media.py
+++ b/src/vidxp/api_routes/media.py
@@ -17,21 +17,39 @@
CreateUploadIntentCommand,
ErrorCategory,
ListMediaCommand,
+ LocalMediaIngestionCommand,
MediaAsset,
MediaPage,
+ MediaUploadSessionStatus,
Principal,
UploadIntent,
UploadIntentId,
+ UploadSessionId,
)
from vidxp.api_models import UploadIntentResponse
from vidxp.composition import HttpApplicationContext
from vidxp.core.identifiers import MediaId
from vidxp.core.media import MediaState
+from vidxp.network_share import is_loopback_host
router = APIRouter(prefix="/media", tags=["media"])
+def _local_ingestion_service(
+ service: HttpApplicationContext,
+):
+ if service.uploads is None or not is_loopback_host(
+ service.settings.http_bind_host
+ ):
+ raise ApplicationError(
+ "local_ingestion_unavailable",
+ ErrorCategory.unavailable,
+ "Local media ingestion is not configured for this HTTP service.",
+ )
+ return service.uploads
+
+
def _upload_response(
service: HttpApplicationContext,
actor: Principal,
@@ -158,6 +176,62 @@ def import_media(
staged.unlink(missing_ok=True)
+@router.post(
+ "/local-ingestions",
+ response_model=MediaUploadSessionStatus,
+ status_code=202,
+ operation_id="ingestLocalMedia",
+ summary="Ingest local media paths",
+ description=(
+ "Local runtime only: register and optionally index up to ten media "
+ "paths that are available to the VidXP process. Poll the returned "
+ "session for per-file progress."
+ ),
+ dependencies=[Depends(write_principal)],
+)
+def create_local_ingestion(
+ command: LocalMediaIngestionCommand,
+ response: Response,
+ service: Annotated[HttpApplicationContext, Depends(context)],
+ actor: Annotated[Principal, Depends(write_principal)],
+ idempotency_key: HttpIdempotencyKey,
+) -> MediaUploadSessionStatus:
+ uploads = _local_ingestion_service(service)
+ selected = service.application.select_index_modalities(command.modalities)
+ status = uploads.create_local_ingestion(
+ command.paths,
+ principal=actor,
+ request_key=scoped_request_key(
+ service,
+ actor,
+ operation="local-media-ingestion",
+ idempotency_key=idempotency_key,
+ ),
+ index_after_import=command.index_after_import,
+ index_modalities=selected,
+ )
+ response.headers["Location"] = (
+ f"/api/v1/media/local-ingestions/{status.session_id}"
+ )
+ return status
+
+
+@router.get(
+ "/local-ingestions/{ingestion_id}",
+ response_model=MediaUploadSessionStatus,
+ operation_id="getLocalMediaIngestion",
+ summary="Get local media ingestion status",
+ dependencies=[Depends(read_principal)],
+)
+def get_local_ingestion(
+ ingestion_id: UploadSessionId,
+ service: Annotated[HttpApplicationContext, Depends(context)],
+ actor: Annotated[Principal, Depends(read_principal)],
+) -> MediaUploadSessionStatus:
+ uploads = _local_ingestion_service(service)
+ return uploads.get_status(ingestion_id, principal=actor)
+
+
@router.get(
"",
response_model=MediaPage,
diff --git a/src/vidxp/application_models.py b/src/vidxp/application_models.py
index e2bb1eb7..395bde42 100644
--- a/src/vidxp/application_models.py
+++ b/src/vidxp/application_models.py
@@ -308,6 +308,7 @@ class CapabilityIdentityMode(StrEnum):
class CapabilitySummary(ApplicationModel):
name: str = Field(min_length=1)
+ label: str = Field(min_length=1)
description: str = Field(min_length=1)
install_extra: str = Field(min_length=1)
supports_indexing: bool
diff --git a/src/vidxp/capabilities/actor/definition.py b/src/vidxp/capabilities/actor/definition.py
index e05c2936..bfb7370d 100644
--- a/src/vidxp/capabilities/actor/definition.py
+++ b/src/vidxp/capabilities/actor/definition.py
@@ -68,6 +68,7 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
name="actor",
+ label="Actor recognition",
description="Index, inspect, and render actor clusters.",
extra="actor",
config_model=ActorConfig,
diff --git a/src/vidxp/capabilities/contracts.py b/src/vidxp/capabilities/contracts.py
index 059babcd..eb4bab2b 100644
--- a/src/vidxp/capabilities/contracts.py
+++ b/src/vidxp/capabilities/contracts.py
@@ -224,6 +224,7 @@ class CapabilityDefinition(_ContractModel):
"""Domain metadata for one named capability."""
name: str = Field(min_length=1)
+ label: str | None = Field(default=None, min_length=1)
description: str = Field(min_length=1)
extra: str = Field(min_length=1)
config_model: type[CapabilityConfig] = CapabilityConfig
@@ -238,6 +239,12 @@ class CapabilityDefinition(_ContractModel):
model_specs: tuple[ModelSpec | ArtifactSpec, ...] = ()
prepares_models: bool = False
+ @property
+ def display_label(self) -> str:
+ """Return the product label while preserving older external plugins."""
+
+ return self.label or self.name.replace("_", " ").title()
+
@field_validator("config_model")
@classmethod
def _require_config_model(
diff --git a/src/vidxp/capabilities/dialogue/definition.py b/src/vidxp/capabilities/dialogue/definition.py
index 0474773a..0c0ba465 100644
--- a/src/vidxp/capabilities/dialogue/definition.py
+++ b/src/vidxp/capabilities/dialogue/definition.py
@@ -83,6 +83,7 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
name="dialogue",
+ label="Dialogue search",
description="Index and search spoken dialogue.",
extra="dialogue",
config_model=DialogueConfig,
diff --git a/src/vidxp/capabilities/scene/definition.py b/src/vidxp/capabilities/scene/definition.py
index 9bc36216..43871f8d 100644
--- a/src/vidxp/capabilities/scene/definition.py
+++ b/src/vidxp/capabilities/scene/definition.py
@@ -48,6 +48,7 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
name="scene",
+ label="Visual scene search",
description="Index and search visual scenes.",
extra="scene",
config_model=SceneConfig,
diff --git a/src/vidxp/capabilities/sound/definition.py b/src/vidxp/capabilities/sound/definition.py
index 5c0caea5..e26cd3ad 100644
--- a/src/vidxp/capabilities/sound/definition.py
+++ b/src/vidxp/capabilities/sound/definition.py
@@ -48,6 +48,7 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
name="sound",
+ label="Sound event search",
description="Index and search music, environmental sounds, and audio events.",
extra="sound",
config_model=SoundConfig,
diff --git a/src/vidxp/capabilities/videoprism/definition.py b/src/vidxp/capabilities/videoprism/definition.py
index b4acc2e9..566d8823 100644
--- a/src/vidxp/capabilities/videoprism/definition.py
+++ b/src/vidxp/capabilities/videoprism/definition.py
@@ -45,6 +45,7 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
name="videoprism",
+ label="Temporal video search",
description="Index and search temporal video clips with VideoPrism.",
extra="videoprism",
config_model=VideoPrismConfig,
diff --git a/src/vidxp/capability_service.py b/src/vidxp/capability_service.py
index 7b0c64cf..abca4848 100644
--- a/src/vidxp/capability_service.py
+++ b/src/vidxp/capability_service.py
@@ -24,6 +24,7 @@ def _summary(self, name: str) -> CapabilitySummary:
definition = self.registry.get(name)
return CapabilitySummary(
name=definition.name,
+ label=definition.display_label,
description=definition.description,
install_extra=definition.extra,
supports_indexing=definition.collection_name is not None,
diff --git a/src/vidxp/control_plane.py b/src/vidxp/control_plane.py
index 91150bff..7b84a08a 100644
--- a/src/vidxp/control_plane.py
+++ b/src/vidxp/control_plane.py
@@ -13,6 +13,7 @@
CreateIndexCommand,
DependencyCheckResult,
IndexStatus,
+ Identifier,
InvalidRequestError,
ListMediaCommand,
MediaAsset,
@@ -88,6 +89,33 @@ def get_capability(self, name: str) -> CapabilityInfo:
except CapabilityRequestError as exc:
raise ResourceNotFoundError("capability") from exc
+ @application_boundary
+ def select_index_modalities(
+ self,
+ requested: tuple[Identifier, ...] | None,
+ ) -> tuple[str, ...]:
+ """Resolve an optional capability selection to indexable names."""
+
+ registry = self.capabilities.registry
+ indexable = registry.index_names()
+ selected = (
+ indexable
+ if requested is None
+ else registry.validate_names(requested)
+ )
+ unsupported = tuple(
+ name for name in selected if name not in indexable
+ )
+ if unsupported:
+ raise CapabilityRequestError(
+ "Indexing does not support these capabilities: "
+ + ", ".join(unsupported)
+ + ".",
+ field="modalities",
+ reason="capability_not_indexable",
+ )
+ return selected
+
@application_boundary
def index_status(self) -> IndexStatus:
stored = self._read_index_status()
diff --git a/src/vidxp/local_probe.py b/src/vidxp/local_probe.py
index 1c0bcfaa..474e0536 100644
--- a/src/vidxp/local_probe.py
+++ b/src/vidxp/local_probe.py
@@ -53,6 +53,32 @@ def desktop_model_cache_catalog() -> list[dict[str, str]]:
return sorted(catalog, key=lambda item: item["id"].casefold())
+def desktop_capability_catalog() -> dict[str, Any]:
+ """Derive the pre-install Desktop catalog from capability contracts."""
+
+ from vidxp.capabilities.registry import create_capability_registry
+ from vidxp.model_contracts import model_artifact_path
+
+ registry = create_capability_registry()
+ capabilities: dict[str, Any] = {}
+ for name, definition in registry.definitions.items():
+ models = [
+ {
+ "cache_key": model_artifact_path(Path(), spec).as_posix(),
+ "download_size_bytes": spec.download_size_bytes,
+ }
+ for spec in registry.model_specs((name,))
+ ]
+ capabilities[name] = {
+ "extra": definition.extra,
+ "modality": definition.name,
+ "label": definition.display_label,
+ "description": definition.description,
+ "models": sorted(models, key=lambda item: item["cache_key"]),
+ }
+ return {"schema_version": 1, "capabilities": capabilities}
+
+
def _module_available(name: str) -> bool:
try:
return importlib.util.find_spec(name) is not None
@@ -102,6 +128,7 @@ def _surface_capability(
def _surface_capabilities(
search_capabilities: list[str],
+ required_worker_capabilities: tuple[str, ...],
) -> dict[str, dict[str, Any]]:
media_ready = media_runtime_is_initialized()
owner_instruction = (
@@ -110,9 +137,7 @@ def _surface_capabilities(
return {
"worker": _surface_capability(
installed=(
- {"dialogue", "scene", "actor", "videoprism"}.issubset(
- search_capabilities
- )
+ set(required_worker_capabilities).issubset(search_capabilities)
and _module_available("pydantic_ai")
),
media_ready=media_ready,
@@ -186,11 +211,11 @@ def _surface_capabilities(
}
-def _installed_search_capabilities() -> list[str]:
+def _installed_search_capabilities(registry: Any | None = None) -> list[str]:
from vidxp.capabilities.registry import create_capability_registry
from vidxp.dependencies import inspect_requirement
- registry = create_capability_registry()
+ registry = registry or create_capability_registry()
installed = []
for name in registry.definitions:
requirements = registry.requirements_for((name,))
@@ -235,8 +260,17 @@ def build_desktop_probe(
)
resolved_launcher = launcher if launcher is not None else sys.argv[0]
- search_capabilities = _installed_search_capabilities()
- surfaces = _surface_capabilities(search_capabilities)
+ from vidxp.capabilities.registry import create_capability_registry
+
+ registry = create_capability_registry()
+ search_capabilities = _installed_search_capabilities(registry)
+ required_worker_capabilities = tuple(
+ name for name in registry.names() if registry.provenance(name) is None
+ )
+ surfaces = _surface_capabilities(
+ search_capabilities,
+ required_worker_capabilities,
+ )
return {
"product": PRODUCT_ID,
"product_version": __version__,
diff --git a/src/vidxp/upload_service.py b/src/vidxp/upload_service.py
index dee90af1..dc5d21ff 100644
--- a/src/vidxp/upload_service.py
+++ b/src/vidxp/upload_service.py
@@ -384,7 +384,7 @@ def create_local_ingestion(
raise ApplicationError(
"local_ingestion_unavailable",
ErrorCategory.unavailable,
- "Local-path ingestion is available only to the local stdio server.",
+ "Local-path ingestion is available only to a local VidXP runtime.",
)
if self.media is None or self.jobs is None:
raise ApplicationError(
diff --git a/tests/test_api.py b/tests/test_api.py
index 1e510ff4..7bd6d735 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -40,6 +40,7 @@
IndexStatus,
MediaAsset,
MediaPage,
+ MediaUploadSessionStatus,
Principal,
SearchCommand,
SearchJobResult,
@@ -56,7 +57,11 @@
from vidxp.authorization import AuthorizationPolicy
from vidxp.core.media import MediaState, MediaStream
from vidxp.core.artifacts import ArtifactKind, ArtifactState
-from vidxp.core.uploads import UploadState
+from vidxp.core.uploads import (
+ UploadSessionState,
+ UploadState,
+ UploadTransferBackend,
+)
from vidxp.job_service import JobService
from vidxp.ports import LocalFileResource
from vidxp.readiness_service import ReadinessService
@@ -68,6 +73,7 @@
JOB_ID = "223456781234423481234567890abcde"
IDEMPOTENCY_KEY = "323456781234423481234567890abcde"
ARTIFACT_ID = "423456781234423481234567890abcde"
+INGESTION_ID = "523456781234423481234567890abcde"
TOKEN = "a" * 32
@@ -104,6 +110,35 @@ def queued_job() -> Job:
)
+def ingestion_status() -> MediaUploadSessionStatus:
+ now = datetime.now(timezone.utc)
+ return MediaUploadSessionStatus(
+ session_id=INGESTION_ID,
+ session_state=UploadSessionState.closed,
+ aggregate_state="processing",
+ transfer_backend=UploadTransferBackend.local_path,
+ resumable=False,
+ index_after_import=True,
+ index_modalities=("scene", "dialogue"),
+ expires_at=now.replace(year=now.year + 1),
+ maximum_files=10,
+ maximum_file_bytes=50 * 1024 * 1024 * 1024,
+ maximum_aggregate_bytes=50 * 1024 * 1024 * 1024,
+ file_count=2,
+ total_bytes=20,
+ reserved_file_count=2,
+ reserved_bytes=20,
+ uploaded_file_count=2,
+ uploaded_bytes=20,
+ ready_file_count=0,
+ searchable_file_count=0,
+ failed_file_count=0,
+ index_failed_file_count=0,
+ status="VidXP is importing the selected Premiere media.",
+ next_action="Poll this ingestion for progress.",
+ )
+
+
def evidence_job() -> Job:
artifact = Artifact(
artifact_id=ARTIFACT_ID,
@@ -171,6 +206,7 @@ def context(
mcp_limit: int = 4 * 1024 * 1024,
allowed_origins: tuple[str, ...] = (),
remote_uploads: bool = False,
+ bind_host: str = "127.0.0.1",
) -> HttpApplicationContext:
settings = VidXPSettings(
repository_root=root,
@@ -206,6 +242,7 @@ def context(
http_max_json_body_bytes=json_limit,
mcp_max_request_body_bytes=mcp_limit,
http_allowed_origins=allowed_origins,
+ http_bind_host=bind_host,
upload_public_endpoint=(
"http://localhost:8080/uploads/"
if remote_uploads
@@ -423,6 +460,110 @@ def test_workspace_passes_filters_to_application(self):
self.assertEqual(command.filename, "batch")
self.assertEqual(command.state, MediaState.pending)
+ def test_local_ingestion_api_delegates_to_durable_batch_workflow(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(Path(directory), remote_uploads=True)
+ status = ingestion_status()
+ context.application.select_index_modalities.return_value = (
+ "scene",
+ "dialogue",
+ )
+ assert context.uploads is not None
+ context.uploads.create_local_ingestion.return_value = status
+ context.uploads.get_status.return_value = status
+ with TestClient(create_app(context=context)) as client:
+ created = client.post(
+ "/api/v1/media/local-ingestions",
+ headers={"Idempotency-Key": IDEMPOTENCY_KEY},
+ json={
+ "paths": [
+ "C:/Premiere/interview.mp4",
+ "C:/Premiere/b-roll.mov",
+ ],
+ "index_after_import": True,
+ "modalities": ["scene", "dialogue"],
+ },
+ )
+ fetched = client.get(
+ f"/api/v1/media/local-ingestions/{INGESTION_ID}"
+ )
+
+ self.assertEqual(created.status_code, 202)
+ self.assertEqual(
+ created.headers["location"],
+ f"/api/v1/media/local-ingestions/{INGESTION_ID}",
+ )
+ self.assertEqual(created.json()["transfer_backend"], "local_path")
+ self.assertEqual(fetched.status_code, 200)
+ context.application.select_index_modalities.assert_called_once_with(
+ ("scene", "dialogue")
+ )
+ call = context.uploads.create_local_ingestion.call_args
+ self.assertEqual(
+ call.args[0],
+ (
+ "C:/Premiere/interview.mp4",
+ "C:/Premiere/b-roll.mov",
+ ),
+ )
+ self.assertTrue(call.kwargs["index_after_import"])
+ self.assertEqual(
+ call.kwargs["index_modalities"],
+ ("scene", "dialogue"),
+ )
+ context.uploads.get_status.assert_called_once_with(
+ INGESTION_ID,
+ principal=context.authenticator.authenticate(None),
+ )
+
+ def test_local_ingestion_api_reports_an_unconfigured_runtime(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(Path(directory))
+ with TestClient(create_app(context=context)) as client:
+ response = client.post(
+ "/api/v1/media/local-ingestions",
+ headers={"Idempotency-Key": IDEMPOTENCY_KEY},
+ json={
+ "paths": ["C:/Premiere/interview.mp4"],
+ "modalities": ["scene"],
+ },
+ )
+
+ self.assertEqual(response.status_code, 503)
+ self.assertEqual(
+ response.json()["error"]["code"],
+ "local_ingestion_unavailable",
+ )
+
+ def test_local_ingestion_api_is_not_exposed_by_a_shared_server(self):
+ with TemporaryDirectory() as directory:
+ context = self.context(
+ Path(directory),
+ auth=HttpAuthMode.static,
+ remote_uploads=True,
+ bind_host="0.0.0.0",
+ )
+ with TestClient(create_app(context=context)) as client:
+ response = client.post(
+ "/api/v1/media/local-ingestions",
+ headers={
+ **self.auth(),
+ "Idempotency-Key": IDEMPOTENCY_KEY,
+ },
+ json={
+ "paths": ["C:/Premiere/interview.mp4"],
+ "modalities": ["scene"],
+ },
+ )
+
+ self.assertEqual(response.status_code, 503)
+ self.assertEqual(
+ response.json()["error"]["code"],
+ "local_ingestion_unavailable",
+ )
+ assert context.uploads is not None
+ context.uploads.create_local_ingestion.assert_not_called()
+
def test_repository_scopes_are_enforced_per_operation(self):
with TemporaryDirectory() as directory:
context = self.context(
diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py
index dc7a10cc..29d45659 100644
--- a/tests/test_capabilities.py
+++ b/tests/test_capabilities.py
@@ -81,6 +81,19 @@ def test_registry_drives_capability_metadata(self):
"videoprism": "videoprism",
},
)
+ self.assertEqual(
+ tuple(
+ capability.label
+ for capability in CapabilityService(self.registry).list()
+ ),
+ (
+ "Dialogue search",
+ "Sound event search",
+ "Visual scene search",
+ "Actor recognition",
+ "Temporal video search",
+ ),
+ )
def test_registered_operations_are_schema_only_metadata(self):
self.assertNotIn("handler", OperationDefinition.model_fields)
diff --git a/tests/test_control_plane.py b/tests/test_control_plane.py
index 446f57b9..93a46345 100644
--- a/tests/test_control_plane.py
+++ b/tests/test_control_plane.py
@@ -52,6 +52,30 @@ def media_asset(media_id: str, filename: str) -> MediaAsset:
class ControlPlaneWorkspaceTests(unittest.TestCase):
+ def test_select_index_modalities_defaults_and_rejects_non_indexable_names(self):
+ with TemporaryDirectory() as directory:
+ root = Path(directory)
+ application = ControlPlaneApplication(
+ layout=RepositoryLayout(root=root),
+ capabilities=CapabilityService(create_capability_registry()),
+ media=Mock(),
+ artifacts=Mock(),
+ index_status=lambda: None,
+ model_cache=root / "models",
+ )
+
+ defaults = application.select_index_modalities(None)
+ selected = application.select_index_modalities(("scene", "sound"))
+ with self.assertRaises(ApplicationError) as raised:
+ application.select_index_modalities(("query",))
+
+ self.assertEqual(
+ defaults,
+ ("dialogue", "sound", "scene", "actor", "videoprism"),
+ )
+ self.assertEqual(selected, ("scene", "sound"))
+ self.assertEqual(raised.exception.detail.code, "invalid_request")
+
def test_index_preflight_rejects_unknown_capability_with_next_action(self):
with TemporaryDirectory() as directory:
root = Path(directory)
diff --git a/tests/test_local_probe.py b/tests/test_local_probe.py
index 8b23d90f..e7bd6364 100644
--- a/tests/test_local_probe.py
+++ b/tests/test_local_probe.py
@@ -14,6 +14,7 @@
DESKTOP_LAUNCH_PROTOCOL_VERSION,
PRODUCT_ID,
build_desktop_probe,
+ desktop_capability_catalog,
desktop_model_cache_catalog,
_resolved_launcher_path,
)
@@ -37,7 +38,13 @@ def build(self, **overrides):
patch("vidxp.local_probe._module_available", return_value=True),
patch(
"vidxp.local_probe._installed_search_capabilities",
- return_value=["actor", "dialogue", "scene", "videoprism"],
+ return_value=[
+ "actor",
+ "dialogue",
+ "scene",
+ "sound",
+ "videoprism",
+ ],
),
patch(
"vidxp.local_probe.media_runtime_is_initialized",
@@ -85,7 +92,7 @@ def test_probe_reports_stable_identity_and_contract_compatibility(self):
)
self.assertEqual(
payload["search_capabilities"],
- ["actor", "dialogue", "scene", "videoprism"],
+ ["actor", "dialogue", "scene", "sound", "videoprism"],
)
self.assertTrue(all(surface["launchable"] for surface in payload["surfaces"].values()))
@@ -100,6 +107,17 @@ def test_differing_package_versions_remain_contract_compatible(self):
"contract_compatible",
)
+ def test_desktop_capability_catalog_comes_from_model_contracts(self):
+ catalog = desktop_capability_catalog()
+
+ sound = catalog["capabilities"]["sound"]
+ self.assertEqual(sound["label"], "Sound event search")
+ self.assertEqual(sound["extra"], "sound")
+ self.assertEqual(
+ sum(model["download_size_bytes"] for model in sound["models"]),
+ 981_760_363,
+ )
+
def test_missing_optional_frontend_does_not_make_product_incompatible(self):
with (
patch("vidxp.local_probe.__version__", "0.4.0b0"),
diff --git a/tests/test_packaging.py b/tests/test_packaging.py
index c2de7f50..bbf2df44 100644
--- a/tests/test_packaging.py
+++ b/tests/test_packaging.py
@@ -674,7 +674,14 @@ def test_desktop_manifest_matches_published_package_contract(self):
self.assertFalse(manifest["surfaces"]["server"]["default"])
for surface in manifest["surfaces"].values():
self.assertIn(surface["extra"], dynamic_extras)
- for capability in manifest["capabilities"].values():
+ capability_catalog = json.loads(
+ (ROOT / "desktop" / "capability-catalog.json").read_text(
+ encoding="utf-8"
+ )
+ )
+ self.assertEqual(capability_catalog["schema_version"], 1)
+ self.assertNotIn("capabilities", manifest)
+ for capability in capability_catalog["capabilities"].values():
self.assertIn(capability["extra"], dynamic_extras)
self.assertEqual(
manifest["media_runtime"]["strategy"],
From 4e49514b332b8a503ee29ac4c69df98fde19786a Mon Sep 17 00:00:00 2001
From: "Saad A. Bazaz"
Date: Sun, 30 Aug 2026 00:07:49 +0500
Subject: [PATCH 4/9] Add glama.json configuration file (#134)
* Add glama.json configuration file
* Add 'tulayha' to maintainers list in glama.json
---
glama.json | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 glama.json
diff --git a/glama.json b/glama.json
new file mode 100644
index 00000000..4e20aaf5
--- /dev/null
+++ b/glama.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
+ "maintainers": [
+ "SaadBazaz",
+ "tulayha"
+ ]
+}
From 10237cc50c31255665bbead0b8d74a1082d6dcb0 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Mon, 31 Aug 2026 22:38:10 +0500
Subject: [PATCH 5/9] feat!: align media capabilities and add grounded-query
tooling (#135)
* feat(benchmarks): add Codex MCP ablation harness
* feat!: rename public media capabilities
Replace the dialogue and videoprism capability identifiers with speech and action across shared contracts, packages, extras, adapters, generated catalogs, benchmarks, and documentation. Bump the index schema so repositories rebuild under the new collection names.
BREAKING CHANGE: dialogue and videoprism are removed as capability names and package extras. Use speech and action, then rebuild existing indexes.
* test(desktop): fix renamed capability ordering
* feat(query): select Qwen3.5 4B local default
* feat(desktop): manage local grounded answers
* feat(premiere): add grounded answers
---
.github/ISSUE_TEMPLATE/bug-report.yml | 4 +-
.github/ISSUE_TEMPLATE/feature-request.yml | 4 +-
.github/release-intro.md | 2 +-
.gitignore | 3 +
INSTALLATION_GUIDE.md | 45 +-
README.md | 12 +-
benchmarks/codex-mcp/.npmrc | 1 +
benchmarks/codex-mcp/package-lock.json | 14624 ++++++++++++++++
benchmarks/codex-mcp/package.json | 20 +
benchmarks/codex-mcp/promptfooconfig.yaml | 134 +
.../codex-mcp/prompts/video-evidence.txt | 13 +
benchmarks/codex-mcp/scripts/preflight.mjs | 80 +
.../codex-mcp/tasks/longvale-part9-pilot.json | 122 +
desktop/capability-catalog.json | 20 +-
desktop/runtime-manifest.json | 7 +
desktop/src-tauri/Cargo.lock | 3 +
desktop/src-tauri/Cargo.toml | 1 +
desktop/src-tauri/src/lib.rs | 602 +-
desktop/src-tauri/src/query_setup.rs | 170 +
desktop/src-tauri/src/target_profiles.rs | 2 +-
desktop/src/App.test.tsx | 23 +-
desktop/src/components/ManagedSetup.tsx | 46 +-
desktop/src/tauri.test.ts | 1 +
desktop/src/tauri.ts | 10 +
docs/adding-a-capability.md | 2 +-
docs/architecture/platform.md | 35 +-
docs/benchmarking/README.md | 2 +
docs/benchmarking/adapter_validation.md | 8 +-
docs/benchmarking/agent_ablation.md | 274 +
docs/benchmarking/benchmark_catalog.md | 11 +-
docs/benchmarking/core_contract.md | 10 +-
docs/benchmarking/direction.md | 2 +-
docs/benchmarking/execution_readiness.md | 4 +-
docs/benchmarking/model_selection.md | 13 +-
docs/benchmarking/results.md | 8 +-
docs/benchmarking/runtime_validation.md | 6 +-
docs/benchmarking_research.md | 4 +-
docs/deployment/coolify.md | 22 +-
docs/deployment/gpu-evaluation.md | 6 +-
docs/integrations/openai-plugin.md | 6 +-
docs/integrations/premiere-pro.md | 18 +-
docs/local-api.md | 59 +-
.../vidxp/skills/vidxp-ingest-video/SKILL.md | 4 +-
premiere/README.md | 4 +-
premiere/docs/MANUAL_TEST_CHECKLIST.md | 19 +-
premiere/src/services/vidxp/client.ts | 34 +-
premiere/src/services/vidxp/types.ts | 46 +-
premiere/src/ui/App.tsx | 121 +-
premiere/src/ui/styles.css | 66 +
premiere/tests/app-query.test.tsx | 58 +
premiere/tests/vidxp-client.test.ts | 65 +-
premiere/vite.config.ts | 2 +-
pyproject.toml | 22 +-
src/vidxp/benchmarks/agent_ablation_score.py | 168 +
src/vidxp/benchmarks/agent_ablation_tests.py | 124 +
src/vidxp/benchmarks/cli.py | 2 +-
src/vidxp/benchmarks/hirest.py | 12 +-
src/vidxp/capabilities/action/__init__.py | 1 +
.../{videoprism => action}/config.py | 2 +-
.../{videoprism => action}/definition.py | 22 +-
.../{videoprism => action}/indexing.py | 12 +-
.../{videoprism => action}/models.py | 4 +-
.../{videoprism => action}/operations.py | 4 +-
.../{videoprism => action}/requirements.txt | 0
.../{videoprism => action}/specs.py | 2 +-
src/vidxp/capabilities/dialogue/__init__.py | 1 -
src/vidxp/capabilities/registry.py | 6 +-
src/vidxp/capabilities/speech/__init__.py | 1 +
.../{dialogue => speech}/config.py | 6 +-
.../{dialogue => speech}/definition.py | 32 +-
.../{dialogue => speech}/indexing.py | 38 +-
.../{dialogue => speech}/models.py | 6 +-
.../{dialogue => speech}/operations.py | 26 +-
.../{dialogue => speech}/requirements.txt | 0
.../{dialogue => speech}/specs.py | 4 +-
src/vidxp/capabilities/videoprism/__init__.py | 1 -
src/vidxp/codex_plugin.py | 6 +
src/vidxp/core/contracts.py | 4 +-
src/vidxp/frontend.py | 16 +-
src/vidxp/infrastructure/ollama_query.py | 2 +
src/vidxp/mcp_cli.py | 18 +-
src/vidxp/runtime.py | 2 +-
src/vidxp/settings.py | 7 +-
tests/test_agent_ablation.py | 158 +
tests/test_api.py | 16 +-
tests/test_application.py | 10 +-
tests/test_benchmark_cli.py | 4 +-
tests/test_benchmarks.py | 2 +-
tests/test_capabilities.py | 37 +-
tests/test_cli.py | 12 +-
tests/test_codex_plugin.py | 36 +
tests/test_contracts.py | 8 +-
tests/test_control_plane.py | 2 +-
tests/test_frontend.py | 18 +-
tests/test_frontend_app.py | 10 +-
tests/test_generation_manifest.py | 8 +-
tests/test_indexing.py | 16 +-
tests/test_job_contracts.py | 4 +-
tests/test_local_probe.py | 6 +-
tests/test_mcp.py | 27 +-
tests/test_models.py | 42 +-
tests/test_ollama_query.py | 9 +-
tests/test_public_path_contracts.py | 2 +-
tests/test_query_service.py | 28 +-
tests/test_runner.py | 20 +-
tests/test_search.py | 32 +-
tests/test_search_fusion.py | 12 +-
tests/test_snapshots.py | 8 +-
tests/test_storage.py | 2 +-
tests/test_upload_service.py | 6 +-
tests/test_videoprism.py | 12 +-
utils/verify_runtime.py | 2 +-
uv.lock | 72 +-
113 files changed, 17547 insertions(+), 483 deletions(-)
create mode 100644 benchmarks/codex-mcp/.npmrc
create mode 100644 benchmarks/codex-mcp/package-lock.json
create mode 100644 benchmarks/codex-mcp/package.json
create mode 100644 benchmarks/codex-mcp/promptfooconfig.yaml
create mode 100644 benchmarks/codex-mcp/prompts/video-evidence.txt
create mode 100644 benchmarks/codex-mcp/scripts/preflight.mjs
create mode 100644 benchmarks/codex-mcp/tasks/longvale-part9-pilot.json
create mode 100644 desktop/src-tauri/src/query_setup.rs
create mode 100644 docs/benchmarking/agent_ablation.md
create mode 100644 premiere/tests/app-query.test.tsx
create mode 100644 src/vidxp/benchmarks/agent_ablation_score.py
create mode 100644 src/vidxp/benchmarks/agent_ablation_tests.py
create mode 100644 src/vidxp/capabilities/action/__init__.py
rename src/vidxp/capabilities/{videoprism => action}/config.py (82%)
rename src/vidxp/capabilities/{videoprism => action}/definition.py (81%)
rename src/vidxp/capabilities/{videoprism => action}/indexing.py (95%)
rename src/vidxp/capabilities/{videoprism => action}/models.py (94%)
rename src/vidxp/capabilities/{videoprism => action}/operations.py (97%)
rename src/vidxp/capabilities/{videoprism => action}/requirements.txt (100%)
rename src/vidxp/capabilities/{videoprism => action}/specs.py (93%)
delete mode 100644 src/vidxp/capabilities/dialogue/__init__.py
create mode 100644 src/vidxp/capabilities/speech/__init__.py
rename src/vidxp/capabilities/{dialogue => speech}/config.py (68%)
rename src/vidxp/capabilities/{dialogue => speech}/definition.py (83%)
rename src/vidxp/capabilities/{dialogue => speech}/indexing.py (90%)
rename src/vidxp/capabilities/{dialogue => speech}/models.py (94%)
rename src/vidxp/capabilities/{dialogue => speech}/operations.py (82%)
rename src/vidxp/capabilities/{dialogue => speech}/requirements.txt (100%)
rename src/vidxp/capabilities/{dialogue => speech}/specs.py (92%)
delete mode 100644 src/vidxp/capabilities/videoprism/__init__.py
create mode 100644 tests/test_agent_ablation.py
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index a509fb94..cb0f6a2c 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -25,9 +25,9 @@ body:
attributes:
label: Affected capability
options:
- - Dialogue search
+ - Speech search
- Scene search
- - Action search (videoprism)
+ - Action and motion search
- Actor matching
- Multiple capabilities
- Not capability-specific or unsure
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
index b28725f1..4e16dd14 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -25,9 +25,9 @@ body:
attributes:
label: Related capability
options:
- - Dialogue search
+ - Speech search
- Scene search
- - Action search (videoprism)
+ - Action and motion search
- Actor matching
- Multiple capabilities
- Not capability-specific or unsure
diff --git a/.github/release-intro.md b/.github/release-intro.md
index 94726190..db137973 100644
--- a/.github/release-intro.md
+++ b/.github/release-intro.md
@@ -1,6 +1,6 @@
## Download VidXP
-{release_notice}VidXP turns video into searchable dialogue, sounds, scenes, actions, people, and
+{release_notice}VidXP turns video into searchable speech, sounds, scenes, actions, people, and
inspectable evidence. Choose the desktop app for the guided local setup, or use
the Python package and containers for command-line and server deployments.
diff --git a/.gitignore b/.gitignore
index 5cb56467..f4b94a05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
/venv
/chroma_data
/benchmark_runs/
+/benchmarks/codex-mcp/.promptfoo/
+/benchmarks/codex-mcp/results/
+/benchmarks/codex-mcp/workspace/
/audio.wav
/video.mp4
/dist/
diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md
index 7ae4036b..8e76da6f 100644
--- a/INSTALLATION_GUIDE.md
+++ b/INSTALLATION_GUIDE.md
@@ -33,7 +33,7 @@ Approximate model downloads are:
| Feature | Download |
|---|---:|
-| Dialogue search | 2.63 GiB |
+| Speech search | 2.63 GiB |
| Sound event search | 0.91 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
@@ -200,8 +200,8 @@ asking for confirmation. Download only selected features when preferred:
```bash
vidxp prepare --modalities scene
-vidxp prepare --modalities dialogue,actor
-vidxp prepare --modalities videoprism # action search
+vidxp prepare --modalities speech,actor
+vidxp prepare --modalities action
vidxp prepare --modalities sound # music and environmental sounds
```
@@ -232,14 +232,14 @@ vidxp index create
# Find a visual scene
vidxp search scene "a yellow taxi on a city street"
-# Find an action or event (`videoprism` is the CLI name for action search)
-vidxp search videoprism "a person opens a door and walks outside"
+# Find an action or motion across multiple frames
+vidxp search action "a person opens a door and walks outside"
# Find music or an environmental sound
vidxp search sound "an alarm ringing"
# Find something that was said
-vidxp search dialogue "the bread just came out of the oven"
+vidxp search speech "the bread just came out of the oven"
```
Add `--media-id ` to a search command to restrict results to one
@@ -313,6 +313,30 @@ clients, or public access, use the supported server deployment instead.
See [Local API and MCP server](docs/local-api.md) for authentication, uploads,
and sharing behavior.
+## Optional local grounded answers
+
+VidXP search does not require a language model. To let CLI, HTTP, or MCP
+queries plan searches and draft grounded answers locally, enable **Local
+grounded answers** in VidXP Desktop setup. Desktop checks for a compatible
+loopback Ollama service, asks before installing Ollama through the supported
+Windows or macOS package manager when needed, and explicitly downloads the
+approved Qwen3.5 4B model. Linux setup links to Ollama's official installation
+instructions instead of running a privileged script.
+
+This optional feature follows Ollama's platform floor: Windows 10 22H2 or
+newer, or macOS 14 or newer. VidXP Desktop itself can still run without local
+grounded answers on older supported systems.
+
+The model is an additional approximately 3.4 GB download and has no per-run
+API charge or numbered hosted-model allowance. It uses local storage, memory,
+compute time, and electricity. Desktop configures the private service address
+for its browser, worker, API, Premiere, and generated MCP/Codex setup; there is
+no URL field to fill in. A command-line-only installation remains available
+for developers and custom deployments.
+
+The complete setup and its current evidence limitations are documented under
+[Enable local grounded answers](docs/local-api.md#enable-local-grounded-answers).
+
## Optional dependency extras
Most users should choose one of the package profiles above. The individual
@@ -321,10 +345,10 @@ assembling a custom installation:
| Extra | Adds |
|---|---|
-| `dialogue` | Transcription, dialogue embeddings, and storage |
+| `speech` | Transcription, speech embeddings, and storage |
| `sound` | Music and environmental-sound search and storage |
| `scene` | Scene search and storage |
-| `videoprism` | Action search and storage |
+| `action` | Multi-frame action and motion search plus storage |
| `actor` | Actor matching and storage |
| `all` | Every built-in search feature |
| `local-worker` | All search features plus local job processing |
@@ -404,6 +428,11 @@ to be indexed again. VidXP reports this instead of silently replacing a working
index. Prepare the required models, re-index the affected videos, and keep the
old repository until you have checked the replacement results.
+The current public capability names are `scene`, `action`, `sound`, `speech`,
+and `actor`. VidXP does not translate removed capability names.
+If an older repository reports an incompatible index schema, rebuild it using
+the current names.
+
## Data locations
VidXP stores data outside the current working directory:
diff --git a/README.md b/README.md
index 3c199746..a180c453 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
- Dialogue search · Sound search · Scene search · Action search · Actor grouping
+ Speech search · Sound search · Scene search · Action search · Actor grouping
@@ -39,7 +39,7 @@
VidXP makes one video—or an entire collection—searchable by meaning:
-- **Dialogue search:** type what you remember someone saying and jump to the
+- **Speech search:** type what you remember someone saying and jump to the
matching moments.
- **Scene search:** describe what appeared on screen and find the closest
visual matches.
@@ -122,7 +122,7 @@ See the [Coolify guide](docs/deployment/coolify.md) for the complete setup.
## What you can do today
- Build searchable libraries from individual videos or whole collections.
-- Find dialogue, sound events, visual scenes, and multi-frame actions by description.
+- Find speech, sound events, visual scenes, and multi-frame actions by description.
- Ask grounded questions and inspect the supporting boards, frames, or clips.
- Group recurring faces and render highlighted actor overlays.
- Keep personal, client, or project libraries separate.
@@ -144,13 +144,13 @@ vidxp index create
vidxp search scene "a yellow taxi on a city street"
# Find an action or event
-vidxp search videoprism "a person opens a door and walks outside"
+vidxp search action "a person opens a door and walks outside"
# Find a sound event
vidxp search sound "a dog barking over traffic noise"
# Find something that was said
-vidxp search dialogue "the bread just came out of the oven"
+vidxp search speech "the bread just came out of the oven"
```
Results include the source video, timestamps, match score, and the evidence
@@ -208,7 +208,7 @@ approximately 3 GiB.
| Capability | Approximate model download |
|---|---:|
-| Dialogue search | 2.64 GiB |
+| Speech search | 2.64 GiB |
| Sound event search | 0.94 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
diff --git a/benchmarks/codex-mcp/.npmrc b/benchmarks/codex-mcp/.npmrc
new file mode 100644
index 00000000..b6f27f13
--- /dev/null
+++ b/benchmarks/codex-mcp/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/benchmarks/codex-mcp/package-lock.json b/benchmarks/codex-mcp/package-lock.json
new file mode 100644
index 00000000..a9d850ad
--- /dev/null
+++ b/benchmarks/codex-mcp/package-lock.json
@@ -0,0 +1,14624 @@
+{
+ "name": "vidxp-codex-mcp-eval",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "vidxp-codex-mcp-eval",
+ "version": "0.0.0",
+ "devDependencies": {
+ "@openai/codex-sdk": "0.151.0",
+ "promptfoo": "0.122.2"
+ },
+ "engines": {
+ "node": ">=22.22.0"
+ }
+ },
+ "node_modules/@ai-sdk/gateway": {
+ "version": "3.0.184",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.184.tgz",
+ "integrity": "sha512-7SuKsT4RS9lm6ShODj0vzS+S1ZKw5GukA7x3gg3t/gVwk0bu7l3XXC3sdEdBsBlTP97KklMYdZHmjcJoOVt67Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.15",
+ "@ai-sdk/provider-utils": "4.0.49",
+ "@vercel/oidc": "3.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/provider": {
+ "version": "3.0.15",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.15.tgz",
+ "integrity": "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "json-schema": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils": {
+ "version": "4.0.49",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.49.tgz",
+ "integrity": "sha512-8e7pd+82bobqrFOaD5dG/PiEuvLYr5olaE3I56ch0jipR0H7sGD6ohwTUynv6k8O8QidWiyIbEsZCtr/2dyXIA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.15",
+ "@standard-schema/spec": "^1.1.0",
+ "eventsource-parser": "^3.0.8",
+ "undici": "^6.28.0"
+ },
+ "engines": {
+ "node": ">=18.17"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils/node_modules/eventsource-parser": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz",
+ "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils/node_modules/undici": {
+ "version": "6.28.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz",
+ "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/@alcalzone/ansi-tokenize": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.5.tgz",
+ "integrity": "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.234.tgz",
+ "integrity": "sha512-988d+JfICQoIIDwcnQm9ivJ3CXfKUhDJ43XSQXiwa4PMnc/+NwAK71JUnOipXgGJNVu+znfzk42CV+wUeX25dg==",
+ "dev": true,
+ "license": "SEE LICENSE IN README.md",
+ "optional": true,
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.234",
+ "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.234"
+ },
+ "peerDependencies": {
+ "@anthropic-ai/sdk": ">=0.93.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
+ "zod": "^4.0.0"
+ }
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.234.tgz",
+ "integrity": "sha512-6BJAbSOD5yGOzn1hU62ZfpybZhPlxcAe0r1w6qeGkAi/W3MD3Wl/TuVZ/y9/xvAwtfDmADDfbcHO0v6i0rjNIw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.234.tgz",
+ "integrity": "sha512-ixb6ta76uNqau9+Qj1TCdy/PbQ0hUF7XH1+hNhGGJueAAROkxt+LL+WiT+6LtYs2Cuyu6Wl+e7ZzVjzf2P48uQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.234.tgz",
+ "integrity": "sha512-mWDXYZ5qe7zUK82ssBlsdIRMTBQdt11Kv9VvDVMxgXgSo1OxwZzb5Ukq2Xws3IvZGbJFOsYoxet820NGv91UZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.234.tgz",
+ "integrity": "sha512-VEvlLX6VTX221HzW2LP3sz7H+Ip+YKmfGbXp5UxfviQzvxrMfsAeKBmM9NJrAq/2UEqzLL6tEteIYF/aYW5cpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.234.tgz",
+ "integrity": "sha512-RAyjcz9IsSvooWxJWoM8nBuRtzAzuRqLVXk5/zVNFE2snxXT1ZayZjpITHaHTh7SEsuMe1j5EbgzvL3AAy4zQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.234.tgz",
+ "integrity": "sha512-329GhW2Y+imBzl+Ian0wr2dJalzyp2JnpBZvSgQg8vf4drBTcCBCIr6ofdIL/6edM/l9hG6aKFrw8XgquqqFfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.234.tgz",
+ "integrity": "sha512-X/3baEzmSOhy36YM+V5D6UBTwXQOWxnHD1xiBkaRIhpL3jmCNc7KKeWy0FZEnKjINL75VHvZR7myRnQjPEiBHw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
+ "version": "0.3.234",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.234.tgz",
+ "integrity": "sha512-YHprZwgq6jkf0pjOF6KU2A+7tY7NqeH4kZRwKK6TvzHQ6yVOMMFr3ebwmP4WiTD2n+I0fLhpOAf8C/Yvz5Ktpg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@anthropic-ai/sdk": {
+ "version": "0.117.1",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.117.1.tgz",
+ "integrity": "sha512-Yn2QlXfyCiKJ5YGCOOay7ZE78ISvII2XY621WMCiflmG8IYgwx59IBwPExxki3Xk9jKUtnD/Sj6UvplWr0rZxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-schema-to-ts": "^3.1.1",
+ "standardwebhooks": "^1.0.0"
+ },
+ "bin": {
+ "anthropic-ai-sdk": "bin/cli"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@apidevtools/json-schema-ref-parser": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-16.0.1.tgz",
+ "integrity": "sha512-JmQn9gXVZ83h3IgC+xXVmR4CXWbX1TTyQBj+q4d8BeJqDWSSD/Q3GbkrjhAbXCZxtjnlljOjyy0vio14vQjJtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-yaml": "^5.2.3",
+ "undici": "^8.10.0"
+ },
+ "engines": {
+ "node": ">=22.19.0"
+ },
+ "peerDependencies": {
+ "@types/json-schema": "^7.0.15"
+ }
+ },
+ "node_modules/@apidevtools/json-schema-ref-parser/node_modules/undici": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz",
+ "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/@aws-sdk/checksums": {
+ "version": "3.1000.29",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.29.tgz",
+ "integrity": "sha512-Dtu0gr4dnATZAPwEYbpCsG+MpLM7OAliy2gTepEFQwl1vZ6DL3QMH2FveMa3HLvPsOdhJsPRB3KtxVhph9T75A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-bedrock-agent-runtime": {
+ "version": "3.1121.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-agent-runtime/-/client-bedrock-agent-runtime-3.1121.0.tgz",
+ "integrity": "sha512-adxnRQCZvTKukgnpS4kTcOq2eG0xakt9cqORoVIhlVxMb8Omx06k10UYwXIuiKJshlIvKH00sgWXZ9JNZqSS/w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-node": "^3.972.81",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-bedrock-runtime": {
+ "version": "3.1121.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1121.0.tgz",
+ "integrity": "sha512-fSMUxttOVPfPkIDrrPO1IqDVK83/0yAKUYweEYsR8IayFRB2z/yvdGzTom7ZtgKH+97j8JKHSYIqM2t8qEZpSA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-node": "^3.972.81",
+ "@aws-sdk/eventstream-handler-node": "^3.972.34",
+ "@aws-sdk/middleware-eventstream": "^3.972.29",
+ "@aws-sdk/middleware-websocket": "^3.972.52",
+ "@aws-sdk/token-providers": "3.1121.0",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3": {
+ "version": "3.1121.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1121.0.tgz",
+ "integrity": "sha512-hBnoqaVBeWdkgXcJElMXA2yUZWkBCBntu2qmN+tfqmzC+j4LzJC3ox8qIgS2WdMS1cb8UwyBogUVrkRXybNm0A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/checksums": "^3.1000.29",
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-node": "^3.972.81",
+ "@aws-sdk/middleware-sdk-s3": "^3.972.75",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.46",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-sagemaker-runtime": {
+ "version": "3.1121.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker-runtime/-/client-sagemaker-runtime-3.1121.0.tgz",
+ "integrity": "sha512-TgRXQlsh39yznhgCnk5ERoNIGzav6ttN+24QEO6sYWp9okgchqF8Hybg3H5Pg6YmzBcpJgvzwP7zmzc6U4nqAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-node": "^3.972.81",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/core": {
+ "version": "3.977.9",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.9.tgz",
+ "integrity": "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.5",
+ "@aws-sdk/xml-builder": "^3.972.40",
+ "@aws/lambda-invoke-store": "^0.3.0",
+ "@smithy/core": "^3.33.3",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.972.70",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.70.tgz",
+ "integrity": "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.972.72",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.72.tgz",
+ "integrity": "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.973.15",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.15.tgz",
+ "integrity": "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-env": "^3.972.70",
+ "@aws-sdk/credential-provider-http": "^3.972.72",
+ "@aws-sdk/credential-provider-login": "^3.972.77",
+ "@aws-sdk/credential-provider-process": "^3.972.70",
+ "@aws-sdk/credential-provider-sso": "^3.973.14",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.76",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/credential-provider-imds": "^4.4.16",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.972.77",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.77.tgz",
+ "integrity": "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.972.81",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.81.tgz",
+ "integrity": "sha512-Rml+WitoFvXmv6JZ18U/xGdGDGGvB/mOin0ya0lTnTrdC0Z1lrVxTYh7iNklZBcvcRMrs4DoEf6xy1KWyrLQQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "^3.972.70",
+ "@aws-sdk/credential-provider-http": "^3.972.72",
+ "@aws-sdk/credential-provider-ini": "^3.973.15",
+ "@aws-sdk/credential-provider-process": "^3.972.70",
+ "@aws-sdk/credential-provider-sso": "^3.973.14",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.76",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/credential-provider-imds": "^4.4.16",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.972.70",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.70.tgz",
+ "integrity": "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.973.14",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.14.tgz",
+ "integrity": "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/token-providers": "3.1116.0",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": {
+ "version": "3.1116.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1116.0.tgz",
+ "integrity": "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.972.76",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.76.tgz",
+ "integrity": "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/eventstream-handler-node": {
+ "version": "3.972.34",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.34.tgz",
+ "integrity": "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-eventstream": {
+ "version": "3.972.29",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.29.tgz",
+ "integrity": "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3": {
+ "version": "3.972.75",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.75.tgz",
+ "integrity": "sha512-wMIsNumRVKaNMKhvU/s9VrdEwE8S6gSzXp4RygFG5BEMnGkkXf8cjh8zf7cKJBpUDpqTWqwbz5isEgp9rH6Lng==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.46",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-websocket": {
+ "version": "3.972.52",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.52.tgz",
+ "integrity": "sha512-vsPPM+nMbKJlUCFU+eoGZbdxdxDIAX9LbpjSXaR5Ufpmqgp8TdYQnoExhLu4T3umW/JIIPny1ydbhWidZZYokQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients": {
+ "version": "3.997.44",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.44.tgz",
+ "integrity": "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.46",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.996.46",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz",
+ "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/token-providers": {
+ "version": "3.1121.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1121.0.tgz",
+ "integrity": "sha512-kyRVbJnFDDHDqPzgNzQdXyj2P3ANq2LQyg65o9IvQnuqiNrsP1zK+fDe5BzT0xKVnvAAGhmqzd5cvdzWP69Sfg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/types": {
+ "version": "3.974.5",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz",
+ "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.972.40",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz",
+ "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws/lambda-invoke-store": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz",
+ "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure-rest/core-client": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.8.0.tgz",
+ "integrity": "sha512-F1ybHeN+++QhyFCF/ehLUEvrOB6fehPdFBFtGdj0C3B2lpQ9zkPiO5JDgsqc6IfjuUe6b3dAbXK0a7+VgSGfhw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.24.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/abort-controller": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz",
+ "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/ai-projects": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@azure/ai-projects/-/ai-projects-2.5.0.tgz",
+ "integrity": "sha512-XbhpCp6w6IcuUwJFp4Va9evpV/HCCHxzLKnlJxbmeVIm3GD98uZLPLMuZu3fJvDWenULZ0Z+dcQXJQ3G5za+yw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure-rest/core-client": "^2.1.0",
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.6.0",
+ "@azure/core-lro": "^3.1.0",
+ "@azure/core-paging": "^1.5.0",
+ "@azure/core-rest-pipeline": "^1.5.0",
+ "@azure/core-sse": "^2.1.3",
+ "@azure/core-util": "^1.9.0",
+ "@azure/identity": "^4.13.0",
+ "@azure/logger": "^1.1.4",
+ "@azure/storage-blob": "^12.26.0",
+ "@opentelemetry/api": "^1.9.1",
+ "openai": "^6.16.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/ai-projects/node_modules/openai": {
+ "version": "6.49.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz",
+ "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peerDependencies": {
+ "@aws-sdk/credential-provider-node": ">=3.972.0 <4",
+ "@smithy/hash-node": ">=4.3.0 <5",
+ "@smithy/signature-v4": ">=5.4.0 <6",
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-provider-node": {
+ "optional": true
+ },
+ "@smithy/hash-node": {
+ "optional": true
+ },
+ "@smithy/signature-v4": {
+ "optional": true
+ },
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@azure/core-auth": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz",
+ "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-util": "^1.13.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-client": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.0.tgz",
+ "integrity": "sha512-JjQWO6akOck45PH/XBrxzsQGAiKrfFl4m5iggJ0ItMIz5omRufOXWpqCPpdjKN3vKDzlSUvFjaMb7Zwf0gvAdA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-http-compat": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.5.0.tgz",
+ "integrity": "sha512-BoSmXPx2er1Ai+wKlDvj29jIQespCNBwEmKyZVHO2kEFsWbGjAjwMCGzug3DJM5/QYIV3vej0S1zcU5bq9fa8w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ },
+ "peerDependencies": {
+ "@azure/core-client": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0"
+ }
+ },
+ "node_modules/@azure/core-lro": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-3.4.0.tgz",
+ "integrity": "sha512-y0uqcVFp5NHd7tkZcn8Nes6yIhVR05m4dd+L8foWiH1IsS75Z2BodJxwdErEF3bV+NSh6nkNnwPyXaLp0ma1Nw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-paging": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.7.0.tgz",
+ "integrity": "sha512-7GEAoIsaoBr6KELNRb8nypowCqvk8dnCHFCYg4XD4lOQGY2GqjQg5IhkRjyBFRO18CGSMq05PaNqSOE9GQro3g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-process": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz",
+ "integrity": "sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-rest-pipeline": {
+ "version": "1.25.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz",
+ "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
+ "@typespec/ts-http-runtime": "^0.3.4",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-sse": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-sse/-/core-sse-2.4.0.tgz",
+ "integrity": "sha512-BFNVsoYE843I/q5/OFNHpaYN8TK8W99OwU9ipToYXdwB14o92A16ZjD6JW/BZ8kO7fWSM4jK2EoojAQmXAOExw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-tracing": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz",
+ "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-util": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz",
+ "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/core-xml": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.6.0.tgz",
+ "integrity": "sha512-e7lX/dk//F6Qf7BB6PTY4+p2yuOQtyOeHGyapYHNwqSp2OnYpwQt49A/Nin2XmKBQ69pwagR4k/lQBq8lbHQkA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "fast-xml-parser": "^5.5.9",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/identity": {
+ "version": "4.13.2",
+ "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.2.tgz",
+ "integrity": "sha512-NXL2/pCJctLxgw8bvrwwgge743kEq8LBT+O1pmV0vyUwetzFPH9auP6jhkU/cgZCPPtWoewAe3ncaGCgPo07fA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.9.0",
+ "@azure/core-client": "^1.9.2",
+ "@azure/core-process": "^1.0.0",
+ "@azure/core-rest-pipeline": "^1.17.0",
+ "@azure/core-tracing": "^1.0.0",
+ "@azure/core-util": "^1.11.0",
+ "@azure/logger": "^1.0.0",
+ "@azure/msal-browser": "^5.5.0",
+ "@azure/msal-node": "^5.1.5",
+ "open": "^10.1.0",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/logger": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz",
+ "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@typespec/ts-http-runtime": "^0.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/msal-browser": {
+ "version": "5.20.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.20.0.tgz",
+ "integrity": "sha512-mtOKr708E/E/+qhI50QufmhR8GS5C84IeFGEaH/guMOaPXT9B/obEt8TVzX5U8j5OEUgukn0PrRmwVKaigr2wA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/msal-common": "16.14.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-common": {
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.0.tgz",
+ "integrity": "sha512-A4rb55hI86Q9tBl/+jBj7TMz7iX2RFgQs/nExFzcAtoI/BFRVdaH5SL/MivrYD7qvweMpN8AgVvVMHV8UBYxew==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-node": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.6.0.tgz",
+ "integrity": "sha512-uFY9NxrWHw8PwZx7gAX6PDn+9vdfS05+levc/kwkx77IkjfaldnQbbcQzzDIZ5Hq5Zdr6/z92oAIoRWKp6MnOA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/msal-common": "16.13.0",
+ "jsonwebtoken": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
+ "version": "16.13.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.13.0.tgz",
+ "integrity": "sha512-rOAy0KUcyBbdwVJ+f3uPpthXatFLLZN+/KWAsTLzk1aB23Xl9DRmmXYwSvBFOZyXj4jUQQ5FKxxRkhAFW1fOow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/openai-assistants": {
+ "version": "1.0.0-beta.6",
+ "resolved": "https://registry.npmjs.org/@azure/openai-assistants/-/openai-assistants-1.0.0-beta.6.tgz",
+ "integrity": "sha512-gINKKcqTpR0neF+36Owe0Q1u1JO3IK6clBzWTfZ+9V/TkQq+LoUgp5F8dKvSv/YChfwEpZA2r1DWCwNE07eYIQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure-rest/core-client": "^1.1.4",
+ "@azure/core-auth": "^1.5.0",
+ "@azure/core-client": "^1.7.3",
+ "@azure/core-rest-pipeline": "^1.13.0",
+ "@azure/core-util": "^1.6.1",
+ "@azure/logger": "^1.0.4",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/openai-assistants/node_modules/@azure-rest/core-client": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-1.4.0.tgz",
+ "integrity": "sha512-ozTDPBVUDR5eOnMIwhggbnVmOrka4fXCs8n8mvUo4WLLc38kki6bAOByDoVZZPz/pZy2jMt2kwfpvy/UjALj6w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-auth": "^1.3.0",
+ "@azure/core-rest-pipeline": "^1.5.0",
+ "@azure/core-tracing": "^1.0.1",
+ "@azure/core-util": "^1.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/storage-blob": {
+ "version": "12.33.0",
+ "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.33.0.tgz",
+ "integrity": "sha512-2SX8oP8PyblUcAFZSg39c8Ls+tFjavM6sBeV+qpw33mRzRhI/5hrFJmJ/x0H9xx5l6ECPvgSP8uPxqTeVbHNIA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.9.0",
+ "@azure/core-client": "^1.9.3",
+ "@azure/core-http-compat": "^2.2.0",
+ "@azure/core-lro": "^2.2.0",
+ "@azure/core-paging": "^1.6.2",
+ "@azure/core-rest-pipeline": "^1.19.1",
+ "@azure/core-tracing": "^1.2.0",
+ "@azure/core-util": "^1.11.0",
+ "@azure/core-xml": "^1.4.5",
+ "@azure/logger": "^1.1.4",
+ "@azure/storage-common": "^12.4.1",
+ "events": "^3.0.0",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@azure/storage-blob/node_modules/@azure/core-lro": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz",
+ "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.0.0",
+ "@azure/core-util": "^1.2.0",
+ "@azure/logger": "^1.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@azure/storage-common": {
+ "version": "12.5.0",
+ "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.5.0.tgz",
+ "integrity": "sha512-bttzuhQiCIwrkzjPDA+AtAR7dg19L/CC6ztcqJ5LfvWpXuys9mHp0UQ0udYnoUvv9SCT9KTR5kqFvFr0e6k0lQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.9.0",
+ "@azure/core-http-compat": "^2.4.0",
+ "@azure/core-rest-pipeline": "^1.24.0",
+ "@azure/core-tracing": "^1.2.0",
+ "@azure/core-util": "^1.11.0",
+ "@azure/logger": "^1.1.4",
+ "events": "^3.3.0",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@borewit/text-codec": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz",
+ "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/@cacheable/utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz",
+ "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hashery": "^1.5.1",
+ "keyv": "^5.6.0"
+ }
+ },
+ "node_modules/@cfworker/json-schema": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
+ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@dabh/diagnostics": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
+ "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@so-ric/colorspace": "^1.1.6",
+ "enabled": "2.0.x",
+ "kuler": "^2.0.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
+ "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@fal-ai/client": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@fal-ai/client/-/client-1.10.1.tgz",
+ "integrity": "sha512-c3AVeH31OioiI2J1BfW8Cryi1DhUYldnY3X35nv6xLMq3fU2NQOo+eYaR5mL2O8MoHHh+HzXdQuIyanIyeq+ug==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@msgpack/msgpack": "^3.0.0-beta2",
+ "eventsource-parser": "^1.1.2",
+ "robot3": "^0.4.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@googleapis/sheets": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@googleapis/sheets/-/sheets-14.0.0.tgz",
+ "integrity": "sha512-fANEl4RQohsPYUWhcLSYyUyE8A8bRfvw/bp8h0t8VDQqTgdQ3itZBkty4nddtdqCAvNDpA+KM66OejVKDd6aFg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "googleapis-common": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@graphql-typed-document-node/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@hono/node-server": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz",
+ "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "hono": "^4"
+ }
+ },
+ "node_modules/@huggingface/jinja": {
+ "version": "0.5.9",
+ "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.9.tgz",
+ "integrity": "sha512-uWTG+l3VJRsl7EXxYizuL3P+cCPoc3cRqbWWRcQN0FhejRfbdq0RNhCmbY/YDtnTcz9icdLYuLDjsnz4d8JMuw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@huggingface/tokenizers": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@huggingface/tokenizers/-/tokenizers-0.1.3.tgz",
+ "integrity": "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/@huggingface/transformers": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-4.2.0.tgz",
+ "integrity": "sha512-8BRCoBMH0XsWaEIamuR0LrJGAfftgHAfb2Vrffy0VKlSAE/MnUJ5/h/zTfEP3fDIft+nk7TqB8xXEyABGitBjQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@huggingface/jinja": "^0.5.6",
+ "@huggingface/tokenizers": "^0.1.3",
+ "onnxruntime-node": "1.24.3",
+ "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c",
+ "sharp": "^0.34.5"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@huggingface/transformers/node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/@ibm-cloud/watsonx-ai": {
+ "version": "1.7.16",
+ "resolved": "https://registry.npmjs.org/@ibm-cloud/watsonx-ai/-/watsonx-ai-1.7.16.tgz",
+ "integrity": "sha512-ks7EI3TlrnZ6uKEIGemLHiBqOaqrA2dALNhC9Potl5CeajGNhF0n6eY30cNRvboDbuMRibw1iAtn7vnX4dzlwg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "form-data": "^4.0.4",
+ "ibm-cloud-sdk-core": "^5.4.20"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz",
+ "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
+ "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-freebsd-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
+ "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz",
+ "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
+ "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
+ "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
+ "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
+ "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
+ "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
+ "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
+ "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
+ "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
+ "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
+ "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
+ "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
+ "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
+ "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
+ "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
+ "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
+ "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
+ "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
+ "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.3"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-webcontainers-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
+ "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
+ "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
+ "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
+ "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@inquirer/ansi": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz",
+ "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ }
+ },
+ "node_modules/@inquirer/checkbox": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.3.tgz",
+ "integrity": "sha512-XEYX2WA8SBkLPczL6/yXPHLPCvDoptmh9v56Cy05BSV1Smk1vWy19bTC4qJBuIffw7+6l4CcaYYzGqG60RfW1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/confirm": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.3.0.tgz",
+ "integrity": "sha512-pZHXJImFtERmSNMBHcjwuz8Ck5vEFEYNUZnwbb8aJpjHv/TwGuFErNxF2Hp8+V+pNJs2EYPMlyWscvFEqO9jOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/confirm/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/core": {
+ "version": "11.2.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz",
+ "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.7",
+ "@inquirer/type": "^4.0.7",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/editor": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.3.1.tgz",
+ "integrity": "sha512-y43COoyVUjPWIobn2Qep/uI1drPS78aaZZZ9kVi94Tyu/GuW2N8d8Q4rifJXGAXCEAXCPTTMjD8gC1HyvM5ukA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/external-editor": "^3.0.4",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/editor/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/expand": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.3.tgz",
+ "integrity": "sha512-3NQJiXNJ/aj9wiAsr7pECdp5Qe9J0X9YUJCKsaFXS+ddOxfL6J4AIl3w3T4Gq3kK0WsQY5GMoDokK5X94m6lHw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/expand/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.4.tgz",
+ "integrity": "sha512-tZbbaK2ovq6vlrRBNQvjrypmrED/p5x2ncIHQ79cD55tei3dD96v5glMMA+6tiq7K104i/25DVYKWVPJuV6ptA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.1",
+ "iconv-lite": "^0.7.2"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/figures": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.8.tgz",
+ "integrity": "sha512-tApbon79GM9ry56ja/Ud3SY2CL4TQsao9fIwDQbgTeNY55025GdMzQ2+UdegV/lx51VNGUB59M0v0nMpybYY4Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ }
+ },
+ "node_modules/@inquirer/input": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.4.tgz",
+ "integrity": "sha512-3xQkQrOvgOzpSN2ciTVdRDlg1FWMCA8l+0KfB6SNlILoTCGzJTzO/gc0Rwjcb3usuGyKdaGtI6OiyMdeMeLWkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/input/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/number": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.2.1.tgz",
+ "integrity": "sha512-5KaqwZNLRpUuWcoCrYghPP9TMaXL5v2Sk4xqePM7RCVegcJStoXdWibio60YIC1bec+z1fCyb67N6XPJIkZtGA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/number/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/password": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.2.0.tgz",
+ "integrity": "sha512-CvVcW09emkBESEOW+4R8CjLNkP3fB3XrjeL8CDvfpjgrJN+V9oerXmJAXXM3l+4xqYPD5Yaujzy/Ph0PLOdDuA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/password/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/prompts": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.3.0.tgz",
+ "integrity": "sha512-JAj66kjdH/F1+B7LCigjARbwstt3SNUOSzMdjpsvwJmzunK88gJeXmcm95L9nw1KynvFVuY4SzXh/3Y0lvtgSg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/checkbox": "^5.1.0",
+ "@inquirer/confirm": "^6.0.8",
+ "@inquirer/editor": "^5.0.8",
+ "@inquirer/expand": "^5.0.8",
+ "@inquirer/input": "^5.0.8",
+ "@inquirer/number": "^4.0.8",
+ "@inquirer/password": "^5.0.8",
+ "@inquirer/rawlist": "^5.2.4",
+ "@inquirer/search": "^4.1.4",
+ "@inquirer/select": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/rawlist": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.3.tgz",
+ "integrity": "sha512-Mu7WrtmDLaXBDEyrRLS70SZgX9ZSm4Up1w0ZxiH8C1OOp9oaVCn2k8q3QGgmlnhsKYUhuaU3zFWhAP6wxkVIMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/search": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.3.1.tgz",
+ "integrity": "sha512-0VWOvsHWI0rPj6CG70MoP4oXNCB6adcyN8bVFZXnh11eLDdPIK2f2XCmva78acPPDfJisfab7qNakwBh7hdBXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/search/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/select": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.3.tgz",
+ "integrity": "sha512-KuRTodDa6xBXX2noIpjuitpX/QT7Sfav7dIZ/OfUY54Hxg95nrGoshSzxx6Ey7qqLbImKdiGkSDt7KjPXjQgmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/core": "^12.0.1",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/select/node_modules/@inquirer/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.1.tgz",
+ "integrity": "sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.8",
+ "@inquirer/type": "^4.1.0",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/type": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.1.0.tgz",
+ "integrity": "sha512-FMiJpuHUG3Dk0ex+UIXkre7i+i4OcwHWk9YdcVtZHFwb/r2rnrU2ipTCNAB7A+QOP0ryzIcqOfy76fRyyvOEAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@keyv/serialize": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz",
+ "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@kwsites/file-exists": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
+ "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1"
+ }
+ },
+ "node_modules/@kwsites/promise-deferred": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
+ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@langfuse/client": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@langfuse/client/-/client-5.11.0.tgz",
+ "integrity": "sha512-3Bf8xI1y8Dc71iLbfBV/CDM6Z5EgRpMPR+m+SQoChVXlzXQATs3MkBR8lLQlU/ag+9Pa1vf1OYTfPX4gf0iJQw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@langfuse/core": "^5.11.0",
+ "@langfuse/tracing": "^5.11.0",
+ "mustache": "^4.2.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.0"
+ }
+ },
+ "node_modules/@langfuse/core": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@langfuse/core/-/core-5.11.0.tgz",
+ "integrity": "sha512-Y5nBcrd8k0bEazb+lcOaEm6ICxxJJLGDkTax7dY4NyiYoO1LlTIFhkfwSOcneQRjpl7EvgL4Dy+9fzDXBDuVaA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.0"
+ }
+ },
+ "node_modules/@langfuse/tracing": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@langfuse/tracing/-/tracing-5.11.0.tgz",
+ "integrity": "sha512-RrAradZuRVYnr7TLv1tHwLzyYxli96E8ujPmrt5LXLsEYsqUlfAhweW8khE1wqwj7uooLFg2FVvw1+IAIQx/+Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@langfuse/core": "^5.11.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.0"
+ }
+ },
+ "node_modules/@libsql/client": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.4.tgz",
+ "integrity": "sha512-lYayFWasDV78A+TjlEhr6ubb3odBV6OHjb+wdp8VQcyWWAEIjuwbCHaraEUS4m4yWoo0BvZo96It4VdzZRmRWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@libsql/core": "^0.17.4",
+ "@libsql/hrana-client": "^0.10.0",
+ "js-base64": "^3.7.5",
+ "libsql": "^0.5.28",
+ "promise-limit": "^2.7.0"
+ }
+ },
+ "node_modules/@libsql/core": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.17.4.tgz",
+ "integrity": "sha512-LqF9gIvnJ38nmAH1y/ChizHqDO/MO1wLgA96XrraulEEbqXxLjleSH92YWTolbuJKgPUmGu4aJk9W3UnAcxLOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-base64": "^3.7.5"
+ }
+ },
+ "node_modules/@libsql/darwin-arm64": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.5.29.tgz",
+ "integrity": "sha512-K+2RIB1OGFPYQbfay48GakLhqf3ArcbHqPFu7EZiaUcRgFcdw8RoltsMyvbj5ix2fY0HV3Q3Ioa/ByvQdaSM0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@libsql/darwin-x64": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.5.29.tgz",
+ "integrity": "sha512-OtT+KFHsKFy1R5FVadr8FJ2Bb1mghtXTyJkxv0trocq7NuHntSki1eUbxpO5ezJesDvBlqFjnWaYYY516QNLhQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@libsql/hrana-client": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.10.0.tgz",
+ "integrity": "sha512-OoA4EMqRAC7kn7V2P6EQqRcpZf2W+AjsNIyCizBg339Tq/aMC7sRnzs3SklderhmQWAqEzvv8A2vhxVmWpkVvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@libsql/isomorphic-ws": "^0.1.5",
+ "js-base64": "^3.7.5"
+ }
+ },
+ "node_modules/@libsql/isomorphic-ws": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz",
+ "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ws": "^8.5.4",
+ "ws": "^8.13.0"
+ }
+ },
+ "node_modules/@libsql/linux-arm-gnueabihf": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.5.29.tgz",
+ "integrity": "sha512-CD4n4zj7SJTHso4nf5cuMoWoMSS7asn5hHygsDuhRl8jjjCTT3yE+xdUvI4J7zsyb53VO5ISh4cwwOtf6k2UhQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/linux-arm-musleabihf": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-arm-musleabihf/-/linux-arm-musleabihf-0.5.29.tgz",
+ "integrity": "sha512-2Z9qBVpEJV7OeflzIR3+l5yAd4uTOLxklScYTwpZnkm2vDSGlC1PRlueLaufc4EFITkLKXK2MWBpexuNJfMVcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/linux-arm64-gnu": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.5.29.tgz",
+ "integrity": "sha512-gURBqaiXIGGwFNEaUj8Ldk7Hps4STtG+31aEidCk5evMMdtsdfL3HPCpvys+ZF/tkOs2MWlRWoSq7SOuCE9k3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/linux-arm64-musl": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.5.29.tgz",
+ "integrity": "sha512-fwgYZ0H8mUkyVqXZHF3mT/92iIh1N94Owi/f66cPVNsk9BdGKq5gVpoKO+7UxaNzuEH1roJp2QEwsCZMvBLpqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/linux-x64-gnu": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.5.29.tgz",
+ "integrity": "sha512-y14V0vY0nmMC6G0pHeJcEarcnGU2H6cm21ZceRkacWHvQAEhAG0latQkCtoS2njFOXiYIg+JYPfAoWKbi82rkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/linux-x64-musl": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.5.29.tgz",
+ "integrity": "sha512-gquqwA/39tH4pFl+J9n3SOMSymjX+6kZ3kWgY3b94nXFTwac9bnFNMffIomgvlFaC4ArVqMnOZD3nuJ3H3VO1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@libsql/win32-x64-msvc": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.5.29.tgz",
+ "integrity": "sha512-4/0CvEdhi6+KjMxMaVbFM2n2Z44escBRoEYpR+gZg64DdetzGnYm8mcNLcoySaDJZNaBd6wz5DNdgRmcI4hXcg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@linear/sdk": {
+ "version": "89.0.0",
+ "resolved": "https://registry.npmjs.org/@linear/sdk/-/sdk-89.0.0.tgz",
+ "integrity": "sha512-LiV2wsIg1Wym4GgaAUMrALo953WEgIAAceyK8qL91q75Ws8+lqwAkPW252MHtwktZCR7exMBd5wCAdct3yLU7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=18.x"
+ }
+ },
+ "node_modules/@modelcontextprotocol/core": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz",
+ "integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "zod": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz",
+ "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@hono/node-server": "^1.19.9 || ^2.0.5",
+ "ajv": "^8.17.1",
+ "ajv-formats": "^3.0.1",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "eventsource-parser": "^3.0.0",
+ "express": "^5.2.1",
+ "express-rate-limit": "^8.2.1",
+ "hono": "^4.11.4",
+ "jose": "^6.1.3",
+ "json-schema-typed": "^8.0.2",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.25 || ^4.0",
+ "zod-to-json-schema": "^3.25.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@cfworker/json-schema": {
+ "optional": true
+ },
+ "zod": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk/node_modules/eventsource-parser": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz",
+ "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@modelcontextprotocol/server": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz",
+ "integrity": "sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@modelcontextprotocol/core": "2.0.0",
+ "zod": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@mongodb-js/saslprep": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.5.0.tgz",
+ "integrity": "sha512-Hk1SKJCMcCos38+vqDnZzlIo4XRj9yCGzYkjB4LcqpeXRIYfia1UWTz+VrueLxoU+uSRJzgkufxoRZg8gi52YA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
+ "node_modules/@msgpack/msgpack": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz",
+ "integrity": "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@napi-rs/canvas": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz",
+ "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "e2e/*"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas-android-arm64": "0.1.80",
+ "@napi-rs/canvas-darwin-arm64": "0.1.80",
+ "@napi-rs/canvas-darwin-x64": "0.1.80",
+ "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80",
+ "@napi-rs/canvas-linux-arm64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-arm64-musl": "0.1.80",
+ "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-x64-gnu": "0.1.80",
+ "@napi-rs/canvas-linux-x64-musl": "0.1.80",
+ "@napi-rs/canvas-win32-x64-msvc": "0.1.80"
+ }
+ },
+ "node_modules/@napi-rs/canvas-android-arm64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz",
+ "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-arm64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz",
+ "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-x64": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz",
+ "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz",
+ "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz",
+ "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz",
+ "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz",
+ "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz",
+ "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-musl": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz",
+ "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-arm64-msvc": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-1.0.8.tgz",
+ "integrity": "sha512-WwPN08IXE4SkL+FhJyPz/iFnycMAUkbphFIT4cmKLlvbSU0Zfn1R7BGJ3Hqky1S89QUYc0Q4IOScXb/42Re9wQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+ "version": "0.1.80",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz",
+ "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@neon-rs/load": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz",
+ "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@nodable/entities": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz",
+ "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@octokit/auth-token": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
+ "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/core": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
+ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/auth-token": "^6.0.0",
+ "@octokit/graphql": "^9.0.3",
+ "@octokit/request": "^10.0.6",
+ "@octokit/request-error": "^7.0.2",
+ "@octokit/types": "^16.0.0",
+ "before-after-hook": "^4.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/endpoint": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.5.tgz",
+ "integrity": "sha512-iXa654H3yFafF/ieHkukfbgWo2rmXD2ceD0ZOtrPhw1bc3FDch1d9N/TNs0FQ1/cIbwb7kspUX8jzIs8nzb9DQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/types": "^18.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
+ "version": "29.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
+ "integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
+ "integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^29.0.1"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.4.tgz",
+ "integrity": "sha512-5s15CCiY8XXQ+FG+b1YQcl6Z2FA++nwAz/tg2VUrTmnMncP+2nnGUEYANImdnxsA2Fnq+Mbl7hDjUTw7cFAwcg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/request": "^10.0.13",
+ "@octokit/types": "^17.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
+ "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@octokit/graphql/node_modules/@octokit/types": {
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
+ "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^28.0.0"
+ }
+ },
+ "node_modules/@octokit/openapi-types": {
+ "version": "27.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
+ "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@octokit/request": {
+ "version": "10.0.15",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.15.tgz",
+ "integrity": "sha512-3CBg9aJ0hO9Pjyij8LbK/xYtEaPws9SW7xKz67daPNxQB1q5Y9OMA7DDOG0A6Hwf9ygGu3tvzusg0LXQ8/wAjA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/endpoint": "^11.0.3",
+ "@octokit/request-error": "^7.1.1",
+ "@octokit/types": "^17.0.0",
+ "content-type": "^3.0.0",
+ "json-with-bigint": "^3.5.12",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/request-error": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.1.tgz",
+ "integrity": "sha512-+eaY7G2VVpSf2pc5Gn1+mph837V/d/TYTJAgWL9Tb0ogGYcpN3IlAVFgjL+Vv93F/sevrxkvsYCedtpLdcFLzA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/types": "^17.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
+ "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@octokit/request-error/node_modules/@octokit/types": {
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
+ "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^28.0.0"
+ }
+ },
+ "node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
+ "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@octokit/request/node_modules/@octokit/types": {
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
+ "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^28.0.0"
+ }
+ },
+ "node_modules/@octokit/request/node_modules/content-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-3.0.0.tgz",
+ "integrity": "sha512-AIi5H6p0xk5uknXcN3/rmhP8jgp69OfSe/JuKiQAFprJ7UGw7mwj7m4XcmDzlrnJDG+cGpphAINGdU3g3g7kDw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/@octokit/types": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
+ "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^27.0.0"
+ }
+ },
+ "node_modules/@openai/agents": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@openai/agents/-/agents-0.11.8.tgz",
+ "integrity": "sha512-D4XHF2g+Ub/L9fRJT/xpuiCqHyxiKzZbi0BqQxnso42t+J049O/OSvVzFBcRskF4uPFAvs0TOOB7KBbanCwaYQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@openai/agents-core": "0.11.8",
+ "@openai/agents-openai": "0.11.8",
+ "@openai/agents-realtime": "0.11.8",
+ "debug": "^4.4.0",
+ "openai": "^6.35.0"
+ },
+ "peerDependencies": {
+ "zod": "^4.0.0"
+ }
+ },
+ "node_modules/@openai/agents-core": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@openai/agents-core/-/agents-core-0.11.8.tgz",
+ "integrity": "sha512-TrE34RXXPoWYv2PjXf5hq3Eq+uvRJMNiY+Q5WBgEPjAg60yt2hya8cS2I8qkO6i25MjNJl37a25X0vL/gs5Wdg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "^4.4.0",
+ "openai": "^6.35.0"
+ },
+ "optionalDependencies": {
+ "@modelcontextprotocol/sdk": "^1.26.0"
+ },
+ "peerDependencies": {
+ "zod": "^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@openai/agents-core/node_modules/openai": {
+ "version": "6.49.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz",
+ "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peerDependencies": {
+ "@aws-sdk/credential-provider-node": ">=3.972.0 <4",
+ "@smithy/hash-node": ">=4.3.0 <5",
+ "@smithy/signature-v4": ">=5.4.0 <6",
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-provider-node": {
+ "optional": true
+ },
+ "@smithy/hash-node": {
+ "optional": true
+ },
+ "@smithy/signature-v4": {
+ "optional": true
+ },
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@openai/agents-openai": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@openai/agents-openai/-/agents-openai-0.11.8.tgz",
+ "integrity": "sha512-XjHCnJPGapgZBlh8y5oxU7zV0hrAQTF5im6HpUwaPcH5CeRFLtc06VXLso0vJ5G3g9e/J5gIh3S1iAxiJqEAVQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@openai/agents-core": "0.11.8",
+ "debug": "^4.4.0",
+ "openai": "^6.35.0"
+ },
+ "peerDependencies": {
+ "zod": "^4.0.0"
+ }
+ },
+ "node_modules/@openai/agents-openai/node_modules/openai": {
+ "version": "6.49.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz",
+ "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peerDependencies": {
+ "@aws-sdk/credential-provider-node": ">=3.972.0 <4",
+ "@smithy/hash-node": ">=4.3.0 <5",
+ "@smithy/signature-v4": ">=5.4.0 <6",
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-provider-node": {
+ "optional": true
+ },
+ "@smithy/hash-node": {
+ "optional": true
+ },
+ "@smithy/signature-v4": {
+ "optional": true
+ },
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@openai/agents-realtime": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@openai/agents-realtime/-/agents-realtime-0.11.8.tgz",
+ "integrity": "sha512-i1qEGUE8GTW0neWgAc1aj/3wZFtstz8bVG2BvVbU/BzQbyhZV8j3CvndkMJGFfgeobvVmn2qGTV5Ry6ibfuxeQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@openai/agents-core": "0.11.8",
+ "@types/ws": "^8.18.1",
+ "debug": "^4.4.0",
+ "ws": "^8.18.1"
+ },
+ "peerDependencies": {
+ "zod": "^4.0.0"
+ }
+ },
+ "node_modules/@openai/agents/node_modules/openai": {
+ "version": "6.49.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz",
+ "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peerDependencies": {
+ "@aws-sdk/credential-provider-node": ">=3.972.0 <4",
+ "@smithy/hash-node": ">=4.3.0 <5",
+ "@smithy/signature-v4": ">=5.4.0 <6",
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-provider-node": {
+ "optional": true
+ },
+ "@smithy/hash-node": {
+ "optional": true
+ },
+ "@smithy/signature-v4": {
+ "optional": true
+ },
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@openai/codex": {
+ "version": "0.151.0",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0.tgz",
+ "integrity": "sha512-mhtWmOZRdmWD1jPbLDnQb59BsaVP/V+lXe/OFNR9ZcLZU0UCiBwn98Fcav1ss7sDIlHkuqj6nWd44IPeXoOhJA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "codex": "bin/codex.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "@openai/codex-darwin-arm64": "npm:@openai/codex@0.151.0-darwin-arm64",
+ "@openai/codex-darwin-x64": "npm:@openai/codex@0.151.0-darwin-x64",
+ "@openai/codex-linux-arm64": "npm:@openai/codex@0.151.0-linux-arm64",
+ "@openai/codex-linux-x64": "npm:@openai/codex@0.151.0-linux-x64",
+ "@openai/codex-win32-arm64": "npm:@openai/codex@0.151.0-win32-arm64",
+ "@openai/codex-win32-x64": "npm:@openai/codex@0.151.0-win32-x64"
+ }
+ },
+ "node_modules/@openai/codex-darwin-arm64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-darwin-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-darwin-arm64.tgz",
+ "integrity": "sha512-g7YzpaCZGCw19R/gly3vRPjnLqaW7JcBAu2WQQ6e8PIlvBPmS/gMplIUURMgNO6gi8LsPzdlQtLqkwoeOOlIdg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-darwin-x64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-darwin-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-darwin-x64.tgz",
+ "integrity": "sha512-0y+g8TVpP+Fn10mjoKYXER6qYjn29w7xBUsbPXJ6Accu/FoM4Qp4WbKXQPmE0G0yUACTQVZRjzTSsdWUezNgkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-linux-arm64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-linux-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-linux-arm64.tgz",
+ "integrity": "sha512-CsLgFeX4TQ6I2Gdrxd2r5UbgIbDLCdtcLAlnMYjr06bCL057MTNGec7Ewb3+Z2DBiMuXCljdTBGqLOePkMV0sQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-linux-x64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-linux-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-linux-x64.tgz",
+ "integrity": "sha512-xcVyY1FtwvVYhh2JBmz8fX8CQqFAxO/lxJ2IXsh8x5uwxZVHVl5fZHFHf8JdRaOGG0vpkYmu/DKKVoLd56/DDQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-sdk": {
+ "version": "0.151.0",
+ "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.151.0.tgz",
+ "integrity": "sha512-vI4gr5ipvVwH4YHW9DGUmaUI1hJzJOCO/0d5NYFnAECsQGEvBmuTocPzRP8yGzLtsYklMnrtGtm2TyBicihVxw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@openai/codex": "0.151.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@openai/codex-security": {
+ "version": "0.1.24",
+ "resolved": "https://registry.npmjs.org/@openai/codex-security/-/codex-security-0.1.24.tgz",
+ "integrity": "sha512-14HrUkO9pe3DY6x5JzXSik2/HAoa4T6JcVlY3LK9downpJbgi1yd5qYb5o6jzcj2Dupsc5bpXV6me+Cr9YinHQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@inquirer/prompts": "8.3.0",
+ "@linear/sdk": "89.0.0",
+ "@octokit/core": "7.0.6",
+ "@openai/codex": "0.149.1",
+ "@openai/codex-sdk": "0.149.1",
+ "ajv": "8.20.0",
+ "extract-zip": "2.0.1",
+ "fast-uri": "3.1.5",
+ "fflate": "0.8.2",
+ "incur": "0.4.13",
+ "ink": "6.8.0",
+ "js-tiktoken": "1.0.21",
+ "papaparse": "5.5.3",
+ "pdfjs-dist": "6.2.108",
+ "react": "19.2.4",
+ "semver": "7.8.5",
+ "smol-toml": "1.6.1"
+ },
+ "bin": {
+ "codex-security": "bin/codex-security.mjs"
+ },
+ "engines": {
+ "node": "^22.13.0 || ^24.0.0 || ^26.0.0"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex": {
+ "version": "0.149.1",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1.tgz",
+ "integrity": "sha512-6q5pbcpFbJbqOpkubSDBwXmktQ55aD8eUzGzBF1zASob2DjwhBKDSNGtdZKalfrNJUdTDTPDMmzCXEXs5tMBYA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "bin": {
+ "codex": "bin/codex.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "@openai/codex-darwin-arm64": "npm:@openai/codex@0.149.1-darwin-arm64",
+ "@openai/codex-darwin-x64": "npm:@openai/codex@0.149.1-darwin-x64",
+ "@openai/codex-linux-arm64": "npm:@openai/codex@0.149.1-linux-arm64",
+ "@openai/codex-linux-x64": "npm:@openai/codex@0.149.1-linux-x64",
+ "@openai/codex-win32-arm64": "npm:@openai/codex@0.149.1-win32-arm64",
+ "@openai/codex-win32-x64": "npm:@openai/codex@0.149.1-win32-x64"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-darwin-arm64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-darwin-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-darwin-arm64.tgz",
+ "integrity": "sha512-6X84kTCbnTgPIJ2EdcPsrvwS0Wxsqpa+bCswGmRf4BjhcQ5nPMnBC6yCAaCMj+vrbXQHj+L6sa9FaR4QkmA1qw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-darwin-x64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-darwin-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-darwin-x64.tgz",
+ "integrity": "sha512-MfLBQLfcElJL9tvj6y45qVHHMGSXCPnQOixuD3/Zq0g1BW/eFizkrGLdn48cFpc+l8cK+gt5nYG5pQYwVs6g4A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-linux-arm64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-linux-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-linux-arm64.tgz",
+ "integrity": "sha512-OqxUfZ1TVvHd18zHPKK/8ZRlpk8Vy11mg5CMHaLxNWldTbwVImDKtSLWT+m8m4NM5Sz4PbjtZMrVT/RfpBW/mQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-linux-x64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-linux-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-linux-x64.tgz",
+ "integrity": "sha512-Of5fGYgr7tAMsyj6vhXb4/RM/UoA3Zq8BLegUBDC09UNy1XTLGYP/2XD+UX8z3qh0NDwxYdCjFIWdDNijKZggQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-sdk": {
+ "version": "0.149.1",
+ "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.149.1.tgz",
+ "integrity": "sha512-R00Rz5327LefZggAxl28r7vFQq1vxa91OxtjZJOsQfAM/MyH8InW5qwwRu6pzUmRGp1E29XrOzm7u1TeV5Yz2A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@openai/codex": "0.149.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-win32-arm64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-win32-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-win32-arm64.tgz",
+ "integrity": "sha512-5K0DmOKGK9Bos627p8sK8ATHjovPK0sDyT6h9Cb+4v+5CW5SGw1HLgjGxoLfJ8g3cg6mtg/pRCXXo2L/j71UVA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-security/node_modules/@openai/codex-win32-x64": {
+ "name": "@openai/codex",
+ "version": "0.149.1-win32-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.149.1-win32-x64.tgz",
+ "integrity": "sha512-G3QXGAg7nyyhqOeooAMUekBCeHd8a1QByhKcVAFyzNBaI06t6Ft7nsF+1SzFS0spuIdU4YyMi5YD26ukADBQUQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-win32-arm64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-win32-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-win32-arm64.tgz",
+ "integrity": "sha512-zDWzOoh9wHm+Om1Nhn7os47rAVeSGPh0SnM3YOttdq6iPJz2zn4vBnbGUZjeih1qW/3mvNF3Oyd4owlaHmphmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@openai/codex-win32-x64": {
+ "name": "@openai/codex",
+ "version": "0.151.0-win32-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.151.0-win32-x64.tgz",
+ "integrity": "sha512-sLT7xvID3jhU6tkzcwRPnMEclKRwUPbpo0mtfxIF9KpdZH3VJV7sM2/kXWXyvUM7Zt/YeyOaeATTEysbRz8Yog==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@opencode-ai/sdk": {
+ "version": "1.18.25",
+ "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.18.25.tgz",
+ "integrity": "sha512-GwgwhW+vE8FWSDw730SjzqNhsWXB0uJjbFOiqFkmM+USFuG13HuTlGe6SR2ixt+WXxoD6FV1hILWqsXyqej9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "cross-spawn": "7.0.6"
+ }
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/api-logs": {
+ "version": "0.221.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz",
+ "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/context-async-hooks": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz",
+ "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz",
+ "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/exporter-trace-otlp-http": {
+ "version": "0.221.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.221.0.tgz",
+ "integrity": "sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/otlp-exporter-base": "0.221.0",
+ "@opentelemetry/otlp-transformer": "0.221.0",
+ "@opentelemetry/sdk-trace": "2.10.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/otlp-exporter-base": {
+ "version": "0.221.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.221.0.tgz",
+ "integrity": "sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/otlp-transformer": "0.221.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/otlp-transformer": {
+ "version": "0.221.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.221.0.tgz",
+ "integrity": "sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.221.0",
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/sdk-logs": "0.221.0",
+ "@opentelemetry/sdk-metrics": "2.10.0",
+ "@opentelemetry/sdk-trace": "2.10.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/resources": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz",
+ "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-logs": {
+ "version": "0.221.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.221.0.tgz",
+ "integrity": "sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.221.0",
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.4.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-metrics": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.10.0.tgz",
+ "integrity": "sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.9.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz",
+ "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-base": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz",
+ "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/sdk-trace": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-node": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz",
+ "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/context-async-hooks": "2.10.0",
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/sdk-trace-base": "2.10.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz",
+ "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@playwright/browser-chromium": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.62.1.tgz",
+ "integrity": "sha512-DU/t4TSqHvAc+uFMt972forQYqBTh/ul7lZ8U81HYGyxnf6vSPPz9NzuE0OR3x/elqvvGm+n4gcny+QSKT+FDw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "playwright-core": "1.62.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@posthog/core": {
+ "version": "1.23.1",
+ "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.23.1.tgz",
+ "integrity": "sha512-GViD5mOv/mcbZcyzz3z9CS0R79JzxVaqEz4sP5Dsea178M/j3ZWe6gaHDZB9yuyGfcmIMQ/8K14yv+7QrK4sQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.6"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
+ "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
+ "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
+ "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz",
+ "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/@redis/bloom": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.12.1.tgz",
+ "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/client": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.12.1.tgz",
+ "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "cluster-key-slot": "1.1.2"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@node-rs/xxhash": "^1.1.0",
+ "@opentelemetry/api": ">=1 <2"
+ },
+ "peerDependenciesMeta": {
+ "@node-rs/xxhash": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@redis/json": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.12.1.tgz",
+ "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/search": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.12.1.tgz",
+ "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/time-series": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.12.1.tgz",
+ "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.63.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz",
+ "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@scalar/openapi-types": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.8.0.tgz",
+ "integrity": "sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/@sec-ant/readable-stream": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@simple-git/args-pathspec": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz",
+ "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@simple-git/argv-parser": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz",
+ "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@simple-git/args-pathspec": "^1.0.3"
+ }
+ },
+ "node_modules/@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@slack/logger": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-5.0.0.tgz",
+ "integrity": "sha512-VGXhmmgsAo9shdQYh4tFDndd+7nsgp0Y5h0UPDaUp8K359pBasI6YdkMqFW3mCOxLQkq09qj7o7cq6f3DuXcJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": ">=20"
+ },
+ "engines": {
+ "node": ">= 20",
+ "npm": ">=9.6.4"
+ }
+ },
+ "node_modules/@slack/types": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@slack/types/-/types-3.1.0.tgz",
+ "integrity": "sha512-bTzqrO3lxJ5iWedo1eJKNAs1koyEhaTwKLJUkD3KETnPeQvD978AAE4jvWstUws8Gbbc4JR6iYE6F7XjE8UGmw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 20",
+ "npm": ">=9.6.4"
+ }
+ },
+ "node_modules/@slack/web-api": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-8.1.1.tgz",
+ "integrity": "sha512-am/z/LLbEC7gNQp6FpaGT0l4XnWqcXetpwdgZHzS7Nbb96q9R9QwWA3lo0ZXGuAHDkct0MvFnouzuc2Y4XNmow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@slack/logger": "^5.0.0",
+ "@slack/types": "^3.1.0",
+ "@types/node": ">=20",
+ "@types/retry": "0.12.5",
+ "eventemitter3": "^5.0.1",
+ "p-queue": "^6.6.2",
+ "p-retry": "^4.6.2",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">= 20",
+ "npm": ">=9.6.4"
+ }
+ },
+ "node_modules/@smithy/core": {
+ "version": "3.33.3",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz",
+ "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/credential-provider-imds": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz",
+ "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/core": "^3.33.2",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/fetch-http-handler": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.2.tgz",
+ "integrity": "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/core": "^3.33.2",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-http-handler": {
+ "version": "4.11.3",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.11.3.tgz",
+ "integrity": "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/signature-v4": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz",
+ "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/types": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.2.tgz",
+ "integrity": "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@so-ric/colorspace": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
+ "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color": "^5.0.2",
+ "text-hex": "1.0.x"
+ }
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@stablelib/base64": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/core": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.16.1.tgz",
+ "integrity": "sha512-nUaeu91O5QZKrQdaDCHd402ogUIoNOOjpkZNq0UomWK0G6gDaGmLhvddF1/3BXf5O8aLyo6ZPY/aMDWvaJQ/hg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "@swc/types": "^0.1.28"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.16.1",
+ "@swc/core-darwin-x64": "1.16.1",
+ "@swc/core-linux-arm-gnueabihf": "1.16.1",
+ "@swc/core-linux-arm64-gnu": "1.16.1",
+ "@swc/core-linux-arm64-musl": "1.16.1",
+ "@swc/core-linux-ppc64-gnu": "1.16.1",
+ "@swc/core-linux-s390x-gnu": "1.16.1",
+ "@swc/core-linux-x64-gnu": "1.16.1",
+ "@swc/core-linux-x64-musl": "1.16.1",
+ "@swc/core-win32-arm64-msvc": "1.16.1",
+ "@swc/core-win32-ia32-msvc": "1.16.1",
+ "@swc/core-win32-x64-msvc": "1.16.1"
+ },
+ "peerDependencies": {
+ "@swc/helpers": ">=0.5.17"
+ },
+ "peerDependenciesMeta": {
+ "@swc/helpers": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.16.1.tgz",
+ "integrity": "sha512-zlJblJ8ncErD43lKdxjbUaUskJQf+LxiPXYcWXD8/8ZMV+7uuAT+CwjciLXpyZBd5Pq/S726bMpeeAwSeL1hhg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-darwin-x64": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.16.1.tgz",
+ "integrity": "sha512-IN0BmPWb0YAh/17mmlWB/HDBtTw2MfuW4hulf/tQAgTQBRH17l+z499bNJLK6LizSjqs0P7V+jU38Zj+vJC1DA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.16.1.tgz",
+ "integrity": "sha512-EYgrx2YOCQ2Twz2S793kqNjPkpvYVUPzzR95bIb7by+VQcyaai4lZZ2iz/tZvcFVKSNcN3/JTKwx+aBn2ZL52A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.16.1.tgz",
+ "integrity": "sha512-moyKm0YZlHdHohzm1YwgAyesqnE853rO0REMfJLFAova51wF9BNi+3ZW2PeS7Vqvn6HeJuepLpAHbBdZctxpHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.16.1.tgz",
+ "integrity": "sha512-kKGBO9wdapiSzuf5ZzZ2fYtlu1BNSYtIIUxvH1ir/gcelTOREEHGDCLTDFx/2Knf878nU11A40z7LxwasEFxqA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-ppc64-gnu": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.16.1.tgz",
+ "integrity": "sha512-nZ6qahtLxC3PM54cWOQZHxt4lTCF/3J4LIoWWzz6v7A+rLs8Dx54anYQf7mH3eIi8KlNpgKci/ie8ZSqFN8O7A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-s390x-gnu": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.16.1.tgz",
+ "integrity": "sha512-4ji5PNzhYq193Z4/4xUaSoNJza6iCkDJSzhetrbB6KOYxsr+kxtQr8ePWhMJUiMt6JUWtXaZ1PYT8FhtED+nGA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.16.1.tgz",
+ "integrity": "sha512-VJQxqrisHV+B394IgrOu8YsIIXZgffnf5tO+yc9Z/hoUpuZEvuQTjWwlnpZdpyD+0nx6LTD1/3k646JYm43yJA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.16.1.tgz",
+ "integrity": "sha512-r9oV1mwxxsIGcLV1IQ/tw76MW3doatKze1QFWuC+a7QqJUkhY/bKTSVk6NpKKUGm2LDsE33Va8VqSClfA7vSiQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.16.1.tgz",
+ "integrity": "sha512-6huNRessoBLxWEqBm5zJXyCQ27TO7anvkdiuQ5MDO4CJni0nOXEqKtV9RllQ2TdyENKKsUMXVnIfW2hIXx/R5Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.16.1.tgz",
+ "integrity": "sha512-OVKJFUzphrGmsh+BGtcZDesx0YryV7/Yvy5XGgTqnrZfjnyfcr5uaqYQugCckdIlupc5Vs3XtDjRAj12z4ZPlw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.16.1.tgz",
+ "integrity": "sha512-Bt+VIhWYCGk4urklnkkteLUOeLv1VxigwTCeB/xC6rBZxY6IIKdDwCJf6on3E3SUGsIqmQS6QqtuJQc1VxF4Aw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/@swc/types": {
+ "version": "0.1.28",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.28.tgz",
+ "integrity": "sha512-V6Mnml8v09QALx6K0elJ7o9K/MkVDtW3t6L+7Ou/JcWtb3xwId2AH4FeOceySd2JaO87IMw4+6vSZxLm34LPbw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@swc/counter": "^0.1.3"
+ }
+ },
+ "node_modules/@tokenizer/inflate": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
+ "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "^4.4.3",
+ "token-types": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@toon-format/toon": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@toon-format/toon/-/toon-2.3.1.tgz",
+ "integrity": "sha512-sWqEMeAJGMda9qRrfPKrX3C4c33CO9SJuvJzzrcBEn5sbDB+k6pSLkDsfN0rVnLQwR97MV3sDgVbcxQNsq8xVw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.19",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+ "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/node": {
+ "version": "26.4.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.0.tgz",
+ "integrity": "sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~8.3.0"
+ }
+ },
+ "node_modules/@types/pegjs": {
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/@types/pegjs/-/pegjs-0.10.6.tgz",
+ "integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz",
+ "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
+ "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/webidl-conversions": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/whatwg-url": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
+ "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typespec/ts-http-runtime": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.8.tgz",
+ "integrity": "sha512-bLMpVcWZNzq6lYOybwFwOAR1IXKcHnhUNqYeHjl1bET/qE3jFPFH+p8Wrh3rU4xwdnifPxmKNESBYnvnmc75aA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@vercel/oidc": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz",
+ "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/a-sync-waterfall": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/content-type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
+ "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/accepts/node_modules/negotiator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz",
+ "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.5.18",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
+ "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12.0"
+ }
+ },
+ "node_modules/afinn-165": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/afinn-165/-/afinn-165-2.0.2.tgz",
+ "integrity": "sha512-mJ/RLUfpXfQA6bzugv+bBsc/QYkVrKaLYeS8fWBpKbTCsonv4iuV9ET0fgReEunm9vKLkaNgnekuSNlTC3WQ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/afinn-165-financialmarketnews": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/afinn-165-financialmarketnews/-/afinn-165-financialmarketnews-3.0.0.tgz",
+ "integrity": "sha512-0g9A1S3ZomFIGDTzZ0t6xmv4AuokBvBmpes8htiyHpH7N4xDmvSQL6UxL/Zcs2ypRb3VwgCscaD8Q3zEawKYhw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ai": {
+ "version": "6.0.271",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.271.tgz",
+ "integrity": "sha512-8eS5Lhf5JT/ncw8+puVOa2cH+H6uKnHoORLqm6Ei3elwcoGIROrIh0VWhrKgbYLGpuz2pOTXW54nAJzPssQzAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/gateway": "3.0.184",
+ "@ai-sdk/provider": "3.0.15",
+ "@ai-sdk/provider-utils": "4.0.49",
+ "@opentelemetry/api": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anynum": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+ "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/apparatus": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/apparatus/-/apparatus-0.0.10.tgz",
+ "integrity": "sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "sylvester": ">= 0.0.8"
+ },
+ "engines": {
+ "node": ">=0.2.6"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/ast-types": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/auto-bind": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz",
+ "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz",
+ "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/axios/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/basic-ftp": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz",
+ "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/before-after-hook": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
+ "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/big-integer": {
+ "version": "1.6.52",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
+ "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
+ "dev": true,
+ "license": "Unlicense",
+ "optional": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-3.1.0.tgz",
+ "integrity": "sha512-Jvvd9hy1w+xUad8+ckQsWA/V1AoyubOvqn0aygjMOVM4BfIaRav1NFS3LsTSDaV4n4FtcCtQXvzep1E6MboqwQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/binaryextensions": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz",
+ "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==",
+ "dev": true,
+ "license": "Artistic-2.0",
+ "dependencies": {
+ "editions": "^6.21.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
+ "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^2.0.0",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.1",
+ "iconv-lite": "^0.7.2",
+ "on-finished": "^2.4.1",
+ "qs": "^6.15.2",
+ "raw-body": "^3.0.2",
+ "type-is": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/body-parser/node_modules/content-type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
+ "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/boolean": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
+ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/bowser": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz",
+ "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bson": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.2.tgz",
+ "integrity": "sha512-1w0ra+ho1cuE+w8jzwgzTFIimFtCfZeCoOsvIPQg6uyFCsp8M29U7bNNf5GrFh88TXbYg1g3TyKUGpd6O7q6zA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "run-applescript": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cache-manager": {
+ "version": "7.2.9",
+ "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.9.tgz",
+ "integrity": "sha512-d4vceEyYe95gPxEyQchlEOH9vJlkNRW8G6gzFzzMTxJK9PahYMhC9chrEqgZN0HulROjgw3IzmWVNk7Q7ytiGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cacheable/utils": "^2.5.0",
+ "keyv": "^5.6.0"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.0.tgz",
+ "integrity": "sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz",
+ "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+ "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "restore-cursor": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-progress": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.3"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
+ "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+ "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "slice-ansi": "^8.0.0",
+ "string-width": "^8.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
+ "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/code-excerpt": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz",
+ "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "convert-to-spaces": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/color": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
+ "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^3.1.3",
+ "color-string": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
+ "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.6"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz",
+ "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/color-string": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
+ "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/complex.js": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz",
+ "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+ "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "compressible": "~2.0.18",
+ "debug": "2.6.9",
+ "negotiator": "~0.6.4",
+ "on-headers": "~1.1.0",
+ "safe-buffer": "5.2.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+ "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-to-spaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
+ "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/csv-parse": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.2.tgz",
+ "integrity": "sha512-uKZghv9UmPkMVLYy//KZ9HFAIJsl7wkhoEdIL0+rhuSY9pZQlhaeGEDPIe+/w7eh81MOql8Q/9+inAGWG6ZHYA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/csv-stringify": {
+ "version": "6.8.3",
+ "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.8.3.tgz",
+ "integrity": "sha512-gIeSCvq5F4VtXV3naV3VAewLhBkiZBz+PPhTOA8H3Y8h/ELa+R1ml0GZck/4/Nzo9ep2lvOluilJ6MJlbZsKMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/debounce": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-3.0.0.tgz",
+ "integrity": "sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dedent": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
+ "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/default-browser": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
+ "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/degenerator": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-7.0.1.tgz",
+ "integrity": "sha512-ABErK0IefDSyHjlPH7WUEenIAX2rPPnrDcDM+TS3z3+zu9TfyKKi07BQM+8rmxpdE2y1v5fjjdoAS/x4D2U60w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ast-types": "^0.13.4",
+ "escodegen": "^2.1.0",
+ "esprima": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "quickjs-wasi": "^2.2.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
+ "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/dotenv": {
+ "version": "17.4.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
+ "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/drizzle-orm": {
+ "version": "0.45.2",
+ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz",
+ "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+ "@electric-sql/pglite": ">=0.2.0",
+ "@libsql/client": ">=0.10.0",
+ "@libsql/client-wasm": ">=0.10.0",
+ "@neondatabase/serverless": ">=0.10.0",
+ "@op-engineering/op-sqlite": ">=2",
+ "@opentelemetry/api": "^1.4.1",
+ "@planetscale/database": ">=1.13",
+ "@prisma/client": "*",
+ "@tidbcloud/serverless": "*",
+ "@types/better-sqlite3": "*",
+ "@types/pg": "*",
+ "@types/sql.js": "*",
+ "@upstash/redis": ">=1.34.7",
+ "@vercel/postgres": ">=0.8.0",
+ "@xata.io/client": "*",
+ "better-sqlite3": ">=7",
+ "bun-types": "*",
+ "expo-sqlite": ">=14.0.0",
+ "gel": ">=2",
+ "knex": "*",
+ "kysely": "*",
+ "mysql2": ">=2",
+ "pg": ">=8",
+ "postgres": ">=3",
+ "sql.js": ">=1",
+ "sqlite3": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/client-rds-data": {
+ "optional": true
+ },
+ "@cloudflare/workers-types": {
+ "optional": true
+ },
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "@libsql/client": {
+ "optional": true
+ },
+ "@libsql/client-wasm": {
+ "optional": true
+ },
+ "@neondatabase/serverless": {
+ "optional": true
+ },
+ "@op-engineering/op-sqlite": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@prisma/client": {
+ "optional": true
+ },
+ "@tidbcloud/serverless": {
+ "optional": true
+ },
+ "@types/better-sqlite3": {
+ "optional": true
+ },
+ "@types/pg": {
+ "optional": true
+ },
+ "@types/sql.js": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/postgres": {
+ "optional": true
+ },
+ "@xata.io/client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "bun-types": {
+ "optional": true
+ },
+ "expo-sqlite": {
+ "optional": true
+ },
+ "gel": {
+ "optional": true
+ },
+ "knex": {
+ "optional": true
+ },
+ "kysely": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "postgres": {
+ "optional": true
+ },
+ "prisma": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/editions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz",
+ "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==",
+ "dev": true,
+ "license": "Artistic-2.0",
+ "dependencies": {
+ "version-range": "^4.15.0"
+ },
+ "engines": {
+ "ecmascript": ">= es5",
+ "node": ">=4"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.6.9",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz",
+ "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "@types/ws": "^8.5.12",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.7.2",
+ "cors": "~2.8.5",
+ "debug": "~4.4.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.21.0"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/engine.io-client": {
+ "version": "6.6.6",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz",
+ "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.21.0",
+ "xmlhttprequest-ssl": "~2.1.1"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io/node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/entities": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+ "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-toolkit": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz",
+ "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "docs",
+ "benchmarks",
+ "tests/types",
+ "tests/browser-compat"
+ ]
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/es6-promisify": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
+ "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-latex": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
+ "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
+ "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/eventsource/node_modules/eventsource-parser": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz",
+ "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-10.0.1.tgz",
+ "integrity": "sha512-ge98qjkRK4IB7tL7Ju/6qmm5LHoH1eEMt5FNZrz3f4UIYhF28lggX20z3FaX1sgc67msLEn0N0BscOs29iuwyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "figures": "^6.1.0",
+ "get-stream": "^9.0.1",
+ "human-signals": "^8.0.1",
+ "is-plain-obj": "^4.1.0",
+ "is-stream": "^4.0.1",
+ "npm-run-path": "^6.0.0",
+ "pretty-ms": "^9.3.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^4.0.0",
+ "which-command": "^0.1.0",
+ "yoctocolors": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz",
+ "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "^4.4.3",
+ "ip-address": "^10.2.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/exsolve": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz",
+ "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extract-zip/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "dev": true,
+ "license": "Unlicense"
+ },
+ "node_modules/fast-string-truncated-width": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
+ "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-string-width": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
+ "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-truncated-width": "^3.0.2"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
+ "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fast-wrap-ansi": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz",
+ "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-width": "^3.0.2"
+ }
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz",
+ "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.6.2",
+ "xml-naming": "^0.3.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.11.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.1.tgz",
+ "integrity": "sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@nodable/entities": "^3.0.0",
+ "fast-xml-builder": "^1.2.0",
+ "is-unsafe": "^2.0.0",
+ "path-expression-matcher": "^1.6.2",
+ "strnum": "^2.4.2",
+ "xml-naming": "^0.3.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/figures": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
+ "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-unicode-supported": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/file-type": {
+ "version": "21.3.2",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.2.tgz",
+ "integrity": "sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tokenizer/inflate": "^0.4.1",
+ "strtok3": "^10.3.4",
+ "token-types": "^6.1.1",
+ "uint8array-extras": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/flatbuffers": {
+ "version": "25.9.23",
+ "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz",
+ "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gaxios": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.1.tgz",
+ "integrity": "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-9.0.3.tgz",
+ "integrity": "sha512-2YYnIlHaKBGT2IPg3G2M57hia9Galz15zsEOvw9T3oRf0lSn6KN6VcHQLqby7x8ksYKnjXvp3rp2KJyLCN6zfQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "^7.1.3",
+ "google-logging-utils": "^2.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-uri": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-8.0.1.tgz",
+ "integrity": "sha512-/5N/P4Lrh0p/mDwlDRi7Y1+P2o/OyzZI3l6Iz1Ov6XXwwm1y3RlZLuo3gVgML99djrEDtV980bBxSuOeHLk8ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "basic-ftp": "^5.3.1",
+ "data-uri-to-buffer": "8.0.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/get-uri/node_modules/data-uri-to-buffer": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-8.0.0.tgz",
+ "integrity": "sha512-6UHfyCux51b8PTGDgveqtz1tvphBku5DrMKKJbFAZAJOI2zsjDpDoYE1+QGj7FOMS4BdTFNJsJiR3zEB0xH0yQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
+ "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "es6-error": "^4.1.1",
+ "matcher": "^3.0.0",
+ "roarr": "^2.15.3",
+ "semver": "^7.3.2",
+ "serialize-error": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/google-auth-library": {
+ "version": "11.0.2",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-11.0.2.tgz",
+ "integrity": "sha512-vzpgPutxrghPsnjrjpzLX2bdv8IOL719Rh0oEjGnQu8YCIbnbMuTTQ5zU9LcKvLdOPgCxBwppbvnhgW90Qna5Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.1.4",
+ "gcp-metadata": "^9.0.0",
+ "google-logging-utils": "^2.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/google-logging-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-2.0.1.tgz",
+ "integrity": "sha512-HMhaQghlOTvbcb3c4T5jmmOMtG3JUF1iOQMezaJXL86CDS+Tm2vHd0IeLFRAx3+ewd+bo9E1HFHoy17X5aJa9A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/googleapis-common": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.3.tgz",
+ "integrity": "sha512-7g1yzQKx0mmNTjiK0H9dJ8eqKqDBveES9vLHeg5neb3BMQy/d1oQefIMhIpOVT8a+f+LOcixMEdRbFIW/cQUJw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "gaxios": "7.1.3",
+ "google-auth-library": "10.5.0",
+ "google-logging-utils": "1.1.3",
+ "qs": "^6.7.0",
+ "url-template": "^2.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/gcp-metadata": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz",
+ "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "7.1.3",
+ "google-logging-utils": "1.1.3",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/google-auth-library": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
+ "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.0.0",
+ "gcp-metadata": "^8.0.0",
+ "google-logging-utils": "^1.0.0",
+ "gtoken": "^8.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/graphql": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-17.0.2.tgz",
+ "integrity": "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": "^22.0.0 || ^24.0.0 || ^25.0.0 || >=26.0.0"
+ }
+ },
+ "node_modules/gtoken": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
+ "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/guid-typescript": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
+ "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hashery": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz",
+ "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hookified": "^1.15.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.13.5",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.5.tgz",
+ "integrity": "sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/hookified": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz",
+ "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/http-z": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/http-z/-/http-z-8.1.1.tgz",
+ "integrity": "sha512-4rEIu4SljSAs+lgCzzskyNdYllteGIHdnMBsu9MqafivyPAofSmCsrRjHQgxLs0BoPkUJBa7Ld6rXP32SPI8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
+ "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/ibm-cloud-sdk-core/-/ibm-cloud-sdk-core-5.6.0.tgz",
+ "integrity": "sha512-7balLY8WKk+bOhe5Vgg4zG2X6Z0zhpG/3VtYPC69evj+lVJSId8xYP7ISRzRfoeXAlJfzLNMbi2Na/0IdDiIkQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@types/debug": "4.1.12",
+ "@types/node": "18.19.80",
+ "@types/tough-cookie": "4.0.0",
+ "axios": "1.18.0",
+ "camelcase": "6.3.0",
+ "debug": "4.3.4",
+ "dotenv": "16.4.5",
+ "extend": "3.0.2",
+ "file-type": "21.3.2",
+ "form-data": "4.0.6",
+ "isstream": "0.1.2",
+ "jsonwebtoken": "9.0.3",
+ "load-esm": "1.0.3",
+ "mime-types": "2.1.35",
+ "retry-axios": "2.6.0",
+ "tough-cookie": "4.1.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/@types/node": {
+ "version": "18.19.80",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz",
+ "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/ibm-cloud-sdk-core/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
+ "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/incur": {
+ "version": "0.4.13",
+ "resolved": "https://registry.npmjs.org/incur/-/incur-0.4.13.tgz",
+ "integrity": "sha512-BeKlYFLIsRCgC8IxsUd3S2/4kPeZaNf1Rbz+Kz41jQII4jOgr7j5w4KYe00aAEQa3V0Ur57BVxE5JDTx8L2s5Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "@modelcontextprotocol/server": "^2.0.0-alpha.2",
+ "@scalar/openapi-types": "^0.8.0",
+ "@toon-format/toon": "^2.1.0",
+ "tokenx": "^1.3.0",
+ "yaml": "^2.8.2",
+ "zod": "^4.3.6"
+ },
+ "bin": {
+ "incur": "dist/bin.js",
+ "incur.src": "src/bin.ts"
+ },
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ink": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/ink/-/ink-6.8.0.tgz",
+ "integrity": "sha512-sbl1RdLOgkO9isK42WCZlJCFN9hb++sX9dsklOvfd1YQ3bQ2AiFu12Q6tFlr0HvEUvzraJntQCCpfEoUe9DSzA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@alcalzone/ansi-tokenize": "^0.2.4",
+ "ansi-escapes": "^7.3.0",
+ "ansi-styles": "^6.2.1",
+ "auto-bind": "^5.0.1",
+ "chalk": "^5.6.0",
+ "cli-boxes": "^3.0.0",
+ "cli-cursor": "^4.0.0",
+ "cli-truncate": "^5.1.1",
+ "code-excerpt": "^4.0.0",
+ "es-toolkit": "^1.39.10",
+ "indent-string": "^5.0.0",
+ "is-in-ci": "^2.0.0",
+ "patch-console": "^2.0.0",
+ "react-reconciler": "^0.33.0",
+ "scheduler": "^0.27.0",
+ "signal-exit": "^3.0.7",
+ "slice-ansi": "^8.0.0",
+ "stack-utils": "^2.0.6",
+ "string-width": "^8.1.1",
+ "terminal-size": "^4.0.1",
+ "type-fest": "^5.4.1",
+ "widest-line": "^6.0.0",
+ "wrap-ansi": "^9.0.0",
+ "ws": "^8.18.0",
+ "yoga-layout": "~3.2.1"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "@types/react": ">=19.0.0",
+ "react": ">=19.0.0",
+ "react-devtools-core": ">=6.1.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react-devtools-core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ink/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ink/node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ink/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/ink/node_modules/string-width": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
+ "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ink/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/ip-address": {
+ "version": "10.7.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz",
+ "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-binary-path/node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-in-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz",
+ "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "is-in-ci": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unsafe": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz",
+ "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/istextorbinary": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz",
+ "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==",
+ "dev": true,
+ "license": "Artistic-2.0",
+ "dependencies": {
+ "binaryextensions": "^6.11.0",
+ "editions": "^6.21.0",
+ "textextensions": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "optional": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/javascript-natural-sort": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
+ "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jks-js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/jks-js/-/jks-js-1.1.7.tgz",
+ "integrity": "sha512-BeiDRKsAi1NwEwgx2JB/9/0tar5BNGIv+foGm1G5GgiyR35s/iUnfd/BWqYd16mLDD8qTaAVBrIcOOuVqXJZNQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "node-forge": "^1.4.0",
+ "node-int64": "^0.4.0",
+ "node-rsa": "^1.1.1"
+ }
+ },
+ "node_modules/jose": {
+ "version": "6.2.10",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz",
+ "integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/js-base64": {
+ "version": "3.9.3",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.3.tgz",
+ "integrity": "sha512-uwYQp+VJ38FVvtim6qNbit6e9uT6dwWQ4Y1+H9TxhW5hcHjpHwoxlR0nMpqUmIFOmu4VqMxwdJA88gIVuZJQ/g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/js-rouge": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/js-rouge/-/js-rouge-3.2.1.tgz",
+ "integrity": "sha512-rr9vSk+0+IFJyQi8ts4Im9sSs2k8RY5tLKVpreq5Qib2j/N5jsk5qJLMTbDU8DbPZUVV7mqjUumV+vgrUnouzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/js-tiktoken": {
+ "version": "1.0.21",
+ "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
+ "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.5.1"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.3.0.tgz",
+ "integrity": "sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.mjs"
+ }
+ },
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true,
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
+ "node_modules/json-schema-to-ts": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz",
+ "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "ts-algebra": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-typed": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
+ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/json-with-bigint": {
+ "version": "3.5.12",
+ "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.12.tgz",
+ "integrity": "sha512-uwbF/wSSuOgC7qqlq27Xp5B6a2MHVug3t0idZdTqu0JnlFvgJuH7ju+KAk/J06C7GfhoYy2gnb9wz2INqcne7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/kareem": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.3.0.tgz",
+ "integrity": "sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1"
+ }
+ },
+ "node_modules/keyv-file": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/keyv-file/-/keyv-file-5.3.5.tgz",
+ "integrity": "sha512-0JFTTi55d1HdhIrSOnPngUw0fyHLn3BHqoLJ8TyGKM/fQfuZsz8HkcFxpl6YzU2mj1ZfRF/6BXlSqOpyfXYAmw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1",
+ "tslib": "^1.14.1"
+ }
+ },
+ "node_modules/keyv-file/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/libsql": {
+ "version": "0.5.29",
+ "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.5.29.tgz",
+ "integrity": "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg==",
+ "cpu": [
+ "x64",
+ "arm64",
+ "wasm32",
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "os": [
+ "darwin",
+ "linux",
+ "win32"
+ ],
+ "dependencies": {
+ "@neon-rs/load": "^0.0.4",
+ "detect-libc": "2.0.2"
+ },
+ "optionalDependencies": {
+ "@libsql/darwin-arm64": "0.5.29",
+ "@libsql/darwin-x64": "0.5.29",
+ "@libsql/linux-arm-gnueabihf": "0.5.29",
+ "@libsql/linux-arm-musleabihf": "0.5.29",
+ "@libsql/linux-arm64-gnu": "0.5.29",
+ "@libsql/linux-arm64-musl": "0.5.29",
+ "@libsql/linux-x64-gnu": "0.5.29",
+ "@libsql/linux-x64-musl": "0.5.29",
+ "@libsql/win32-x64-msvc": "0.5.29"
+ }
+ },
+ "node_modules/load-esm": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz",
+ "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://buymeacoffee.com/borewit"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=13.2.0"
+ }
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/log-symbols": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
+ "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-unicode-supported": "^2.0.0",
+ "yoctocolors": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/logform": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/logform/node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mathjs": {
+ "version": "15.2.0",
+ "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-15.2.0.tgz",
+ "integrity": "sha512-UAQzSVob9rNLdGpqcFMYmSu9dkuLYy7Lr2hBEQS5SHQdknA9VppJz3cy2KkpMzTODunad6V6cNv+5kOLsePLow==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "^7.26.10",
+ "complex.js": "^2.2.5",
+ "decimal.js": "^10.4.3",
+ "escape-latex": "^1.2.0",
+ "fraction.js": "^5.2.1",
+ "javascript-natural-sort": "^0.7.1",
+ "seedrandom": "^3.0.5",
+ "tiny-emitter": "^2.1.0",
+ "typed-function": "^4.2.1"
+ },
+ "bin": {
+ "mathjs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz",
+ "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/memjs": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/memjs/-/memjs-1.3.2.tgz",
+ "integrity": "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/memory-pager": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.8"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mongodb-connection-string-url": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.2.tgz",
+ "integrity": "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@types/whatwg-url": "^13.0.0",
+ "whatwg-url": "^14.1.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/mongoose": {
+ "version": "9.9.4",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.9.4.tgz",
+ "integrity": "sha512-Gc7buf0ExrOZ3t8MD8tVzTek7Qr5d+tEwj4GRFmHCtTZvpaDb38EVsXgCkYacPL9ICAftgS+FGe+dQHLRLHhcw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "kareem": "3.3.0",
+ "mongodb": "~7.5",
+ "mpath": "0.9.0",
+ "mquery": "6.0.0",
+ "ms": "2.1.3",
+ "sift": "17.1.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mongoose"
+ }
+ },
+ "node_modules/mongoose/node_modules/gcp-metadata": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz",
+ "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/mongoose/node_modules/google-logging-utils": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz",
+ "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/mongoose/node_modules/mongodb": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.5.0.tgz",
+ "integrity": "sha512-5FnrEDLnvp6ycUOGLNLLU33BfCx2qmp2mJjGPDwKLruYsVzXVSK5fsGpoDXvsXJwBfBsD7ebMRdawbDxC2814g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@mongodb-js/saslprep": "^1.4.11",
+ "bson": "^7.2.0",
+ "mongodb-connection-string-url": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.806.0",
+ "@mongodb-js/zstd": "^7.0.0",
+ "gcp-metadata": "^7.0.1",
+ "kerberos": "^7.0.0",
+ "mongodb-client-encryption": "^7.2.0",
+ "snappy": "^7.3.2",
+ "socks": "^2.8.6"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "gcp-metadata": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ },
+ "socks": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mpath": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
+ "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mquery": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz",
+ "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "mustache": "bin/mustache"
+ }
+ },
+ "node_modules/mute-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz",
+ "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/natural": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/natural/-/natural-8.1.1.tgz",
+ "integrity": "sha512-Ucb+lsUcGxUqu3rn8cwHjT6gJQosO63nIX/aBQXB3+IDkNbFV7PuviysO+Rzz3aKn7PZhPj3bNF4PS9gDVjYCQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "afinn-165": "^2.0.2",
+ "afinn-165-financialmarketnews": "^3.0.0",
+ "apparatus": "^0.0.10",
+ "dotenv": "^17.3.1",
+ "memjs": "^1.3.2",
+ "mongoose": "^9.2.1",
+ "pg": "^8.18.0",
+ "redis": "^5.11.0",
+ "safe-stable-stringify": "^2.5.0",
+ "stopwords-iso": "^1.1.0",
+ "sylvester": "^0.0.21",
+ "underscore": "^1.13.0",
+ "uuid": "^13.0.0",
+ "wordnet-db": "^3.1.14"
+ },
+ "engines": {
+ "node": ">=0.4.10"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+ "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/netmask": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz",
+ "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/node-forge": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+ "dev": true,
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "optional": true,
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/node-rsa": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz",
+ "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "asn1": "^0.2.4"
+ }
+ },
+ "node_modules/node-sql-parser": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-5.4.0.tgz",
+ "integrity": "sha512-jVe6Z61gPcPjCElPZ6j8llB3wnqGcuQzefim1ERsqIakxnEy5JlzV7XKdO1KmacRG5TKwPc4vJTgSRQ0LfkbFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@types/pegjs": "^0.10.0",
+ "big-integer": "^1.6.48"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
+ "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^4.0.0",
+ "unicorn-magic": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nunjucks": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz",
+ "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "a-sync-waterfall": "^1.0.0",
+ "asap": "^2.0.3",
+ "commander": "^5.1.0"
+ },
+ "bin": {
+ "nunjucks-precompile": "bin/precompile"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nunjucks/node_modules/commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fn.name": "1.x.x"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/onnxruntime-common": {
+ "version": "1.24.3",
+ "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz",
+ "integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/onnxruntime-node": {
+ "version": "1.24.3",
+ "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz",
+ "integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32",
+ "darwin",
+ "linux"
+ ],
+ "dependencies": {
+ "adm-zip": "^0.5.16",
+ "global-agent": "^3.0.0",
+ "onnxruntime-common": "1.24.3"
+ }
+ },
+ "node_modules/onnxruntime-web": {
+ "version": "1.26.0-dev.20260416-b7804b056c",
+ "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.26.0-dev.20260416-b7804b056c.tgz",
+ "integrity": "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "flatbuffers": "^25.1.24",
+ "guid-typescript": "^1.0.9",
+ "long": "^5.2.3",
+ "onnxruntime-common": "1.24.0-dev.20251116-b39e144322",
+ "platform": "^1.3.6",
+ "protobufjs": "^7.2.4"
+ }
+ },
+ "node_modules/onnxruntime-web/node_modules/onnxruntime-common": {
+ "version": "1.24.0-dev.20251116-b39e144322",
+ "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.0-dev.20251116-b39e144322.tgz",
+ "integrity": "sha512-BOoomdHYmNRL5r4iQ4bMvsl2t0/hzVQ3OM3PHD0gxeXu1PmggqBv3puZicEUVOA3AtHHYmqZtjMj9FOfGrATTw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/onnxruntime-web/node_modules/protobufjs": {
+ "version": "7.6.6",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz",
+ "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.5",
+ "@protobufjs/eventemitter": "^1.1.1",
+ "@protobufjs/fetch": "^1.1.1",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.1",
+ "@types/node": ">=13.7.0",
+ "long": "^5.3.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/open": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "wsl-utils": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/openai": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-7.8.0.tgz",
+ "integrity": "sha512-/2g9JzdnXNcjX1W/UlSNu+OdSFDAaAVt0n9Onom0kPenH54o59G2WrX/xjTnr26UHNSh6hxcAf58doGYRme2rw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=22.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-provider-node": ">=3.972.0 <4",
+ "@smithy/hash-node": ">=4.3.0 <5",
+ "@smithy/signature-v4": ">=5.4.0 <6",
+ "undici": ">=5 <9",
+ "ws": "^8.21.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-provider-node": {
+ "optional": true
+ },
+ "@smithy/hash-node": {
+ "optional": true
+ },
+ "@smithy/signature-v4": {
+ "optional": true
+ },
+ "undici": {
+ "optional": true
+ },
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true,
+ "license": "(WTFPL OR MIT)",
+ "bin": {
+ "opener": "bin/opener-bin.js"
+ }
+ },
+ "node_modules/ora": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz",
+ "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.6.2",
+ "cli-cursor": "^5.0.0",
+ "cli-spinners": "^3.2.0",
+ "is-interactive": "^2.0.0",
+ "is-unicode-supported": "^2.1.0",
+ "log-symbols": "^7.0.1",
+ "stdin-discarder": "^0.3.2",
+ "string-width": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/string-width": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
+ "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
+ "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "eventemitter3": "^4.0.4",
+ "p-timeout": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue/node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-retry/node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/p-timeout": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+ "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "p-finally": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pac-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-1aU+1mpj3DrQPfo3gh+3Gap3G5x+axnMx1P/y0ZF2ch7kb2meyOCAH8K2k9d27ROsTE7TnAerzxqF9aon2jqnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "get-uri": "8.0.1",
+ "http-proxy-agent": "9.1.0",
+ "https-proxy-agent": "9.1.0",
+ "pac-resolver": "9.0.1",
+ "quickjs-wasi": "^2.2.0",
+ "socks-proxy-agent": "10.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/pac-proxy-agent/node_modules/agent-base": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+ "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/pac-resolver": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-9.0.1.tgz",
+ "integrity": "sha512-lJbS008tmkj08VhoM8Hzuv/VE5tK9MS0OIQ/7+s0lIF+BYhiQWFYzkSpML7lXs9iBu2jfmzBTLzhe9n6BX+dYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "degenerator": "7.0.1",
+ "netmask": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "quickjs-wasi": "^2.2.0"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "optional": true
+ },
+ "node_modules/papaparse": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz",
+ "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
+ "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/patch-console": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz",
+ "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
+ "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+ "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/pdf-parse": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-2.4.5.tgz",
+ "integrity": "sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/canvas": "0.1.80",
+ "pdfjs-dist": "5.4.296"
+ },
+ "bin": {
+ "pdf-parse": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": ">=20.16.0 <21 || >=22.3.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/mehmet-kozan"
+ }
+ },
+ "node_modules/pdf-parse/node_modules/pdfjs-dist": {
+ "version": "5.4.296",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz",
+ "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=20.16.0 || >=22.3.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^0.1.80"
+ }
+ },
+ "node_modules/pdfjs-dist": {
+ "version": "6.2.108",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-6.2.108.tgz",
+ "integrity": "sha512-YxFb+SQcodN2rnX9Tn3dHYlqfb7NjlzzfONPpJd+AKoKtUjEdevTfbC07d5TcczzOK6261auRkP/M8OBHs9vFQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=22.13.0 || >=24"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^1.0.0"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-1.0.8.tgz",
+ "integrity": "sha512-/SaLcvlqGWdm0HSCWMgHu7cjJiQXfP8/mOY+6dUyV9flQz7sPBBZ+ed2zYtoukojPmxOaL7bm+d/G4GeWWoN7g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "e2e/*"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas-android-arm64": "1.0.8",
+ "@napi-rs/canvas-darwin-arm64": "1.0.8",
+ "@napi-rs/canvas-darwin-x64": "1.0.8",
+ "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.8",
+ "@napi-rs/canvas-linux-arm64-gnu": "1.0.8",
+ "@napi-rs/canvas-linux-arm64-musl": "1.0.8",
+ "@napi-rs/canvas-linux-riscv64-gnu": "1.0.8",
+ "@napi-rs/canvas-linux-x64-gnu": "1.0.8",
+ "@napi-rs/canvas-linux-x64-musl": "1.0.8",
+ "@napi-rs/canvas-win32-arm64-msvc": "1.0.8",
+ "@napi-rs/canvas-win32-x64-msvc": "1.0.8"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-android-arm64": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-1.0.8.tgz",
+ "integrity": "sha512-5+nkh8i3gt6lqS/d2jTZ1xAn6tdgtB4Lf1mW6T0Qm5/rXNwBuV1sAEyLEWan5o9gJPU/GuvHR3rvSeZ+FaGrbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-darwin-arm64": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-1.0.8.tgz",
+ "integrity": "sha512-7jQ47gi+fZ7KJmfc/5rNyy1CYw/cu4kZ0KPIYbo9UUgSdW0bKQJpt+WihEor6s4Lyp7+xc3a+3HeyXmAEbbnPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-darwin-x64": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-1.0.8.tgz",
+ "integrity": "sha512-rRjDMZs9pIRKGxgijwezplKc1RnJsqUokrA9h88bbTkqQ+7ePj0ZN4ZnZDy8Vu0tXs7KRlI2tQLaK4mx9QlxHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-1.0.8.tgz",
+ "integrity": "sha512-jGcCd+8ra6Q61xKqZeiItujTpp9a9eRLcQ0jW6qYNku+WpupqOPFPY0SrsuSnXFviJwkpKYT9p7QrB4lsf3LNQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-1.0.8.tgz",
+ "integrity": "sha512-od6I2Y7kU7i1SwZYG2EKW8rWz6JiedtPpko4WEe1DDsiikrfaotVBCRaUTM5/yeZKaZ92EatoAS+5xG+6uJlYA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-arm64-musl": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-1.0.8.tgz",
+ "integrity": "sha512-yYkPbJDJiWj6N0gASA3CAvRypZmVpJnxU0DQg3aBhneLDQde9TPLKADsQkobNoJUtTT/lj46aWpzT48PDb3Qcg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-1.0.8.tgz",
+ "integrity": "sha512-PB00MSKAp4VwK/xwe6duKxRKmH8UH4GIl1pqHSbxng0jnU9Dr7FwaDypDiqwNFZ774N+8G7mJLGuLtg9NTcQsg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-x64-gnu": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-1.0.8.tgz",
+ "integrity": "sha512-TWM2XWJoitLiIPCvgJh7SriC+L/T9qkYCVzC66AidsZy0QP1hkKzBzVwshCdcA3q6fIn3yE0ISbq4lMJSy8jFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-linux-x64-musl": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-1.0.8.tgz",
+ "integrity": "sha512-hb20MxKXXb5IB7AAwN8UHz9WRsa2HmdZfjsDCzjElwJoeV1aotVEwFU4FrFQcYQVzsJQLeaCc/2Qdt/0Q72mMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pdfjs-dist/node_modules/@napi-rs/canvas-win32-x64-msvc": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-1.0.8.tgz",
+ "integrity": "sha512-XkrVqKb+pxyba7kjy2LJvABFVBTE0DNpEl7MrG4OYUmaWarrXH+t54z/Czj2YxCKtizYTV4mg6phNm3x24qjhQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/pem": {
+ "version": "1.14.8",
+ "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.8.tgz",
+ "integrity": "sha512-ZpbOf4dj9/fQg5tQzTqv4jSKJQsK7tPl0pm4/pvPcZVjZcJg7TMfr3PBk6gJH97lnpJDu4e4v8UUqEz5daipCg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es6-promisify": "^7.0.0",
+ "md5": "^2.3.0",
+ "os-tmpdir": "^1.0.2",
+ "which": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg": {
+ "version": "8.23.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz",
+ "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "pg-connection-string": "^2.14.0",
+ "pg-pool": "^3.14.0",
+ "pg-protocol": "^1.16.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.4.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
+ "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz",
+ "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz",
+ "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pkce-challenge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
+ "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/platform": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
+ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/playwright": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
+ "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "playwright-core": "1.62.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
+ "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/playwright-extra": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/playwright-extra/-/playwright-extra-4.3.6.tgz",
+ "integrity": "sha512-q2rVtcE8V8K3vPVF1zny4pvwZveHLH8KBuVU2MoE3Jw4OKVoBWsHI9CH9zPydovHHOCDxjGN2Vg+2m644q3ijA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "playwright": "*",
+ "playwright-core": "*"
+ },
+ "peerDependenciesMeta": {
+ "playwright": {
+ "optional": true
+ },
+ "playwright-core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/posthog-node": {
+ "version": "5.24.17",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.24.17.tgz",
+ "integrity": "sha512-mdb8TKt+YCRbGQdYar3AKNUPCyEiqcprScF4unYpGALF6HlBaEuO6wPuIqXXpCWkw4VclJYCKbb6lq6pH6bJeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@posthog/core": "1.23.1"
+ },
+ "engines": {
+ "node": "^20.20.0 || >=22.22.0"
+ }
+ },
+ "node_modules/pretty-ms": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.1.tgz",
+ "integrity": "sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse-ms": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/promise-limit": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
+ "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/promptfoo": {
+ "version": "0.122.2",
+ "resolved": "https://registry.npmjs.org/promptfoo/-/promptfoo-0.122.2.tgz",
+ "integrity": "sha512-biyTIbtpH3ZNcnibhYkmvVe/N6VSmf0RumMrEgB+WRLOrUoEvgq7cXsxXguYmTlz44MWV1Un+TzIpkyqZ8DQgA==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "src/app",
+ "site"
+ ],
+ "dependencies": {
+ "@anthropic-ai/sdk": "0.117.1",
+ "@apidevtools/json-schema-ref-parser": "^16.0.0",
+ "@hono/node-server": "2.1.1",
+ "@inquirer/checkbox": "^5.1.0",
+ "@inquirer/confirm": "^6.0.8",
+ "@inquirer/core": "^11.1.5",
+ "@inquirer/editor": "^5.0.8",
+ "@inquirer/input": "^5.0.8",
+ "@inquirer/search": "^4.1.8",
+ "@inquirer/select": "^5.1.0",
+ "@libsql/client": "^0.17.3",
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
+ "@opentelemetry/resources": "^2.6.0",
+ "@opentelemetry/sdk-trace-base": "^2.6.0",
+ "@opentelemetry/sdk-trace-node": "^2.6.0",
+ "@opentelemetry/semantic-conventions": "^1.40.0",
+ "@types/ws": "^8.18.1",
+ "ai": "^6.0.190",
+ "ajv": "^8.18.0",
+ "ajv-formats": "^3.0.1",
+ "async": "^3.2.6",
+ "binary-extensions": "^3.1.0",
+ "cache-manager": ">=7.2.8 <7.2.10",
+ "chalk": "^6.0.0",
+ "chokidar": "5.0.0",
+ "cli-progress": "^3.12.0",
+ "cli-table3": "^0.6.5",
+ "commander": "^14.0.3",
+ "compression": "^1.8.1",
+ "cors": "^2.8.6",
+ "csv-parse": "^7.0.0",
+ "csv-stringify": "^6.7.0",
+ "debounce": "^3.0.0",
+ "dedent": "^1.7.2",
+ "dotenv": "^17.3.1",
+ "drizzle-orm": "^0.45.1",
+ "execa": "^10.0.0",
+ "express": "^5.2.1",
+ "exsolve": "^1.0.8",
+ "fast-deep-equal": "^3.1.3",
+ "fast-safe-stringify": "^2.1.1",
+ "fast-xml-parser": "^5.7.1",
+ "fastest-levenshtein": "^1.0.16",
+ "gcp-metadata": "^9.0.2",
+ "glob": "^13.0.6",
+ "http-z": "^8.1.1",
+ "istextorbinary": "^9.5.0",
+ "js-rouge": "^3.2.0",
+ "js-yaml": "5.3.0",
+ "json5": "^2.2.3",
+ "keyv": "^5.6.0",
+ "keyv-file": "^5.3.3",
+ "lru-cache": "^11.3.0",
+ "mathjs": "^15.1.1",
+ "minimatch": "^10.2.4",
+ "nunjucks": "^3.2.4",
+ "openai": "^7.1.0",
+ "opener": "^1.5.2",
+ "ora": "^9.3.0",
+ "parse5": "^8.0.0",
+ "posthog-node": "~5.24.10",
+ "protobufjs": "^8.7.2",
+ "proxy-agent": "^8.0.0",
+ "proxy-from-env": "^2.1.0",
+ "python-shell": "^5.0.0",
+ "rfdc": "^1.4.1",
+ "rxjs": "^7.8.2",
+ "saxes": "^6.0.0",
+ "semver": "^7.7.4",
+ "simple-git": "^3.33.0",
+ "socket.io": "^4.8.3",
+ "socket.io-client": "^4.8.3",
+ "text-extensions": "^3.1.0",
+ "tsx": "^4.23.11",
+ "undici": ">=7.29.0 <8",
+ "winston": "^3.19.0",
+ "ws": "^8.21.3",
+ "zod": "^4.3.6"
+ },
+ "bin": {
+ "pf": "dist/src/entrypoint.js",
+ "promptfoo": "dist/src/entrypoint.js"
+ },
+ "engines": {
+ "node": ">=22.22.0"
+ },
+ "optionalDependencies": {
+ "@anthropic-ai/claude-agent-sdk": "0.3.234",
+ "@aws-sdk/client-bedrock-agent-runtime": "^3.1045.0",
+ "@aws-sdk/client-bedrock-runtime": "^3.1045.0",
+ "@aws-sdk/client-s3": "^3.1003.0",
+ "@aws-sdk/client-sagemaker-runtime": "^3.1045.0",
+ "@aws-sdk/credential-provider-sso": "^3.972.16",
+ "@azure/ai-projects": "^2.1.1",
+ "@azure/identity": "^4.13.0",
+ "@azure/msal-node": "^5.2.0",
+ "@azure/openai-assistants": "^1.0.0-beta.6",
+ "@azure/storage-blob": "^12.31.0",
+ "@fal-ai/client": "~1.10.1",
+ "@googleapis/sheets": "^14.0.0",
+ "@huggingface/transformers": "^4.0.0",
+ "@ibm-cloud/watsonx-ai": "^1.7.16",
+ "@langfuse/client": "^5.10.1",
+ "@modelcontextprotocol/sdk": "^1.30.0",
+ "@openai/agents": "^0.11.3",
+ "@openai/codex-sdk": "^0.144.0",
+ "@openai/codex-security": "^0.1.18",
+ "@opencode-ai/sdk": "^1.18.18",
+ "@playwright/browser-chromium": "^1.60.0",
+ "@rollup/rollup-linux-x64-gnu": "^4.62.0",
+ "@slack/web-api": "^8.0.0",
+ "@smithy/node-http-handler": "^4.4.14",
+ "@swc/core": "^1.16.0",
+ "@swc/core-darwin-arm64": "^1.16.0",
+ "@swc/core-darwin-x64": "^1.16.0",
+ "@swc/core-linux-x64-gnu": "^1.16.0",
+ "@swc/core-linux-x64-musl": "^1.16.0",
+ "@swc/core-win32-x64-msvc": "^1.16.0",
+ "google-auth-library": "^11.0.2",
+ "hono": "^4.13.2",
+ "ibm-cloud-sdk-core": "^5.6.0",
+ "jks-js": "^1.1.5",
+ "natural": "^8.1.1",
+ "node-sql-parser": "^5.4.0",
+ "pdf-parse": "^2.4.5",
+ "pem": "~1.14.8",
+ "playwright": "^1.60.0",
+ "playwright-extra": "^4.3.6",
+ "read-excel-file": "^9.3.9",
+ "sharp": "^0.35.3"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex": {
+ "version": "0.144.6",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6.tgz",
+ "integrity": "sha512-wk+2CWiBNXiJLBoN2D08N9RceWkSBnlgk5g2K1a4CXrP/C0gdlHyRUG7RFzm9y41DCK/7tvCct233JVxyFmznw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "bin": {
+ "codex": "bin/codex.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "@openai/codex-darwin-arm64": "npm:@openai/codex@0.144.6-darwin-arm64",
+ "@openai/codex-darwin-x64": "npm:@openai/codex@0.144.6-darwin-x64",
+ "@openai/codex-linux-arm64": "npm:@openai/codex@0.144.6-linux-arm64",
+ "@openai/codex-linux-x64": "npm:@openai/codex@0.144.6-linux-x64",
+ "@openai/codex-win32-arm64": "npm:@openai/codex@0.144.6-win32-arm64",
+ "@openai/codex-win32-x64": "npm:@openai/codex@0.144.6-win32-x64"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-darwin-arm64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-darwin-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-darwin-arm64.tgz",
+ "integrity": "sha512-6zgvh70MzBNSeT17HEhSOrmmGGZGAKzSC7x6JAq+edkJkdPYA9P0I1tG7aJ49GlBkBxuC+MKBH1qm6+2Cghcww==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-darwin-x64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-darwin-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-darwin-x64.tgz",
+ "integrity": "sha512-THRyPG0zSU6M8NQAge1LHEHsJDnoH4BpKsfJHB/qe3Fm+Wf6zqAmWJFlOKzBm27m0K2Hq3za4Ac2I5p5i4yp/A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-linux-arm64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-linux-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-linux-arm64.tgz",
+ "integrity": "sha512-PGiLXMN+2IQRkf7tOLi64dMInjU1pRLbz0Rwfj/yt2Y97SZQqAjFQoi2wmswmqtqMDnfwCPTC1DRXVQkvU6T6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-linux-x64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-linux-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-linux-x64.tgz",
+ "integrity": "sha512-4E7EnzCg0OnBxCyYnwJ+qnZwWHYe0YScr5ucKWbngE9u4+0XrpWELqq2Kn9jl5GZK8MDjU7PrJwFIwusHOHjuw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-sdk": {
+ "version": "0.144.6",
+ "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.144.6.tgz",
+ "integrity": "sha512-jFgXHjFq2//PTfJa8CySqhUilRBPVmCLuQoH5F+iJ2x4owZwPlnqmIbCkWIJJ6IxDuQdjf3ewg0LaDtc3i6h9Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@openai/codex": "0.144.6"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-win32-arm64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-win32-arm64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-win32-arm64.tgz",
+ "integrity": "sha512-SpMjXJLW43JzMP0K62mVcYfmFcpk0BK4AOgYmWSfyZHs3iRtHMd0UYw7605n/9lwkT2EqbwQLT2omZFeKJFzwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/@openai/codex-win32-x64": {
+ "name": "@openai/codex",
+ "version": "0.144.6-win32-x64",
+ "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.144.6-win32-x64.tgz",
+ "integrity": "sha512-dN39VnjEthKz5io1RNWwZDtErdSn07nW3pGUgvlA6DMxgm/nuGaIAZO/sG/Hgxq/x5j9HteAENfrFgVkpZ0lFg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/promptfoo/node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/promptfoo/node_modules/readdirp": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
+ "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.8.0.tgz",
+ "integrity": "sha512-N3xhQ5yyBx3vQq4gubBfASzYhJGNzeDbjqBpu61g7UVylsN/qyffU96TKWD3GbbLOKF82VGNRNvv1+BFgE31Eg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "long": "^5.3.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-agent": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-8.0.2.tgz",
+ "integrity": "sha512-idLLRewuemWd7GH/BDJzGiB0dWGfT2SQs3jy6NtZtGWU9uPTTSdeC1/cdbqLwgzhfv027daGFuXX426e2Eg20A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "9.1.0",
+ "https-proxy-agent": "9.1.0",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "9.1.0",
+ "proxy-from-env": "^2.0.0",
+ "socks-proxy-agent": "10.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/proxy-agent-negotiate": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz",
+ "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "kerberos": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "kerberos": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/proxy-agent/node_modules/agent-base": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+ "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/http-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/https-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
+ "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/lupomontero"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/python-shell": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/python-shell/-/python-shell-5.0.0.tgz",
+ "integrity": "sha512-RUOOOjHLhgR1MIQrCtnEqz/HJ1RMZBIN+REnpSUrfft2bXqXy69fwJASVziWExfFXsR1bCY0TznnHooNsCo0/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/quickjs-wasi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-2.2.0.tgz",
+ "integrity": "sha512-zQxXmQMrEoD3S+jQdYsloq4qAuaxKFHZj6hHqOYGwB2iQZH+q9e/lf5zQPXCKOk0WJuAjzRFbO4KwHIp2D05Iw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/range-parser": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
+ "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.4",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
+ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-reconciler": {
+ "version": "0.33.0",
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
+ "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.0"
+ }
+ },
+ "node_modules/read-excel-file": {
+ "version": "9.3.10",
+ "resolved": "https://registry.npmjs.org/read-excel-file/-/read-excel-file-9.3.10.tgz",
+ "integrity": "sha512-zFcBdzunLCGBmLRT4Q3mLPJnhKPAXXfGAZ83feODlEqX2aRUbgF1h/n2oY0UF7I8tVwnJNRas6fTRD5veDUs+g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "fflate": "^0.8.3",
+ "saxen": "^11.1.0",
+ "unzipper-esm": "^0.13.3",
+ "worker-f": "^0.1.12"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/read-excel-file/node_modules/fflate": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
+ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/redis": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-5.12.1.tgz",
+ "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@redis/bloom": "5.12.1",
+ "@redis/client": "5.12.1",
+ "@redis/json": "5.12.1",
+ "@redis/search": "5.12.1",
+ "@redis/time-series": "5.12.1"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/restore-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+ "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/retry-axios": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-2.6.0.tgz",
+ "integrity": "sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=10.7.0"
+ },
+ "peerDependencies": {
+ "axios": "*"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "optional": true,
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/roarr": {
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
+ "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "detect-node": "^2.0.4",
+ "globalthis": "^1.0.1",
+ "json-stringify-safe": "^5.0.1",
+ "semver-compare": "^1.0.0",
+ "sprintf-js": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/robot3": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/robot3/-/robot3-0.4.1.tgz",
+ "integrity": "sha512-hzjy826lrxzx8eRgv80idkf8ua1JAepRc9Efdtj03N3KNJuznQCPlyCJ7gnUmDFwZCLQjxy567mQVKmdv2BsXQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/run-applescript": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/saxen": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/saxen/-/saxen-11.1.1.tgz",
+ "integrity": "sha512-J4BkmJFaM7VgE7pgkFGsNEcqqM3h7+Mz80vfLWFhx7uNOCOXIu6LLjQHYWNejdst3pf/3JUaBIG9+pkk1umlow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 20.12"
+ }
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/seedrandom": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
+ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/serialize-error": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
+ "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "type-fest": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/serialize-error/node_modules/type-fest": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sharp": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
+ "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.1.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.8.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.35.4",
+ "@img/sharp-darwin-x64": "0.35.4",
+ "@img/sharp-freebsd-wasm32": "0.35.4",
+ "@img/sharp-libvips-darwin-arm64": "1.3.3",
+ "@img/sharp-libvips-darwin-x64": "1.3.3",
+ "@img/sharp-libvips-linux-arm": "1.3.3",
+ "@img/sharp-libvips-linux-arm64": "1.3.3",
+ "@img/sharp-libvips-linux-ppc64": "1.3.3",
+ "@img/sharp-libvips-linux-riscv64": "1.3.3",
+ "@img/sharp-libvips-linux-s390x": "1.3.3",
+ "@img/sharp-libvips-linux-x64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3",
+ "@img/sharp-linux-arm": "0.35.4",
+ "@img/sharp-linux-arm64": "0.35.4",
+ "@img/sharp-linux-ppc64": "0.35.4",
+ "@img/sharp-linux-riscv64": "0.35.4",
+ "@img/sharp-linux-s390x": "0.35.4",
+ "@img/sharp-linux-x64": "0.35.4",
+ "@img/sharp-linuxmusl-arm64": "0.35.4",
+ "@img/sharp-linuxmusl-x64": "0.35.4",
+ "@img/sharp-webcontainers-wasm32": "0.35.4",
+ "@img/sharp-win32-arm64": "0.35.4",
+ "@img/sharp-win32-ia32": "0.35.4",
+ "@img/sharp-win32-x64": "0.35.4"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/sharp/node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sift": {
+ "version": "17.1.3",
+ "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
+ "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/simple-git": {
+ "version": "3.36.0",
+ "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz",
+ "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@kwsites/file-exists": "^1.1.1",
+ "@kwsites/promise-deferred": "^1.1.1",
+ "@simple-git/args-pathspec": "^1.0.3",
+ "@simple-git/argv-parser": "^1.1.0",
+ "debug": "^4.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/steveukx/git-js?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+ "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.3",
+ "is-fullwidth-code-point": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/smol-toml": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
+ "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
+ "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "cors": "~2.8.5",
+ "debug": "~4.4.1",
+ "engine.io": "~6.6.0",
+ "socket.io-adapter": "~2.5.2",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.5.8",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz",
+ "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "~4.4.1",
+ "ws": "~8.21.0"
+ }
+ },
+ "node_modules/socket.io-client": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
+ "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1",
+ "engine.io-client": "~6.6.1",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.7",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
+ "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io/node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
+ "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.1.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-10.1.0.tgz",
+ "integrity": "sha512-WlMj/67cEJ6MDI1OcsnjuYKDNDoyPCCYZ249kuuXPiMDw9F8PXkVaQ7YWu3siTydfQ/4BEZcvGzu+aYvz7dDCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/socks-proxy-agent/node_modules/agent-base": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+ "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sparse-bitfield": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "memory-pager": "^1.0.2"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/stack-utils/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/standardwebhooks": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz",
+ "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/base64": "^1.0.0",
+ "fast-sha256": "^1.3.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/stdin-discarder": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz",
+ "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stopwords-iso": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stopwords-iso/-/stopwords-iso-1.1.0.tgz",
+ "integrity": "sha512-I6GPS/E0zyieHehMRPQcqkiBMJKGgLta+1hREixhoLPqEA0AlVFiC43dl8uPpmkkeRdDMzYRWFWk5/l9x7nmNg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+ "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz",
+ "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anynum": "^1.0.1"
+ }
+ },
+ "node_modules/strtok3": {
+ "version": "10.3.5",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz",
+ "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/sylvester": {
+ "version": "0.0.21",
+ "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.21.tgz",
+ "integrity": "sha512-yUT0ukFkFEt4nb+NY+n2ag51aS/u9UHXoZw+A4jgD77/jzZsBoSDHuqysrVCBC4CYR4TYvUJq54ONpXgDBH8tA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.2.6"
+ }
+ },
+ "node_modules/tagged-tag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terminal-size": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/terminal-size/-/terminal-size-4.0.1.tgz",
+ "integrity": "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/text-extensions": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-3.1.0.tgz",
+ "integrity": "sha512-anOjtXr8OT5w4vc/2mP4AYTCE0GWc/21icGmaHtBHnI7pN7o01a/oqG9m06/rGzoAsDm/WNzggBpqptuCmRlZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/textextensions": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz",
+ "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==",
+ "dev": true,
+ "license": "Artistic-2.0",
+ "dependencies": {
+ "editions": "^6.21.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/token-types": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz",
+ "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@borewit/text-codec": "^0.2.1",
+ "@tokenizer/token": "^0.3.0",
+ "ieee754": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/tokenx": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/tokenx/-/tokenx-1.6.0.tgz",
+ "integrity": "sha512-CKTjk345ajvBAUp5xUI9a5KKN0zU0lBueVHQbCskH1Hp6WkUKsPW2qGCYNs0pxNyfzxfo+IIjdt2W4sMbw/qBw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
+ "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/ts-algebra": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz",
+ "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.23.12",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz",
+ "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.28.0"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz",
+ "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "optional": true,
+ "dependencies": {
+ "tagged-tag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
+ "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.0.0",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/type-is/node_modules/content-type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
+ "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/typed-function": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz",
+ "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/uint8array-extras": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
+ "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/underscore": {
+ "version": "1.13.8",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
+ "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/undici": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+ "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
+ "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+ "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/universal-user-agent": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
+ "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unzipper-esm": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/unzipper-esm/-/unzipper-esm-0.13.3.tgz",
+ "integrity": "sha512-LUO6VZ6fCzkDbdMev0/fOhoIeVGKaOkTIOoYxVLE0SQjfvmAHK+oywl7lfhloSZIsdGJ25mJ18Mtd9CyTASjrA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.2",
+ "node-int64": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/url-template": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==",
+ "dev": true,
+ "license": "BSD",
+ "optional": true
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz",
+ "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/version-range": {
+ "version": "4.15.0",
+ "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz",
+ "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==",
+ "dev": true,
+ "license": "Artistic-2.0",
+ "engines": {
+ "node": ">=4"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-command": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/which-command/-/which-command-0.1.0.tgz",
+ "integrity": "sha512-XZyoF5/5hZtXitIwzrU4NKK+Wtbb9aB9CezUEw2Q0wlYK8NUYQxC1rRXgNueYLtBAJwXIb+/tFVk4dozciNJMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "which-command": "cli.js"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/which-command?sponsor=1"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-6.0.0.tgz",
+ "integrity": "sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "string-width": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/widest-line/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/widest-line/node_modules/string-width": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz",
+ "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/widest-line/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/winston": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
+ "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.8",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.7.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston/node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/winston/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wordnet-db": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/wordnet-db/-/wordnet-db-3.1.14.tgz",
+ "integrity": "sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/worker-f": {
+ "version": "0.1.20",
+ "resolved": "https://registry.npmjs.org/worker-f/-/worker-f-0.1.20.tgz",
+ "integrity": "sha512-7z5K5z4x++FykhpDTfriT/dOu7CmSap9BBv38DVFU3CD38obopq+DoFH75yBV3butpGm+OeqR9BKdSvYJSnUfQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+ "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/wsl-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-wsl": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xml-naming": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz",
+ "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/xmlhttprequest-ssl": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+ "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/yoctocolors": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz",
+ "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yoga-layout": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
+ "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/zod": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz",
+ "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.25.2",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
+ "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "peerDependencies": {
+ "zod": "^3.25.28 || ^4"
+ }
+ }
+ }
+}
diff --git a/benchmarks/codex-mcp/package.json b/benchmarks/codex-mcp/package.json
new file mode 100644
index 00000000..f0643007
--- /dev/null
+++ b/benchmarks/codex-mcp/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "vidxp-codex-mcp-eval",
+ "private": true,
+ "version": "0.0.0",
+ "description": "Paired Codex evaluation with and without the local VidXP MCP server",
+ "engines": {
+ "node": ">=22.22.0"
+ },
+ "scripts": {
+ "check": "promptfoo validate -c promptfooconfig.yaml",
+ "preflight": "node scripts/preflight.mjs",
+ "eval:smoke": "npm run preflight && promptfoo eval -c promptfooconfig.yaml --filter-first-n 2 --repeat 1 --no-cache --no-share",
+ "eval:pilot": "npm run preflight && promptfoo eval -c promptfooconfig.yaml --repeat 3 --no-cache --no-share",
+ "view": "promptfoo view"
+ },
+ "devDependencies": {
+ "@openai/codex-sdk": "0.151.0",
+ "promptfoo": "0.122.2"
+ }
+}
diff --git a/benchmarks/codex-mcp/promptfooconfig.yaml b/benchmarks/codex-mcp/promptfooconfig.yaml
new file mode 100644
index 00000000..d8e1e837
--- /dev/null
+++ b/benchmarks/codex-mcp/promptfooconfig.yaml
@@ -0,0 +1,134 @@
+# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
+description: VidXP Codex MCP-on versus MCP-off temporal evidence evaluation
+
+prompts:
+ - id: video-evidence-task
+ label: Fixed video evidence task
+ raw: file://prompts/video-evidence.txt
+
+providers:
+ - id: openai:codex-sdk
+ label: codex-vidxp-mcp
+ config:
+ model: "{{ env.VIDXP_EVAL_MODEL | default('gpt-5.6-sol') }}"
+ model_reasoning_effort: "{{ env.VIDXP_EVAL_REASONING | default('medium') }}"
+ maxRetries: 0
+ working_dir: "{{ env.VIDXP_EVAL_WORKSPACE }}"
+ skip_git_repo_check: true
+ sandbox_mode: read-only
+ approval_policy: never
+ network_access_enabled: false
+ web_search_mode: disabled
+ persist_threads: false
+ enable_streaming: true
+ output_schema: &result_schema
+ type: object
+ additionalProperties: false
+ required:
+ - video_id
+ - answer
+ - start_seconds
+ - end_seconds
+ - modalities
+ - evidence
+ properties:
+ video_id:
+ type: string
+ answer:
+ type: string
+ start_seconds:
+ type:
+ - number
+ - "null"
+ end_seconds:
+ type:
+ - number
+ - "null"
+ modalities:
+ type: array
+ uniqueItems: true
+ items:
+ type: string
+ enum:
+ - scene
+ - action
+ - sound
+ - speech
+ evidence:
+ type: array
+ items:
+ type: object
+ additionalProperties: false
+ required:
+ - start_seconds
+ - end_seconds
+ - modality
+ - description
+ properties:
+ start_seconds:
+ type: number
+ end_seconds:
+ type: number
+ modality:
+ type: string
+ enum:
+ - scene
+ - action
+ - sound
+ - speech
+ description:
+ type: string
+ cli_env:
+ CODEX_HOME: "{{ env.VIDXP_EVAL_CODEX_HOME }}"
+ cli_config:
+ features:
+ multi_agent: false
+ mcp_servers:
+ vidxp:
+ command: "{{ env.VIDXP_MCP_COMMAND }}"
+ args:
+ - --repository
+ - "{{ env.VIDXP_EVAL_REPOSITORY | default('default') }}"
+ - --index-directory
+ - "{{ env.VIDXP_EVAL_INDEX_DIR }}"
+ - --data-dir
+ - "{{ env.VIDXP_EVAL_DATA_DIR }}"
+ - --device
+ - "{{ env.VIDXP_EVAL_DEVICE | default('cpu') }}"
+
+ - id: openai:codex-sdk
+ label: codex-no-mcp
+ config:
+ model: "{{ env.VIDXP_EVAL_MODEL | default('gpt-5.6-sol') }}"
+ model_reasoning_effort: "{{ env.VIDXP_EVAL_REASONING | default('medium') }}"
+ maxRetries: 0
+ working_dir: "{{ env.VIDXP_EVAL_WORKSPACE }}"
+ skip_git_repo_check: true
+ sandbox_mode: read-only
+ approval_policy: never
+ network_access_enabled: false
+ web_search_mode: disabled
+ persist_threads: false
+ enable_streaming: true
+ output_schema: *result_schema
+ cli_env:
+ CODEX_HOME: "{{ env.VIDXP_EVAL_CODEX_HOME }}"
+ cli_config:
+ features:
+ multi_agent: false
+
+tests:
+ - path: file://../../src/vidxp/benchmarks/agent_ablation_tests.py:generate_tests
+ config:
+ manifest: tasks/longvale-part9-pilot.json
+ providers:
+ mcp_on: codex-vidxp-mcp
+ mcp_off: codex-no-mcp
+
+evaluateOptions:
+ cache: false
+ maxConcurrency: 1
+ repeat: 1
+
+tracing:
+ enabled: true
diff --git a/benchmarks/codex-mcp/prompts/video-evidence.txt b/benchmarks/codex-mcp/prompts/video-evidence.txt
new file mode 100644
index 00000000..2afc6d49
--- /dev/null
+++ b/benchmarks/codex-mcp/prompts/video-evidence.txt
@@ -0,0 +1,13 @@
+Locate one event in the supplied video and return the single best time interval.
+
+Dataset: {{ dataset }}
+Video ID: {{ video_id }}
+Media path: {{ env.VIDXP_EVAL_WORKSPACE }}/{{ media_relpath }}
+Video duration: {{ duration_seconds }} seconds
+Event to locate: {{ query }}
+
+Use the media and any assistant tools already available in this condition. Do
+not use the network, read benchmark annotations, or invoke the VidXP CLI from
+the shell. Base the result on inspected evidence rather than the filename or
+query alone. If the evidence cannot be inspected, return null start and end
+values and explain the limitation. Return only the requested JSON object.
diff --git a/benchmarks/codex-mcp/scripts/preflight.mjs b/benchmarks/codex-mcp/scripts/preflight.mjs
new file mode 100644
index 00000000..7d464939
--- /dev/null
+++ b/benchmarks/codex-mcp/scripts/preflight.mjs
@@ -0,0 +1,80 @@
+import { existsSync, readFileSync } from 'node:fs';
+import { isAbsolute, join, resolve } from 'node:path';
+import { spawnSync } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+
+const benchmarkRoot = resolve(fileURLToPath(new URL('..', import.meta.url)));
+const manifestPath = join(benchmarkRoot, 'tasks', 'longvale-part9-pilot.json');
+
+const requiredNode = [22, 22, 0];
+const currentNode = process.versions.node.split('.').map(Number);
+const firstDifference = requiredNode.findIndex(
+ (part, index) => currentNode[index] !== part,
+);
+const nodeIsSupported = firstDifference === -1
+ || currentNode[firstDifference] > requiredNode[firstDifference];
+if (!nodeIsSupported) {
+ throw new Error(
+ `Node.js 22.22.0 or newer is required; found ${process.versions.node}.`,
+ );
+}
+
+function requireDirectory(name) {
+ const value = process.env[name];
+ if (!value || !isAbsolute(value) || !existsSync(value)) {
+ throw new Error(`${name} must name an existing absolute directory.`);
+ }
+ return value;
+}
+
+function requireFile(name) {
+ const value = process.env[name];
+ if (!value || !isAbsolute(value) || !existsSync(value)) {
+ throw new Error(`${name} must name an existing absolute file.`);
+ }
+ return value;
+}
+
+const codexHome = requireDirectory('VIDXP_EVAL_CODEX_HOME');
+const workspace = requireDirectory('VIDXP_EVAL_WORKSPACE');
+const dataDirectory = requireDirectory('VIDXP_EVAL_DATA_DIR');
+const indexDirectory = requireDirectory('VIDXP_EVAL_INDEX_DIR');
+const mcpCommand = requireFile('VIDXP_MCP_COMMAND');
+
+if (!existsSync(join(codexHome, 'auth.json'))) {
+ throw new Error('The isolated Codex home has no auth.json; sign in there before evaluating.');
+}
+
+const codexConfig = join(codexHome, 'config.toml');
+if (existsSync(codexConfig)) {
+ const content = readFileSync(codexConfig, 'utf8');
+ if (/^\s*\[mcp_servers(?:\.|\])/m.test(content)) {
+ throw new Error('The isolated Codex home config contains ambient MCP servers.');
+ }
+}
+
+const tasks = JSON.parse(readFileSync(manifestPath, 'utf8'));
+const missingMedia = [...new Set(tasks
+ .map((task) => join(workspace, task.media_relpath))
+ .filter((path) => !existsSync(path)))];
+if (missingMedia.length > 0) {
+ throw new Error(`Pilot media is missing:\n${missingMedia.join('\n')}`);
+}
+
+const check = spawnSync(
+ mcpCommand,
+ [
+ '--check',
+ '--repository', process.env.VIDXP_EVAL_REPOSITORY || 'default',
+ '--index-directory', indexDirectory,
+ '--data-dir', dataDirectory,
+ '--device', process.env.VIDXP_EVAL_DEVICE || 'cpu',
+ ],
+ { encoding: 'utf8', stdio: 'pipe' },
+);
+if (check.status !== 0) {
+ throw new Error(`VidXP MCP preflight failed:\n${check.stderr || check.stdout}`);
+}
+
+process.stdout.write(check.stdout);
+process.stdout.write(`Ready: ${tasks.length} tasks, 2 conditions, no model calls made.\n`);
diff --git a/benchmarks/codex-mcp/tasks/longvale-part9-pilot.json b/benchmarks/codex-mcp/tasks/longvale-part9-pilot.json
new file mode 100644
index 00000000..faaa84aa
--- /dev/null
+++ b/benchmarks/codex-mcp/tasks/longvale-part9-pilot.json
@@ -0,0 +1,122 @@
+[
+ {
+ "id": "longvale-part9-ZYT-rain-wind-engine",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZYTmgi1pAIE",
+ "media_relpath": "media/ZYTmgi1pAIE.mp4",
+ "duration_seconds": 75.809067,
+ "event_index": 0,
+ "query": "heavy rain and howling wind over a desolate landscape, followed by an engine starting and revving",
+ "expected_start": 0.0,
+ "expected_end": 6.0,
+ "modalities": ["scene", "sound", "action"]
+ },
+ {
+ "id": "longvale-part9-ZYT-bell-subscribe",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZYTmgi1pAIE",
+ "media_relpath": "media/ZYTmgi1pAIE.mp4",
+ "duration_seconds": 75.809067,
+ "event_index": 2,
+ "query": "a ringing bell echoes while SUBSCRIBE flashes over a starry night sky",
+ "expected_start": 70.07,
+ "expected_end": 75.742,
+ "modalities": ["scene", "sound"]
+ },
+ {
+ "id": "longvale-part9-ZId-car-siren",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZIdFAGJrlCw",
+ "media_relpath": "media/ZIdFAGJrlCw.mp4",
+ "duration_seconds": 296.4,
+ "event_index": 0,
+ "query": "a red car speeds down a winding road as a siren suddenly blares",
+ "expected_start": 7.68,
+ "expected_end": 10.2,
+ "modalities": ["action", "sound"]
+ },
+ {
+ "id": "longvale-part9-ZId-engine-rev",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZIdFAGJrlCw",
+ "media_relpath": "media/ZIdFAGJrlCw.mp4",
+ "duration_seconds": 296.4,
+ "event_index": 3,
+ "query": "the driver gestures while revving the Cayenne Coupe engine to demonstrate its exhaust sound",
+ "expected_start": 25.56,
+ "expected_end": 27.56,
+ "modalities": ["action", "sound"]
+ },
+ {
+ "id": "longvale-part9-ZId-sketch",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZIdFAGJrlCw",
+ "media_relpath": "media/ZIdFAGJrlCw.mp4",
+ "duration_seconds": 296.4,
+ "event_index": 8,
+ "query": "a hand sketches the sleek lines of a car among other automotive drawings",
+ "expected_start": 88.8,
+ "expected_end": 90.83646258503401,
+ "modalities": ["scene", "action"]
+ },
+ {
+ "id": "longvale-part9-ZGX-office-speech",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZGXCr5n8Frg",
+ "media_relpath": "media/ZGXCr5n8Frg.mp4",
+ "duration_seconds": 222.28,
+ "event_index": 2,
+ "query": "Changlin Dou sits at his office desk and describes bringing innovative medicine to the Chinese market",
+ "expected_start": 22.24,
+ "expected_end": 39.8,
+ "modalities": ["scene", "speech"]
+ },
+ {
+ "id": "longvale-part9-py-signing",
+ "dataset": "LongVALE evaluation",
+ "video_id": "_py1WXVX4oc",
+ "media_relpath": "media/_py1WXVX4oc.mp4",
+ "duration_seconds": 73.139733,
+ "event_index": 2,
+ "query": "a woman signs the phrase Find words you know against a blue dotted background",
+ "expected_start": 9.509,
+ "expected_end": 24.591,
+ "modalities": ["scene", "action"]
+ },
+ {
+ "id": "longvale-part9-py-phone-ring",
+ "dataset": "LongVALE evaluation",
+ "video_id": "_py1WXVX4oc",
+ "media_relpath": "media/_py1WXVX4oc.mp4",
+ "duration_seconds": 73.139733,
+ "event_index": 4,
+ "query": "Website coming in 2018 appears in purple letters while a telephone rings",
+ "expected_start": 70.136,
+ "expected_end": 73.139,
+ "modalities": ["scene", "sound"]
+ },
+ {
+ "id": "longvale-part9-ZVU-stir-and-cover",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZVUAC3m48G0",
+ "media_relpath": "media/ZVUAC3m48G0.mp4",
+ "duration_seconds": 247.16,
+ "event_index": 2,
+ "query": "a hand stirs chicken casserole in a green pot and secures the lid",
+ "expected_start": 190.24,
+ "expected_end": 192.84,
+ "modalities": ["scene", "action"]
+ },
+ {
+ "id": "longvale-part9-ZVU-casserole-drumbeat",
+ "dataset": "LongVALE evaluation",
+ "video_id": "ZVUAC3m48G0",
+ "media_relpath": "media/ZVUAC3m48G0.mp4",
+ "duration_seconds": 247.16,
+ "event_index": 4,
+ "query": "a close-up shows the completed chicken casserole as a simple drumbeat plays",
+ "expected_start": 242.88,
+ "expected_end": 247.08,
+ "modalities": ["scene", "sound"]
+ }
+]
diff --git a/desktop/capability-catalog.json b/desktop/capability-catalog.json
index 543a5b9d..8297ae0d 100644
--- a/desktop/capability-catalog.json
+++ b/desktop/capability-catalog.json
@@ -1,11 +1,11 @@
{
"schema_version": 1,
"capabilities": {
- "dialogue": {
- "extra": "dialogue",
- "modality": "dialogue",
- "label": "Dialogue search",
- "description": "Index and search spoken dialogue.",
+ "speech": {
+ "extra": "speech",
+ "modality": "speech",
+ "label": "Speech search",
+ "description": "Transcribe and search spoken words with timestamps.",
"models": [
{
"cache_key": "models--Qwen--Qwen3-Embedding-0.6B/snapshots/97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3/model.safetensors",
@@ -69,11 +69,11 @@
}
]
},
- "videoprism": {
- "extra": "videoprism",
- "modality": "videoprism",
- "label": "Temporal video search",
- "description": "Index and search temporal video clips with VideoPrism.",
+ "action": {
+ "extra": "action",
+ "modality": "action",
+ "label": "Action and motion search",
+ "description": "Index and search multi-frame actions and motion.",
"models": [
{
"cache_key": "models--google--videoprism-lvt-base-f16r288/snapshots/fb6de9f0eb7bc285be86bdca1cf7daa3e3ef51ff/model.safetensors",
diff --git a/desktop/runtime-manifest.json b/desktop/runtime-manifest.json
index 6a2b7583..cf4d362d 100644
--- a/desktop/runtime-manifest.json
+++ b/desktop/runtime-manifest.json
@@ -7,6 +7,13 @@
"python_version": "3.14.6",
"uv_version": "0.12.0",
"managed_runtime_estimated_size_bytes": 3221225472,
+ "local_answers": {
+ "engine": "ollama",
+ "model": "qwen3.5:4b-q4_K_M",
+ "download_size_bytes": 3650722202,
+ "label": "Local grounded answers",
+ "description": "Turn VidXP search evidence into cited answers on this computer. Setup reuses or installs Ollama and downloads the approved Qwen 3.5 4B model."
+ },
"surfaces": {
"worker": {
"extra": "local-worker",
diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock
index 9665d4e5..30f81226 100644
--- a/desktop/src-tauri/Cargo.lock
+++ b/desktop/src-tauri/Cargo.lock
@@ -1130,6 +1130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
@@ -2884,6 +2885,7 @@ checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [
"base64 0.22.1",
"bytes",
+ "futures-channel",
"futures-core",
"futures-util",
"http",
@@ -4431,6 +4433,7 @@ dependencies = [
"hex",
"log",
"process-wrap",
+ "reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml
index 6400d321..fb270307 100644
--- a/desktop/src-tauri/Cargo.toml
+++ b/desktop/src-tauri/Cargo.toml
@@ -20,6 +20,7 @@ atomic-write-file = "0.3.0"
hex = "0.4.3"
log = "0.4.29"
process-wrap = { version = "9.1.0", features = ["std"] }
+reqwest = { version = "0.13.4", default-features = false, features = ["blocking", "json"] }
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
sha2 = "0.11.0"
diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index fca2be96..bb306510 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -2,7 +2,7 @@ use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
env, fs,
- io::{self, Read, Write},
+ io::{self, BufRead, BufReader, Read, Write},
net::{SocketAddr, TcpListener, TcpStream},
path::{Path, PathBuf},
process::Command,
@@ -32,6 +32,7 @@ mod browser_readiness;
mod lifecycle;
mod media_setup;
mod premiere_integration;
+mod query_setup;
mod target_profiles;
use activation::{ActivationRecovery, ActivationStage, activation_recovery};
@@ -58,6 +59,8 @@ const PRODUCT_DATA_DIRECTORY_NAME: &str = "VidXP";
const RUNTIME_CONSTRAINTS_FILE_NAME: &str = "runtime-constraints.txt";
const MAX_SETUP_OUTPUT_BYTES: usize = 4 * 1024 * 1024;
const MEDIA_RUNTIME_INSTALL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
+const QUERY_RUNTIME_INSTALL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
+const QUERY_MODEL_PULL_TIMEOUT: Duration = Duration::from_secs(2 * 60 * 60);
static READINESS_SEQUENCE: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Deserialize, Serialize)]
@@ -90,6 +93,15 @@ struct MediaRuntimeSpec {
reason: String,
}
+#[derive(Clone, Deserialize, Serialize)]
+struct LocalAnswersSpec {
+ engine: String,
+ model: String,
+ download_size_bytes: u64,
+ label: String,
+ description: String,
+}
+
#[derive(Clone, Deserialize, Serialize)]
struct RuntimeManifest {
schema_version: u32,
@@ -101,6 +113,7 @@ struct RuntimeManifest {
python_version: String,
uv_version: String,
managed_runtime_estimated_size_bytes: u64,
+ local_answers: LocalAnswersSpec,
surfaces: BTreeMap,
capabilities: BTreeMap,
media_runtime: MediaRuntimeSpec,
@@ -111,6 +124,7 @@ struct InstallRequest {
capabilities: Vec,
surfaces: Vec,
prepare_models: bool,
+ local_answers: bool,
model_directory: Option,
draft_id: String,
}
@@ -127,6 +141,7 @@ struct InstallResult {
capabilities: Vec,
surfaces: Vec,
model_directory: String,
+ local_answers: bool,
prepared: bool,
}
@@ -209,6 +224,7 @@ struct RuntimeStatus {
capabilities: Vec,
surfaces: Vec,
model_directory: String,
+ local_answers: bool,
detail: String,
}
@@ -275,6 +291,62 @@ struct ActiveRuntime {
surfaces: Vec,
#[serde(default)]
model_directory: PathBuf,
+ #[serde(default)]
+ local_answers: bool,
+}
+
+fn emit_local_answer_progress(
+ app: &AppHandle,
+ draft_id: &str,
+ current: u8,
+ total: u8,
+ message: impl Into,
+ downloaded: Option,
+ download_total: Option,
+) {
+ let _ = app.emit(
+ "managed-setup-progress",
+ ManagedSetupProgress {
+ draft_id: draft_id.into(),
+ current,
+ total,
+ stage: "local-answers".into(),
+ message: "Preparing the local grounded-answer model".into(),
+ model_message: Some(message.into()),
+ model_current: downloaded,
+ model_total: download_total,
+ },
+ );
+}
+
+#[derive(Deserialize)]
+struct OllamaVersionResponse {
+ version: String,
+}
+
+#[derive(Deserialize)]
+struct OllamaTagsResponse {
+ models: Vec,
+}
+
+#[derive(Deserialize)]
+struct OllamaModel {
+ name: String,
+ #[serde(default)]
+ digest: String,
+}
+
+#[derive(Deserialize)]
+struct OllamaPullProgress {
+ status: String,
+ #[serde(default)]
+ digest: Option,
+ #[serde(default)]
+ total: Option,
+ #[serde(default)]
+ completed: Option,
+ #[serde(default)]
+ error: Option,
}
#[derive(Clone)]
@@ -470,6 +542,12 @@ struct ManagedApiService {
profile_id: String,
}
+struct ManagedQueryService {
+ process: background_process::OwnedChild,
+ executable: PathBuf,
+ model_directory: PathBuf,
+}
+
#[derive(Clone, Debug, Serialize)]
struct LocalServerStatus {
state: &'static str,
@@ -529,6 +607,7 @@ struct TrayMenuItems {
struct DesktopState {
ui_process: Mutex>,
api_process: Mutex >,
+ query_process: Mutex >,
worker_stop: Arc,
operation_cancellation: Arc>>,
transition: Arc>,
@@ -545,6 +624,7 @@ impl Default for DesktopState {
Self {
ui_process: Mutex::new(None),
api_process: Mutex::new(None),
+ query_process: Mutex::new(None),
worker_stop: Arc::new(WorkerStopSupervisor::default()),
operation_cancellation: Arc::new(Mutex::new(None)),
transition: Arc::new(Mutex::new(TransitionState::default())),
@@ -1201,6 +1281,29 @@ fn package_extras(
extras.into_iter().collect::>().join(",")
}
+fn managed_package_specification(
+ manifest: &RuntimeManifest,
+ capabilities: &[String],
+ surfaces: &[String],
+ local_answers: bool,
+) -> String {
+ if !local_answers || surfaces.iter().any(|surface| surface == "worker") {
+ return package_specification(manifest, capabilities, surfaces);
+ }
+ let mut extras: BTreeSet<_> = package_extras(manifest, capabilities, surfaces)
+ .split(',')
+ .filter(|extra| !extra.is_empty())
+ .map(str::to_owned)
+ .collect();
+ extras.insert("slm".into());
+ format!(
+ "{}[{}]=={}",
+ manifest.package_name,
+ extras.into_iter().collect::>().join(","),
+ manifest.package_version
+ )
+}
+
fn external_installation_arguments(
manifest: &RuntimeManifest,
capabilities: &[String],
@@ -1299,6 +1402,7 @@ fn dependency_installation_invocation(
manifest: &RuntimeManifest,
capabilities: &[String],
surfaces: &[String],
+ local_answers: bool,
python: &Path,
constraints: &Path,
cpu_torch: bool,
@@ -1330,7 +1434,12 @@ fn dependency_installation_invocation(
if cpu_torch {
arguments.extend(["--torch-backend".into(), "cpu".into()]);
}
- arguments.push(package_specification(manifest, capabilities, surfaces));
+ arguments.push(managed_package_specification(
+ manifest,
+ capabilities,
+ surfaces,
+ local_answers,
+ ));
Ok(UvInvocation {
arguments,
working_directory: working_directory.to_path_buf(),
@@ -1418,7 +1527,11 @@ fn executable_candidates(name: &str) -> Vec {
}
fn resolve_system_executable(name: &str) -> Option {
- let resolved = executable_candidates(name)
+ let mut candidates = executable_candidates(name);
+ if name == "ollama" {
+ candidates.extend(query_setup::executable_candidates());
+ }
+ let resolved = candidates
.into_iter()
.find(|candidate| candidate.is_file())
.and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)));
@@ -1429,6 +1542,10 @@ fn resolve_system_executable(name: &str) -> Option {
if matches!(name, "ffmpeg" | "ffprobe") {
return media_setup::resolve_winget_ffmpeg_executable(&format!("{name}.exe"));
}
+ #[cfg(windows)]
+ if name == "ollama" {
+ return query_setup::resolve_winget_ollama_executable();
+ }
None
}
@@ -1522,6 +1639,357 @@ fn verified_media_runtime() -> Result {
})
}
+fn ollama_management_url(path: &str) -> String {
+ format!("http://{}{path}", query_setup::OLLAMA_HOST)
+}
+
+fn ollama_client(timeout: Duration) -> Result {
+ reqwest::blocking::Client::builder()
+ .connect_timeout(Duration::from_secs(2))
+ .timeout(timeout)
+ .build()
+ .map_err(|error| format!("Could not configure the local answer runtime client: {error}"))
+}
+
+fn ollama_server_version() -> Result {
+ let response = ollama_client(Duration::from_secs(3))?
+ .get(ollama_management_url("/api/version"))
+ .send()
+ .and_then(reqwest::blocking::Response::error_for_status)
+ .map_err(|error| format!("The local Ollama service is not ready: {error}"))?
+ .json::()
+ .map_err(|error| {
+ format!("The local Ollama service returned an invalid version: {error}")
+ })?;
+ if response.version.trim().is_empty() {
+ return Err("The local Ollama service returned an empty version.".into());
+ }
+ Ok(response.version)
+}
+
+fn installed_ollama_model(model: &str) -> Result, String> {
+ let response = ollama_client(Duration::from_secs(10))?
+ .get(ollama_management_url("/api/tags"))
+ .send()
+ .and_then(reqwest::blocking::Response::error_for_status)
+ .map_err(|error| format!("Could not inspect local Ollama models: {error}"))?
+ .json::()
+ .map_err(|error| format!("Ollama returned an invalid model inventory: {error}"))?;
+ Ok(response.models.into_iter().find(|candidate| {
+ candidate.name == model
+ || candidate.name.strip_suffix(":latest") == model.strip_suffix(":latest")
+ }))
+}
+
+fn stop_query_process(state: &DesktopState) {
+ let Ok(mut active) = state.query_process.lock() else {
+ return;
+ };
+ if let Some(mut service) = active.take() {
+ service.process.terminate_and_reap();
+ }
+}
+
+fn ensure_query_service(
+ state: &DesktopState,
+ executable: &Path,
+ model_directory: &Path,
+) -> Result {
+ let mut active = state
+ .query_process
+ .lock()
+ .map_err(|_| "The local answer runtime supervisor is unavailable.".to_string())?;
+ if let Some(service) = active.as_mut() {
+ let running = service
+ .process
+ .try_wait()
+ .map_err(|error| format!("Could not inspect the local answer runtime: {error}"))?
+ .is_none();
+ if running && service.executable == executable && service.model_directory == model_directory
+ {
+ if let Ok(version) = ollama_server_version() {
+ return Ok(version);
+ }
+ }
+ service.process.terminate_and_reap();
+ *active = None;
+ }
+ if let Ok(version) = ollama_server_version() {
+ return Ok(version);
+ }
+ fs::create_dir_all(model_directory).map_err(|error| {
+ format!(
+ "Could not create the local answer model directory at {}: {error}",
+ model_directory.display()
+ )
+ })?;
+ let mut command = Command::new(executable);
+ command
+ .arg("serve")
+ .env("OLLAMA_HOST", query_setup::OLLAMA_HOST)
+ .env("OLLAMA_MODELS", model_directory);
+ let mut process = background_process::spawn_service(command)
+ .map_err(|error| format!("Could not start the local answer runtime: {}", error.detail))?;
+ let deadline = Instant::now() + Duration::from_secs(30);
+ let version = loop {
+ if let Ok(version) = ollama_server_version() {
+ break version;
+ }
+ if process
+ .try_wait()
+ .map_err(|error| format!("Could not inspect the local answer runtime: {error}"))?
+ .is_some()
+ {
+ return Err("The local answer runtime exited before becoming healthy.".into());
+ }
+ if Instant::now() >= deadline {
+ return Err(
+ "The local answer runtime did not become healthy within 30 seconds.".into(),
+ );
+ }
+ thread::sleep(Duration::from_millis(150));
+ };
+ *active = Some(ManagedQueryService {
+ process,
+ executable: executable.to_path_buf(),
+ model_directory: model_directory.to_path_buf(),
+ });
+ Ok(version)
+}
+
+fn pull_ollama_model(
+ app: &AppHandle,
+ draft_id: &str,
+ current: u8,
+ total_steps: u8,
+ model: &str,
+ cancellation: background_process::CancellationToken,
+) -> Result {
+ if let Some(installed) = installed_ollama_model(model)? {
+ emit_local_answer_progress(
+ app,
+ draft_id,
+ current,
+ total_steps,
+ format!("Reusing {model}"),
+ None,
+ None,
+ );
+ return Ok(installed);
+ }
+ let response = ollama_client(QUERY_MODEL_PULL_TIMEOUT)?
+ .post(ollama_management_url("/api/pull"))
+ .json(&serde_json::json!({"model": model, "stream": true}))
+ .send()
+ .and_then(reqwest::blocking::Response::error_for_status)
+ .map_err(|error| format!("Could not start the {model} download: {error}"))?;
+ let reader = BufReader::new(response);
+ for line in reader.lines() {
+ if cancellation.is_cancelled() {
+ return Err("the local answer model download was cancelled".into());
+ }
+ let line = line.map_err(|error| format!("The model download stream failed: {error}"))?;
+ if line.trim().is_empty() {
+ continue;
+ }
+ let progress: OllamaPullProgress = serde_json::from_str(&line)
+ .map_err(|error| format!("Ollama returned invalid download progress: {error}"))?;
+ if let Some(error) = progress.error {
+ return Err(format!("Ollama could not download {model}: {error}"));
+ }
+ let layer = progress
+ .digest
+ .as_deref()
+ .and_then(|digest| digest.get(..12))
+ .map(|digest| format!(" · layer {digest}"))
+ .unwrap_or_default();
+ emit_local_answer_progress(
+ app,
+ draft_id,
+ current,
+ total_steps,
+ format!("{}{layer}", progress.status),
+ progress.completed,
+ progress.total,
+ );
+ }
+ installed_ollama_model(model)?.ok_or_else(|| {
+ format!("Ollama finished downloading {model}, but the model was not present afterward.")
+ })
+}
+
+fn local_answer_platform_error() -> Option {
+ #[cfg(windows)]
+ {
+ let mut command = Command::new("cmd");
+ command.args(["/C", "ver"]);
+ if let Ok(output) = checked_output(command, "Windows version check")
+ && query_setup::version_meets_minimum(
+ &String::from_utf8_lossy(&output.stdout),
+ (10, 0, 19045),
+ ) == Some(false)
+ {
+ return Some(
+ "Local grounded answers require Windows 10 22H2 or newer because that is Ollama's supported Windows baseline."
+ .into(),
+ );
+ }
+ }
+ #[cfg(target_os = "macos")]
+ {
+ let mut command = Command::new("/usr/bin/sw_vers");
+ command.arg("-productVersion");
+ if let Ok(output) = checked_output(command, "macOS version check")
+ && query_setup::version_meets_minimum(
+ &String::from_utf8_lossy(&output.stdout),
+ (14, 0, 0),
+ ) == Some(false)
+ {
+ return Some(
+ "Local grounded answers require macOS 14 or newer because that is Ollama's supported macOS baseline."
+ .into(),
+ );
+ }
+ }
+ None
+}
+
+async fn prepare_local_answers_runtime(
+ app: &AppHandle,
+ state: &DesktopState,
+ paths: &DesktopPaths,
+ draft_id: &str,
+ current: u8,
+ total_steps: u8,
+ model: &str,
+ cancellation: background_process::CancellationToken,
+) -> Result<(), String> {
+ if let Some(error) = local_answer_platform_error() {
+ return Err(error);
+ }
+ let server_ready = ollama_server_version().is_ok();
+ let mut executable = resolve_system_executable("ollama");
+ if !server_ready && executable.is_none() {
+ let plan = query_setup::system_install_plan(resolve_system_executable).ok_or_else(|| {
+ if cfg!(target_os = "macos") {
+ "Local grounded answers require Ollama on macOS 14 or newer. Install the official Ollama app from https://ollama.com/download, then retry.".to_string()
+ } else if cfg!(target_os = "linux") {
+ "Local grounded answers require Ollama. Install it using the official Linux instructions at https://ollama.com/download/linux, then retry.".to_string()
+ } else {
+ "Local grounded answers require Ollama, but no supported automatic installer was found. Install it from https://ollama.com/download, then retry.".to_string()
+ }
+ })?;
+ let approved = app
+ .dialog()
+ .message(format!(
+ "Local grounded answers use Ollama and download {model} (approximately 3.4 GB, Apache-2.0).\n\nInstall Ollama with {}?\n\n{}",
+ plan.manager,
+ display_command(&plan.command)
+ ))
+ .title("Install local answer runtime")
+ .kind(MessageDialogKind::Info)
+ .buttons(MessageDialogButtons::OkCancelCustom(
+ "Install".into(),
+ "Not now".into(),
+ ))
+ .blocking_show();
+ if !approved {
+ return Err("Local grounded-answer setup was deferred.".into());
+ }
+ emit_local_answer_progress(
+ app,
+ draft_id,
+ current,
+ total_steps,
+ format!("Installing Ollama with {}", plan.manager),
+ None,
+ None,
+ );
+ let command = app
+ .shell()
+ .command(plan.command[0].clone())
+ .args(&plan.command[1..]);
+ supervised_output_with_timeout(
+ command.into(),
+ cancellation.clone(),
+ &format!("{} Ollama installation", plan.manager),
+ QUERY_RUNTIME_INSTALL_TIMEOUT,
+ )
+ .await?;
+ for _ in 0..10 {
+ executable = resolve_system_executable("ollama");
+ if executable.is_some() {
+ break;
+ }
+ thread::sleep(Duration::from_secs(1));
+ }
+ }
+ let model_directory = paths.models.join("ollama");
+ if let Some(executable) = executable {
+ ensure_query_service(state, &executable, &model_directory)?;
+ } else if !server_ready {
+ return Err(
+ "Ollama installation finished, but VidXP could not locate its executable.".to_string(),
+ );
+ }
+ let pull_app = app.clone();
+ let pull_draft = draft_id.to_owned();
+ let pull_model = model.to_owned();
+ let pull_cancellation = cancellation;
+ let installed = tauri::async_runtime::spawn_blocking(move || {
+ pull_ollama_model(
+ &pull_app,
+ &pull_draft,
+ current,
+ total_steps,
+ &pull_model,
+ pull_cancellation,
+ )
+ })
+ .await
+ .map_err(|error| format!("Local answer model preparation stopped unexpectedly: {error}"))??;
+ if installed.digest.trim().is_empty() {
+ return Err(format!("Ollama did not report a digest for {model}."));
+ }
+ Ok(())
+}
+
+fn active_local_answers(paths: &DesktopPaths) -> bool {
+ active_runtime(paths).is_ok_and(|active| active.local_answers)
+}
+
+fn configure_local_answer_environment(command: &mut Command, paths: &DesktopPaths) {
+ if active_local_answers(paths) {
+ let model = manifest()
+ .map(|manifest| manifest.local_answers.model)
+ .unwrap_or_else(|_| "qwen3.5:4b-q4_K_M".into());
+ command
+ .env(
+ "VIDXP_SLM_BASE_URL",
+ format!("http://{}/v1", query_setup::OLLAMA_HOST),
+ )
+ .env("VIDXP_SLM_MODEL", model);
+ }
+}
+
+fn ensure_active_query_service(state: &DesktopState, paths: &DesktopPaths) -> Result<(), String> {
+ if !active_local_answers(paths) {
+ return Ok(());
+ }
+ if ollama_server_version().is_err() {
+ let executable = resolve_system_executable("ollama").ok_or_else(|| {
+ "Local grounded answers are enabled, but Ollama is no longer installed. Open Setup options and repair VidXP."
+ .to_string()
+ })?;
+ ensure_query_service(state, &executable, &paths.models.join("ollama"))?;
+ }
+ let model = manifest()?.local_answers.model;
+ installed_ollama_model(&model)?.ok_or_else(|| {
+ format!("The local answer model {model} is missing. Open Setup options and repair VidXP.")
+ })?;
+ Ok(())
+}
+
fn clean_environment(paths: &DesktopPaths) -> Vec<(String, String)> {
clean_environment_from(paths, std::env::vars())
}
@@ -2783,6 +3251,7 @@ fn runtime_status_sync(app: &AppHandle) -> Result {
capabilities: Vec::new(),
surfaces: Vec::new(),
model_directory: default_model_directory,
+ local_answers: false,
detail: "No Desktop-managed runtime has been created yet.".into(),
});
}
@@ -2799,6 +3268,7 @@ fn runtime_status_sync(app: &AppHandle) -> Result {
capabilities: Vec::new(),
surfaces: Vec::new(),
model_directory: default_model_directory,
+ local_answers: false,
detail: format!("The active runtime pointer is invalid: {error}"),
});
}
@@ -2849,6 +3319,7 @@ fn configured_runtime_status(active: ActiveRuntime, problems: Vec) -> Ru
capabilities: active.capabilities,
surfaces: active.surfaces,
model_directory: active.model_directory.to_string_lossy().into_owned(),
+ local_answers: active.local_answers,
detail: if ready {
"Local video processing is ready.".into()
} else {
@@ -2953,12 +3424,13 @@ async fn install_runtime(
.map_err(|error| format!("Managed runtime preparation stopped unexpectedly: {error}"))??;
let profile_seed = format!(
- "{}:{}:{}:{}:{}",
+ "{}:{}:{}:{}:{}:{}",
manifest_digest(),
std::env::consts::OS,
std::env::consts::ARCH,
capabilities.join(","),
- surfaces.join(",")
+ surfaces.join(","),
+ request.local_answers,
);
let profile_hash = hex::encode(Sha256::digest(profile_seed.as_bytes()));
let timestamp = SystemTime::now()
@@ -2968,13 +3440,36 @@ async fn install_runtime(
let profile = format!("{profile_hash}-{timestamp}");
let runtime = paths.runtimes.join(&profile);
let constraints = runtime.join(RUNTIME_CONSTRAINTS_FILE_NAME);
- let progress_total = if request.prepare_models { 8 } else { 7 };
+ let local_answer_offset = u8::from(request.local_answers);
+ let progress_total = (if request.prepare_models { 8 } else { 7 }) + local_answer_offset;
let install_result = async {
+ if request.local_answers {
+ emit_local_answer_progress(
+ &app,
+ &request.draft_id,
+ 2,
+ progress_total,
+ "Checking Ollama and the approved Qwen model",
+ None,
+ None,
+ );
+ prepare_local_answers_runtime(
+ &app,
+ &state,
+ &paths,
+ &request.draft_id,
+ 2,
+ progress_total,
+ &manifest.local_answers.model,
+ cancellation.token(),
+ )
+ .await?;
+ }
emit_managed_setup_progress(
&app,
&request.draft_id,
- 2,
+ 2 + local_answer_offset,
progress_total,
"python",
"Preparing an isolated Python runtime",
@@ -3009,7 +3504,7 @@ async fn install_runtime(
emit_managed_setup_progress(
&app,
&request.draft_id,
- 3,
+ 3 + local_answer_offset,
progress_total,
"package",
"Acquiring the VidXP package",
@@ -3032,6 +3527,7 @@ async fn install_runtime(
&manifest,
&capabilities,
&surfaces,
+ request.local_answers,
&executable(&runtime, "python"),
&constraints,
!cfg!(target_os = "macos"),
@@ -3039,7 +3535,7 @@ async fn install_runtime(
emit_managed_setup_progress(
&app,
&request.draft_id,
- 4,
+ 4 + local_answer_offset,
progress_total,
"dependencies",
"Installing the selected search features",
@@ -3053,6 +3549,20 @@ async fn install_runtime(
"VidXP package installation",
)
.await?;
+ if request.local_answers {
+ let mut query_client_check = configured_command(&executable(&runtime, "python"), &paths);
+ query_client_check.args([
+ "-c",
+ "from pydantic_ai.models.openai import OpenAIChatModel",
+ ]);
+ supervised_output_with_timeout(
+ query_client_check,
+ cancellation.token(),
+ "Local grounded-answer client validation",
+ Duration::from_secs(30),
+ )
+ .await?;
+ }
if let Err(error) = fs::remove_file(&runtime_wheel) {
log::warn!(
"Installed the embedded VidXP package, but could not remove its staged wheel: {error}"
@@ -3062,7 +3572,7 @@ async fn install_runtime(
emit_managed_setup_progress(
&app,
&request.draft_id,
- 5,
+ 5 + local_answer_offset,
progress_total,
"media",
"Configuring FFmpeg and video codecs",
@@ -3086,7 +3596,7 @@ async fn install_runtime(
emit_managed_setup_progress(
&app,
&request.draft_id,
- 6,
+ 6 + local_answer_offset,
progress_total,
"validation",
"Validating installed packages and video tools",
@@ -3106,7 +3616,7 @@ async fn install_runtime(
emit_managed_setup_progress(
&app,
&request.draft_id,
- 7,
+ 7 + local_answer_offset,
progress_total,
"models",
"Verifying and downloading selected model files",
@@ -3127,7 +3637,7 @@ async fn install_runtime(
&preparation_app,
&preparation_draft_id,
&progress_path_worker,
- 7,
+ 7 + local_answer_offset,
progress_total,
&monitor_stop_worker,
);
@@ -3192,6 +3702,7 @@ async fn install_runtime(
capabilities: capabilities.clone(),
surfaces: surfaces.clone(),
model_directory: paths.models.clone(),
+ local_answers: request.local_answers,
};
let activation_app = app.clone();
let activation_cancellation = cancellation.token();
@@ -3315,6 +3826,9 @@ async fn install_runtime(
}
stop_ui_process(&state);
stop_api_process(&state);
+ if !request.local_answers {
+ stop_query_process(&state);
+ }
transition.commit_draft();
refresh_tray_for_selected_target(&app);
@@ -3327,6 +3841,7 @@ async fn install_runtime(
.selected_profile()
.and_then(|profile| profile.model_directory.as_ref())
.map_or_else(String::new, |path| path.to_string_lossy().into_owned()),
+ local_answers: request.local_answers,
prepared: request.prepare_models,
},
setup: activation,
@@ -3405,6 +3920,7 @@ fn start_ui(
if let Some(model_directory) = &profile.model_directory {
paths.models = model_directory.clone();
}
+ ensure_active_query_service(state, &paths)?;
let mut active_process = state
.ui_process
.lock()
@@ -3573,10 +4089,14 @@ fn target_command(
paths: &DesktopPaths,
executable_path: &Path,
) -> Command {
- match profile.kind {
+ let mut command = match profile.kind {
target_profiles::TargetKind::Managed => configured_command(executable_path, paths),
target_profiles::TargetKind::ExistingLocal => Command::new(executable_path),
+ };
+ if profile.kind == target_profiles::TargetKind::Managed {
+ configure_local_answer_environment(&mut command, paths);
}
+ command
}
fn selected_target_context(
@@ -3628,6 +4148,7 @@ async fn target_doctor(
let _active = state.active_operations.register()?;
tauri::async_runtime::spawn_blocking(move || {
let (profile, paths) = selected_target_context(&app)?;
+ ensure_active_query_service(&app.state::(), &paths)?;
let arguments = capability_command_arguments(&manifest()?, "doctor", &profile.capabilities);
let mut command = target_command(&profile, &paths, &profile.executable);
command
@@ -3713,6 +4234,7 @@ async fn configure_external_installation(
}
stop_ui_process(&state);
stop_api_process(&state);
+ stop_query_process(&state);
let target_version = external_installation_version(
&manifest,
runtime_update_required,
@@ -3754,6 +4276,7 @@ async fn mcp_client_config(
let _active = state.active_operations.register()?;
tauri::async_runtime::spawn_blocking(move || {
let (profile, paths) = selected_target_context(&app)?;
+ ensure_active_query_service(&app.state::(), &paths)?;
if !profile
.surfaces
.iter()
@@ -3797,6 +4320,7 @@ async fn install_codex_plugin(
let _active = state.active_operations.register()?;
tauri::async_runtime::spawn_blocking(move || {
let (profile, paths) = selected_target_context(&app)?;
+ ensure_active_query_service(&app.state::(), &paths)?;
if !profile
.surfaces
.iter()
@@ -3905,6 +4429,7 @@ fn execute_worker_action(app: &AppHandle, action: &str) -> Result(), &paths)?;
let mut command = target_command(&profile, &paths, &profile.executable);
command
.arg("--data-dir")
@@ -4105,6 +4630,7 @@ fn start_server_mode(
"The selected VidXP installation does not include the app integration service.".into(),
);
}
+ ensure_active_query_service(state, &paths)?;
let mut active = state
.api_process
.lock()
@@ -4683,6 +5209,7 @@ fn begin_shutdown(app: &AppHandle) {
log::info!("VidXP supervised shutdown requested");
stop_ui_process(&state);
stop_api_process(&state);
+ stop_query_process(&state);
let app = app.clone();
let operations = state.active_operations.clone();
tauri::async_runtime::spawn(async move {
@@ -4841,6 +5368,7 @@ fn shutdown(app: &AppHandle, deadline: Instant) {
cancel_active_operation(&state);
stop_ui_process(&state);
stop_api_process(&state);
+ stop_query_process(&state);
let Ok(mut paths) = desktop_paths(app) else {
log::warn!("Could not resolve desktop paths during shutdown");
return;
@@ -4988,10 +5516,10 @@ mod tests {
configure_ui_service_command, configured_runtime_status,
dependency_installation_invocation, desktop_paths_from_roots, display_command,
external_installation_arguments, external_installation_version, inventory_model_directory,
- manifest, manifest_digest, normalize_line_endings, normalized_runtime_constraints,
- package_acquisition_arguments, package_specification, read_active_runtime_snapshot,
- reconcile_managed_runtime_storage, required_encoder_missing, restore_active_runtime,
- selected_capabilities, selected_surfaces, ui_process_action,
+ managed_package_specification, manifest, manifest_digest, normalize_line_endings,
+ normalized_runtime_constraints, package_acquisition_arguments, package_specification,
+ read_active_runtime_snapshot, reconcile_managed_runtime_storage, required_encoder_missing,
+ restore_active_runtime, selected_capabilities, selected_surfaces, ui_process_action,
validate_managed_runtime_identity, write_activation_journal, write_active_runtime,
};
use std::{
@@ -5445,6 +5973,7 @@ mod tests {
capabilities: vec!["scene".into()],
surfaces: vec!["browser".into()],
model_directory: paths.models.clone(),
+ local_answers: false,
};
let active = runtime(active_profile.clone());
write_active_runtime(&paths, &active).expect("active pointer");
@@ -5520,6 +6049,7 @@ mod tests {
capabilities: vec!["scene".into()],
surfaces: vec!["browser".into()],
model_directory: PathBuf::from(model_directory),
+ local_answers: false,
}
}
@@ -5565,19 +6095,19 @@ mod tests {
&manifest,
&[
"scene".into(),
- "videoprism".into(),
- "dialogue".into(),
+ "action".into(),
+ "speech".into(),
"scene".into(),
],
)
.expect("selection");
- assert_eq!(selected, ["dialogue", "scene", "videoprism"]);
+ assert_eq!(selected, ["action", "scene", "speech"]);
assert!(selected_capabilities(&manifest, &["other".into()]).is_err());
}
#[test]
- fn runtime_manifest_exposes_sound_setup_and_dependency() {
+ fn runtime_manifest_exposes_sound_and_local_answer_setup() {
let manifest = manifest().expect("manifest");
let sound = manifest
.capabilities
@@ -5599,6 +6129,9 @@ mod tests {
package_specification(&manifest, &["sound".into()], &[]),
format!("vidxp[sound]=={}", manifest.package_version)
);
+ assert_eq!(manifest.local_answers.engine, "ollama");
+ assert_eq!(manifest.local_answers.model, "qwen3.5:4b-q4_K_M");
+ assert_eq!(manifest.local_answers.download_size_bytes, 3_650_722_202);
}
#[test]
@@ -5627,10 +6160,10 @@ mod tests {
assert_eq!(
package_specification(
&manifest,
- &["scene".into(), "dialogue".into()],
+ &["scene".into(), "speech".into()],
&["browser".into()],
),
- format!("vidxp[dialogue,frontend,scene]=={version}")
+ format!("vidxp[frontend,scene,speech]=={version}")
);
assert_eq!(
package_specification(&manifest, &["scene".into()], &[]),
@@ -5639,11 +6172,24 @@ mod tests {
assert_eq!(
package_specification(
&manifest,
- &["actor".into(), "dialogue".into(), "scene".into()],
+ &["actor".into(), "speech".into(), "scene".into()],
&["worker".into()],
),
format!("vidxp[local-worker]=={version}")
);
+ assert_eq!(
+ managed_package_specification(
+ &manifest,
+ &["scene".into()],
+ &["browser".into(), "mcp".into()],
+ true,
+ ),
+ format!("vidxp[frontend,mcp,scene,slm]=={version}")
+ );
+ assert_eq!(
+ managed_package_specification(&manifest, &["scene".into()], &["worker".into()], true,),
+ format!("vidxp[local-worker]=={version}")
+ );
assert_eq!(
selected_surfaces(&manifest, &["browser".into(), "browser".into()])
.expect("surface selection"),
@@ -5724,6 +6270,7 @@ mod tests {
&manifest,
&["scene".into()],
&[],
+ false,
python,
&constraints,
true,
@@ -5791,6 +6338,7 @@ mod tests {
&manifest,
&["scene".into()],
&[],
+ false,
Path::new("managed-python"),
&constraints,
false,
@@ -5812,8 +6360,8 @@ mod tests {
let manifest = manifest().expect("manifest");
assert_eq!(
- capability_command_arguments(&manifest, "doctor", &["dialogue".into(), "scene".into()]),
- ["doctor", "--json", "--modalities", "dialogue,scene"]
+ capability_command_arguments(&manifest, "doctor", &["scene".into(), "speech".into()]),
+ ["doctor", "--json", "--modalities", "scene,speech"]
);
assert_eq!(
capability_command_arguments(&manifest, "prepare", &["scene".into()]),
diff --git a/desktop/src-tauri/src/query_setup.rs b/desktop/src-tauri/src/query_setup.rs
new file mode 100644
index 00000000..0738bad4
--- /dev/null
+++ b/desktop/src-tauri/src/query_setup.rs
@@ -0,0 +1,170 @@
+use std::{env, path::PathBuf};
+
+#[cfg(windows)]
+use std::{fs, path::Path};
+
+use crate::media_setup::SystemInstallPlan;
+
+pub(crate) const OLLAMA_HOST: &str = "127.0.0.1:11434";
+
+pub(crate) fn version_meets_minimum(output: &str, minimum: (u32, u32, u32)) -> Option {
+ let version = output
+ .split(|character: char| !(character.is_ascii_digit() || character == '.'))
+ .find(|candidate| candidate.contains('.'))?;
+ let parts = version
+ .split('.')
+ .take(3)
+ .map(str::parse::)
+ .collect::, _>>()
+ .ok()?;
+ if parts.len() < 2 {
+ return None;
+ }
+ let actual = (parts[0], parts[1], parts.get(2).copied().unwrap_or(0));
+ Some(actual >= minimum)
+}
+
+pub(crate) fn system_install_plan(
+ mut resolve: impl FnMut(&str) -> Option,
+) -> Option {
+ if cfg!(windows) {
+ resolve("winget")?;
+ return Some(SystemInstallPlan {
+ manager: "Windows Package Manager".into(),
+ command: vec![
+ "winget".into(),
+ "install".into(),
+ "--id".into(),
+ "Ollama.Ollama".into(),
+ "--exact".into(),
+ "--source".into(),
+ "winget".into(),
+ "--silent".into(),
+ "--disable-interactivity".into(),
+ "--accept-package-agreements".into(),
+ "--accept-source-agreements".into(),
+ ],
+ automatic: true,
+ });
+ }
+ if cfg!(target_os = "macos") {
+ let brew = resolve("brew")?;
+ return Some(SystemInstallPlan {
+ manager: "Homebrew".into(),
+ command: vec![
+ brew.to_string_lossy().into_owned(),
+ "install".into(),
+ "--cask".into(),
+ "ollama-app".into(),
+ ],
+ automatic: true,
+ });
+ }
+ None
+}
+
+pub(crate) fn executable_candidates() -> Vec {
+ let mut candidates = Vec::new();
+ if cfg!(windows) {
+ if let Some(local) = env::var_os("LOCALAPPDATA") {
+ candidates.push(
+ PathBuf::from(local)
+ .join("Programs")
+ .join("Ollama")
+ .join("ollama.exe"),
+ );
+ }
+ }
+ if cfg!(target_os = "macos") {
+ candidates.push(
+ PathBuf::from("/Applications")
+ .join("Ollama.app")
+ .join("Contents")
+ .join("Resources")
+ .join("ollama"),
+ );
+ }
+ candidates
+}
+
+#[cfg(windows)]
+fn find_winget_ollama_in(root: &Path) -> Option {
+ let entries = fs::read_dir(root).ok()?;
+ let mut matches = Vec::new();
+ for entry in entries.flatten() {
+ let package = entry.path();
+ if !package.is_dir()
+ || !package
+ .file_name()
+ .is_some_and(|name| name.to_string_lossy().starts_with("Ollama.Ollama_"))
+ {
+ continue;
+ }
+ let executable = package.join("ollama.exe");
+ if executable.is_file() {
+ matches.push(executable);
+ }
+ }
+ matches.sort();
+ matches.into_iter().next()
+}
+
+#[cfg(windows)]
+pub(crate) fn resolve_winget_ollama_executable() -> Option {
+ let local = env::var_os("LOCALAPPDATA")?;
+ let root = PathBuf::from(local)
+ .join("Microsoft")
+ .join("WinGet")
+ .join("Packages");
+ find_winget_ollama_in(&root)
+ .and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn supported_install_plans_never_run_an_unattended_shell_script() {
+ if let Some(plan) = system_install_plan(|name| Some(PathBuf::from(name))) {
+ let command = plan.command.join(" ").to_ascii_lowercase();
+ assert!(!command.contains("curl"));
+ assert!(!command.contains("powershell"));
+ assert!(!command.contains("sh -"));
+ }
+ }
+
+ #[test]
+ fn platform_versions_are_compared_as_numeric_triples() {
+ assert_eq!(
+ version_meets_minimum("Microsoft Windows [Version 10.0.19045.1]", (10, 0, 19045)),
+ Some(true)
+ );
+ assert_eq!(
+ version_meets_minimum("Microsoft Windows [Version 10.0.19044.1]", (10, 0, 19045)),
+ Some(false)
+ );
+ assert_eq!(version_meets_minimum("14.0.0", (14, 0, 0)), Some(true));
+ assert_eq!(version_meets_minimum("14.0", (14, 0, 0)), Some(true));
+ assert_eq!(version_meets_minimum("13.6.9", (14, 0, 0)), Some(false));
+ assert_eq!(version_meets_minimum("unknown", (14, 0, 0)), None);
+ }
+
+ #[cfg(windows)]
+ #[test]
+ fn windows_install_is_explicit_and_non_interactive() {
+ let plan =
+ system_install_plan(|name| (name == "winget").then(|| PathBuf::from("winget.exe")))
+ .expect("install plan");
+ assert!(
+ plan.command
+ .windows(2)
+ .any(|pair| pair == ["--id", "Ollama.Ollama"])
+ );
+ assert!(
+ plan.command
+ .iter()
+ .any(|value| value == "--disable-interactivity")
+ );
+ }
+}
diff --git a/desktop/src-tauri/src/target_profiles.rs b/desktop/src-tauri/src/target_profiles.rs
index 6739e04b..5501d644 100644
--- a/desktop/src-tauri/src/target_profiles.rs
+++ b/desktop/src-tauri/src/target_profiles.rs
@@ -2049,7 +2049,7 @@ mod tests {
existing.display_name = "Editing workstation".into();
existing.executable = PathBuf::from("/stale/vidxp");
existing.model_directory = Some(PathBuf::from("/legacy/models"));
- existing.capabilities = vec!["dialogue".into()];
+ existing.capabilities = vec!["speech".into()];
let reconciled = reconcile_managed_profile(
Some(&existing),
diff --git a/desktop/src/App.test.tsx b/desktop/src/App.test.tsx
index bdc08ec8..d16d8eee 100644
--- a/desktop/src/App.test.tsx
+++ b/desktop/src/App.test.tsx
@@ -96,7 +96,7 @@ describe('desktop target lifecycle', () => {
browser: { extra: 'frontend', label: 'Browser interface', description: 'Open VidXP in your browser.', default: true },
mcp: { extra: 'mcp', label: 'AI assistant integration', description: 'Connect a compatible AI app.', default: false },
server: { extra: 'server', label: 'App integration service', description: 'Let other local apps connect.', default: false },
- } });
+ }, local_answers: { engine: 'ollama', model: 'qwen3.5:4b-q4_K_M', download_size_bytes: 3650722202, label: 'Local grounded answers', description: 'Turn search evidence into cited answers locally.' } });
mocks.runtimeStatus.mockResolvedValue({ state: 'never_configured', ready: false, runtime_profile: null, package_version: '0.4.0', capabilities: [], surfaces: [], model_directory: 'C:\\Models', detail: 'No managed runtime yet.' });
mocks.modelDirectoryInventory.mockResolvedValue({ directory: 'C:\\Models', exists: false, readable: true, total_bytes: 0, file_count: 0, recognized_models: [], empty: true, verification_required: false, truncated: false, detail: 'Empty.' });
mocks.installMediaRuntime.mockResolvedValue({ ready: true });
@@ -517,6 +517,23 @@ describe('desktop target lifecycle', () => {
}));
});
+ it('installs the approved local answer model without asking for a URL', async () => {
+ const user = userEvent.setup();
+ renderApp();
+ await enterManaged(user);
+
+ await user.click(screen.getByRole('checkbox', { name: /Local grounded answers/i }));
+ expect(screen.getByText(/There is no URL to enter/i)).toBeVisible();
+ expect(screen.getByText(/grounded-answer model adds 3.40 GiB/i)).toBeVisible();
+ await user.click(screen.getByRole('button', { name: 'Install VidXP' }));
+
+ expect(mocks.installMediaRuntime).toHaveBeenCalledWith('draft-1', 9);
+ expect(mocks.installRuntime).toHaveBeenCalledWith(expect.objectContaining({
+ local_answers: true,
+ draft_id: 'draft-1',
+ }));
+ });
+
it('installs Premiere with its processing and private-service dependencies in one setup flow', async () => {
mocks.premiereIntegrationState.mockResolvedValue({
installations: [{ display_name: 'Adobe Premiere Pro 2023', version: '23.2.0.69', executable: 'C:\\Program Files\\Adobe\\Premiere.exe', host_kind: 'cep', compatible: true }],
@@ -649,11 +666,11 @@ describe('desktop target lifecycle', () => {
total: 8,
stage: 'models',
message: 'Verifying and downloading selected model files',
- model_message: 'Downloading dialogue transcription model.',
+ model_message: 'Downloading speech transcription model.',
model_current: 512 * 1024 * 1024,
model_total: 1024 * 1024 * 1024,
});
- expect(await screen.findByText('Downloading dialogue transcription model.')).toBeVisible();
+ expect(await screen.findByText('Downloading speech transcription model.')).toBeVisible();
expect(screen.getByText('512.0 MiB of 1.00 GiB')).toBeVisible();
expect(screen.getByRole('progressbar', { name: 'Current model download progress' })).toHaveAttribute('aria-valuenow', '50');
diff --git a/desktop/src/components/ManagedSetup.tsx b/desktop/src/components/ManagedSetup.tsx
index 1c1a24f4..95126656 100644
--- a/desktop/src/components/ManagedSetup.tsx
+++ b/desktop/src/components/ManagedSetup.tsx
@@ -59,6 +59,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
const [premiere, setPremiere] = useState(null);
const [premiereEnabled, setPremiereEnabled] = useState(premiereRequested);
const [prepareDuringInstall, setPrepareDuringInstall] = useState(true);
+ const [localAnswers, setLocalAnswers] = useState(false);
const [modelDirectory, setModelDirectory] = useState('');
const [inventory, setInventory] = useState(null);
const [operation, setOperation] = useState('load');
@@ -122,6 +123,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
setSurfaces(shouldEnablePremiere ? [...new Set([...nextSurfaces, 'worker', 'server'])] : nextSurfaces);
setPremiereEnabled(shouldEnablePremiere);
setModelDirectory(nextStatus.model_directory);
+ setLocalAnswers(recoverable ? Boolean(nextStatus.local_answers) : false);
setPrepareDuringInstall(!recoverable);
setInventory(nextInventory);
setMessage(nextStatus.ready ? 'VidXP is ready.' : nextStatus.detail);
@@ -235,6 +237,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
surfaces: premiereEnabled ? [...new Set([...surfaces, 'worker', 'server'])] : [...surfaces],
premiere: premiereEnabled,
prepare_models: prepareDuringInstall,
+ local_answers: localAnswers,
model_directory: modelDirectory || undefined,
draft_id: draftId,
};
@@ -246,13 +249,13 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
setSetupProgress({
draft_id: draftId,
current: 1,
- total: captured.prepare_models ? 8 : 7,
+ total: (captured.prepare_models ? 8 : 7) + (captured.local_answers ? 1 : 0),
stage: 'video-tools',
message: 'Checking FFmpeg and required video codecs',
});
try {
setMessage('Checking FFmpeg and required codecs…');
- await installMediaRuntime(draftId, captured.prepare_models ? 8 : 7);
+ await installMediaRuntime(draftId, (captured.prepare_models ? 8 : 7) + (captured.local_answers ? 1 : 0));
if (status?.state === 'broken' && status.runtime_profile && !dirty) {
const repaired = await runtimeStatus();
if (repaired.ready) {
@@ -311,7 +314,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
setSetupProgress((current) => ({
draft_id: draftId,
current: current?.current ?? 1,
- total: current?.total ?? (prepareDuringInstall ? 8 : 7),
+ total: current?.total ?? (prepareDuringInstall ? 8 : 7) + (localAnswers ? 1 : 0),
stage: current?.stage ?? 'cancelling',
message: 'Stopping setup safely',
model_message: current?.model_message,
@@ -371,6 +374,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
!sameValues(capabilities, status?.capabilities ?? [])
|| !sameValues(surfaces, status?.surfaces ?? [])
|| modelDirectory !== status?.model_directory
+ || localAnswers !== Boolean(status?.local_answers)
);
const premiereNeedsInstall = premiereEnabled && !premiere?.cep_installed && !premiere?.uxp_installed;
@@ -383,6 +387,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
setPremiereEnabled(keepPremiere);
setSurfaces(keepPremiere ? [...new Set([...status.surfaces, 'worker', 'server'])] : status.surfaces);
setModelDirectory(status.model_directory);
+ setLocalAnswers(Boolean(status.local_answers));
setInventory(null);
setFailure(null);
try {
@@ -395,7 +400,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
const isBusy = operation !== null;
const attentionTitle = /ffmpeg|ffprobe/i.test(message) ? 'Video tools need attention' : 'VidXP needs attention';
const progressCurrent = setupProgress?.current ?? 1;
- const progressTotal = setupProgress?.total ?? (prepareDuringInstall ? 8 : 7);
+ const progressTotal = setupProgress?.total ?? (prepareDuringInstall ? 8 : 7) + (localAnswers ? 1 : 0);
const selectedModelDownloads = new Map();
for (const capability of capabilities) {
for (const model of manifest?.capabilities[capability]?.models ?? []) {
@@ -407,7 +412,15 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
}
const selectedModelBytes = [...selectedModelDownloads.values()].reduce((total, bytes) => total + bytes, 0);
const managedRuntimeBytes = manifest?.managed_runtime_estimated_size_bytes ?? 0;
- const plannedSetupBytes = managedRuntimeBytes + selectedModelBytes;
+ const localAnswerSpec = manifest?.local_answers ?? {
+ engine: 'ollama',
+ model: 'qwen3.5:4b-q4_K_M',
+ download_size_bytes: 3650722202,
+ label: 'Local grounded answers',
+ description: 'Turn VidXP search evidence into cited answers on this computer.',
+ };
+ const localAnswerModelBytes = localAnswers ? localAnswerSpec.download_size_bytes : 0;
+ const plannedSetupBytes = managedRuntimeBytes + selectedModelBytes + localAnswerModelBytes;
const capabilityModelSummary = capabilities
.map((id) => {
const capability = manifest?.capabilities[id];
@@ -500,6 +513,22 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
+
+
Grounded answers
+
setLocalAnswers(event.currentTarget.checked)}
+ label={localAnswerSpec.label}
+ description={`${localAnswerSpec.description} Model download: ${formatBytes(localAnswerSpec.download_size_bytes)}.`}
+ />
+ {localAnswers && (
+
+ VidXP checks for Ollama, asks before installing it, starts only a VidXP-owned service when needed, and configures the browser, API, worker, Premiere, and MCP surfaces automatically. There is no URL to enter.
+
+ )}
+
+
Interfaces and integrations
@@ -520,11 +549,11 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
- Downloaded model storage VidXP keeps the files needed by your selected search features here. {modelDirectory && Storage location {displayPath(modelDirectory)} }
+ Downloaded model storage VidXP keeps search models here. A reused external Ollama service continues to own its existing model store. {modelDirectory && Storage location {displayPath(modelDirectory)} }
} loading={operation === 'folder'} disabled={isBusy} onClick={() => void chooseFolder()}>Change location…
- The managed runtime can use approximately {formatBytes(managedRuntimeBytes)}. Selected model downloads total up to {formatBytes(selectedModelBytes)}.
+ The managed runtime can use approximately {formatBytes(managedRuntimeBytes)}. Selected model downloads total up to {formatBytes(selectedModelBytes)}.{localAnswers ? ` The grounded-answer model adds ${formatBytes(localAnswerModelBytes)}.` : ''}
{capabilityModelSummary}
Plan for approximately {formatBytes(plannedSetupBytes)} locally, plus temporary installation space, indexes, and videos. Valid cached model files are reused.
@@ -566,6 +595,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
VidXP is installed
Search: {status.capabilities.map((id) => manifest?.capabilities[id]?.label || id).join(', ') || 'none selected'}
Processing and access: {status.surfaces.map((id) => manifest?.surfaces[id]?.label || id).join(', ') || 'command line only'}
+
Grounded answers: {status.local_answers ? localAnswerSpec.model : 'not installed'}
Technical details Version {status.package_version} Models: {displayPath(status.model_directory)}
)}
@@ -608,7 +638,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
{setupElapsed}s elapsed
- {setupProgress?.stage === 'models'
+ {(setupProgress?.stage === 'models' || setupProgress?.stage === 'local-answers')
&& setupProgress.model_message && (
diff --git a/desktop/src/tauri.test.ts b/desktop/src/tauri.test.ts
index 422388c3..9fab4d41 100644
--- a/desktop/src/tauri.test.ts
+++ b/desktop/src/tauri.test.ts
@@ -77,6 +77,7 @@ describe('desktop IPC adapter', () => {
capabilities: ['actor'],
surfaces: ['browser'],
prepare_models: false,
+ local_answers: false,
draft_id: draft.id,
};
invoke.mockResolvedValueOnce(draft).mockResolvedValueOnce({
diff --git a/desktop/src/tauri.ts b/desktop/src/tauri.ts
index 591a231a..43c98446 100644
--- a/desktop/src/tauri.ts
+++ b/desktop/src/tauri.ts
@@ -150,6 +150,13 @@ export interface RuntimeManifest {
managed_runtime_estimated_size_bytes: number;
capabilities: Record;
surfaces: Record;
+ local_answers: {
+ engine: string;
+ model: string;
+ download_size_bytes: number;
+ label: string;
+ description: string;
+ };
}
export interface RuntimeStatus {
@@ -160,6 +167,7 @@ export interface RuntimeStatus {
capabilities: string[];
surfaces: string[];
model_directory: string;
+ local_answers: boolean;
detail: string;
}
@@ -185,6 +193,7 @@ export interface InstallRuntimeRequest {
capabilities: string[];
surfaces: string[];
prepare_models: boolean;
+ local_answers: boolean;
model_directory?: string;
draft_id: string;
}
@@ -205,6 +214,7 @@ export interface InstallRuntimeResult {
capabilities: string[];
surfaces: string[];
model_directory: string;
+ local_answers: boolean;
prepared: boolean;
}
diff --git a/docs/adding-a-capability.md b/docs/adding-a-capability.md
index fd541dcf..55509e33 100644
--- a/docs/adding-a-capability.md
+++ b/docs/adding-a-capability.md
@@ -36,7 +36,7 @@ needs:
```
Use an existing capability with a similar shape as a starting point. Scene,
-dialogue, action search, and actor features demonstrate different combinations
+speech, action search, and actor features demonstrate different combinations
of shared indexing and operations.
## 2. Define the public contract
diff --git a/docs/architecture/platform.md b/docs/architecture/platform.md
index c8dd8ef8..ca55ee19 100644
--- a/docs/architecture/platform.md
+++ b/docs/architecture/platform.md
@@ -767,7 +767,7 @@ composition root and is sorted deterministically.
`dropbox-dash/faster-whisper-large-v3-turbo` at immutable revision
`0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf`. Its transcript/timestamp output is
tested against the existing
- dialogue contract. Forced alignment is an optional provider behind a separate
+ speech contract. Forced alignment is an optional provider behind a separate
contract; it cannot hold base transcription or Python back. Sentence embeddings
use Qwen3-Embedding-0.6B at immutable revision
`97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3`; its published multilingual MTEB
@@ -864,8 +864,27 @@ evidence fallback. Typed JSON alone is never treated as proof of grounding.
The result records the configured provider/model identity and plan for
reproducibility.
-The default SLM model ID, immutable revision/digest, and quantization are
-intentionally unset until candidate models pass:
+The default local query model is the official Ollama
+`qwen3.5:4b-q4_K_M` artifact: Qwen3.5 4B with Q4_K_M quantization. Enabling a
+self-hosted Ollama base URL selects that model unless an operator explicitly
+overrides it. VidXP sets temperature zero, disables reasoning output, and
+requires the native JSON schemas for both planning and synthesis. Model weights
+are never bundled. Desktop setup pulls the approved artifact only after the
+user selects local grounded answers and approves any required Ollama install;
+CLI and server operators pull it explicitly.
+
+Desktop treats the provider as an optional supervised system dependency. It
+first probes the loopback `/api/version` and `/api/tags` contracts, reuses an
+existing healthy service without taking ownership, or starts a child
+`ollama serve` process that its existing process-tree supervisor owns. The
+model pull uses Ollama's streaming `/api/pull` contract. Desktop persists only
+the feature selection, injects the private `/v1` endpoint and approved model
+into managed processes, and includes the same non-secret environment in stdio
+MCP configuration. It never stops an externally owned Ollama service.
+
+Published model results select the integration candidate; the repository gate
+does not attempt to reproduce general model leaderboards. Promotion still
+requires the narrow checks that can fail specifically in VidXP:
- both output schemas
- adversarial-plan rejection
@@ -875,10 +894,12 @@ intentionally unset until candidate models pass:
- offline-cache behavior
- license and redistribution review
-Until that gate passes, the provider is fixed to self-hosted Ollama but no arbitrary
-caller-provided model string is accepted. Ollama runs as an internal optional `slm`
-Compose profile with a persistent model cache; deterministic evidence retrieval
-remains available when it is disabled.
+The provider remains fixed to self-hosted Ollama. Ollama runs as an internal
+optional `slm` Compose profile with a persistent model cache; deterministic
+evidence retrieval remains available when it is disabled. The current adapter
+receives typed retrieval evidence rather than media bytes. Direct keyframe,
+video, and audio enrichment is a separate application-layer step, not an
+implicit capability of selecting a multimodal checkpoint.
## 17. Artifact and snippet delivery
diff --git a/docs/benchmarking/README.md b/docs/benchmarking/README.md
index e4aecea9..4e9920fa 100644
--- a/docs/benchmarking/README.md
+++ b/docs/benchmarking/README.md
@@ -18,6 +18,7 @@ installation and product usage, start with the main
| HiREST transcript localization | Legacy full result + current smoke | The legacy MiniLM stack scored all 193 validation pairs; current Qwen3 passed a two-video real execution smoke; 776 released test predictions remain unscored because their public bounds are placeholders |
| Environmental-sound retrieval | Implementation complete; benchmark pending | FineLAP stores global ten-second windows and dense timestamped sound activations; no VidXP quality score is claimed yet |
| LongVALE combined evaluation | Next adapter and pilot | Validate vision, environmental sound, and speech together on one evaluation archive before scheduling the full run |
+| Codex MCP ablation | Runnable scaffold; not run | Promptfoo pairs the same Codex video tasks with and without VidXP MCP; no agent result is claimed yet |
| Actor clustering | Data-gated | The preferred BBT/Buffy evaluation still requires lawful access to the source episodes |
Read [current results](results.md) for the scores, plain-language metric
@@ -32,6 +33,7 @@ definitions, honest comparisons, and the next benchmark decision.
| Understand the benchmark-ready Python structure | [Core contract](core_contract.md) |
| See which benchmarks exist and what each measures | [Benchmark catalog](benchmark_catalog.md) |
| Understand the current model and benchmark choices | [Multimodal model direction](model_selection.md) |
+| Run the Codex MCP-on/MCP-off experiment | [Codex agent ablation](agent_ablation.md) |
| Find exact published competitor scores | [Published comparison results](published_results.md) |
| Review the relevant papers | [Research-paper inventory](research_papers.md) |
| Audit what was checked in each paper | [Paper-validation ledger](paper_validation.md) |
diff --git a/docs/benchmarking/adapter_validation.md b/docs/benchmarking/adapter_validation.md
index aa92a8c3..e9976c81 100644
--- a/docs/benchmarking/adapter_validation.md
+++ b/docs/benchmarking/adapter_validation.md
@@ -86,15 +86,15 @@ command, working directory, compatibility note, output, and return code.
1. Verify the test split, categories, evaluator, and released-ASR archive.
2. Select only declared `clip: true` prompt/video pairs.
3. Parse the matching released SRT files with the `srt` package.
-4. Split each SRT cue into the configured five-word dialogue phrases. Because
+4. Split each SRT cue into the configured five-word speech phrases. Because
released SRT cues do not contain word timestamps, phrase bounds are
interpolated linearly within the real cue bounds and disclosed in the run
manifest.
-5. Submit the timestamped phrases to the dialogue-only VidXP core. The legacy
+5. Submit the timestamped phrases to the speech-only VidXP core. The earlier
full run used MiniLM; the current smoke used Qwen3 Embedding. Neither path
loads a transcription model or decodes video.
6. Search each prompt only within its known video and retrieve every stored
- dialogue phrase. Project phrase scores onto one-second bins, assign uncovered
+ speech phrase. Project phrase scores onto one-second bins, assign uncovered
seconds an explicit absence penalty, and rank duration-relative windows by
their mean score with an earliest-start tie break.
7. Clamp the selected window to the official video duration, then reject missing,
@@ -263,7 +263,7 @@ copy were removed after recording the evidence above.
Install the optional adapters and initialize FFmpeg:
```powershell
-uv tool install "vidxp[scene,dialogue,benchmarks]"
+uv tool install "vidxp[benchmarks,scene,speech]"
vidxp init
```
diff --git a/docs/benchmarking/agent_ablation.md b/docs/benchmarking/agent_ablation.md
new file mode 100644
index 00000000..e26e4a03
--- /dev/null
+++ b/docs/benchmarking/agent_ablation.md
@@ -0,0 +1,274 @@
+# Codex evaluation with and without VidXP MCP
+
+Collection index: [Benchmarking research](README.md)
+
+Status: Runnable scaffold; no agent results recorded
+
+Last verified: 2026-08-30
+
+This experiment measures whether access to VidXP through its local stdio MCP
+server improves a Codex agent's ability to find timestamped evidence in long
+videos. It is a product-level ablation, not a replacement for published model
+benchmarks such as MAEB, MVEB, or AEGBench.
+
+## What the comparison holds constant
+
+Every task runs once in each condition with the same Codex model, reasoning
+effort, prompt, media workspace, filesystem sandbox, network policy, output
+schema, and fresh thread:
+
+| Condition | VidXP access | Purpose |
+| --- | --- | --- |
+| `codex-vidxp-mcp` | The local `vidxp-mcp` stdio server | Measure the complete agent-plus-VidXP workflow |
+| `codex-no-mcp` | No MCP server and no direct VidXP CLI use | Measure what the same Codex agent can recover from the local media without VidXP |
+
+The two conditions use an isolated `CODEX_HOME` that contains authentication but
+no ambient MCP servers, plugins, or skills. Promptfoo receives the MCP definition
+through the Codex provider's `cli_config`; the MCP-off provider receives no such
+definition. Streaming traces must prove that MCP-on used at least one VidXP tool
+and MCP-off neither used a VidXP tool nor invoked the VidXP CLI through the shell.
+
+The committed configuration disables network access, persistent threads, result
+caching, provider retries, parallel execution, and Codex subagents. These
+controls reduce leakage, cross-task state, and accidental extra model runs.
+
+## Why Promptfoo owns orchestration
+
+[Promptfoo](https://www.promptfoo.dev/docs/providers/openai-codex-sdk/) runs the
+paired provider matrix, repetitions, structured output, traces, usage
+collection, and local reports. VidXP's Python benchmark code owns task expansion
+and deterministic scoring. This division avoids rebuilding a general evaluation
+runner while keeping official temporal metrics and dataset logic reviewable in
+the repository. This follows OpenAI's documented
+[Codex evaluation workflow](https://learn.chatgpt.com/use-cases/ai-app-evals).
+
+Promptfoo is not needed to choose a component model from published leaderboards.
+It is used here because this experiment evaluates an agent workflow and its tool
+trajectory. Inspect AI or Harbor would become stronger candidates if the work
+expands into a provider-independent public agent benchmark or centralized
+leaderboard.
+
+The harness choice was made against the actual subscription-authenticated Codex
+constraint, not just against generic eval feature lists:
+
+| Harness | Decision for this experiment |
+| --- | --- |
+| Promptfoo Codex SDK | Selected: directly reuses Codex login, forwards per-provider Codex/MCP configuration, repeats paired cases, and captures usage and tool traces |
+| Native Codex SDK/CLI | Capable, but would require custom pairing, retry, aggregation, and report plumbing that Promptfoo already provides |
+| [Inspect AI](https://inspect.aisi.org.uk/) | Stronger for portable research evals, but subscription-authenticated Codex requires a custom bridge rather than its standard model path |
+| [EvalBench](https://github.com/GoogleCloudPlatform/evalbench) | Supports MCP scenarios, but its documented Codex path is API-key oriented and its simulated-user turns would add runs not needed here |
+| [Harbor](https://github.com/harbor-framework/harbor) | Strong containerized agent benchmark infrastructure, but heavyweight and credential/API oriented for this local pilot |
+| [DeepEval](https://github.com/confident-ai/deepeval) | Potential later scorer layer; it does not provide the direct Codex runner needed here |
+| Braintrust, LangSmith, or Phoenix | Potential result/trace backends, not substitutes for the local Codex runner |
+
+If this grows into the centralized public benchmark discussed in the roadmap,
+revisit Inspect or Harbor. That is a different deliverable from establishing the
+VidXP MCP effect under the user's existing Codex plan.
+
+## Pilot dataset and exact videos
+
+The first pilot uses the human-refined LongVALE evaluation annotations and the
+smallest raw evaluation archive, `LongVALE_test_1171_part_9.zip`. At pinned
+dataset revision `18889b01886e30c36b0d1c650ac4439ad460ee73`, the archive is
+1,063,510,782 bytes, has SHA-256
+`c83d62557f102c6d41ea95c2c3b3581657481c8646cc70b1e12a85ead27a7ae3`, and
+contains 28 videos. The annotation file is 4,522,592 bytes.
+
+Only these five videos are indexed for the first ten-task pilot:
+
+| Video ID | Seed coverage |
+| --- | --- |
+| `ZYTmgi1pAIE` | rain, wind, engine start, bell, and a visual title transition |
+| `ZIdFAGJrlCw` | driving action, siren, engine revving, and a short sketching action |
+| `ZGXCr5n8Frg` | visible speaker plus spoken corporate content |
+| `_py1WXVX4oc` | sign-language action, title text, and a ringing telephone |
+| `ZVUAC3m48G0` | short cooking actions and a visual-plus-drumbeat event |
+
+The task manifest is
+[`benchmarks/codex-mcp/tasks/longvale-part9-pilot.json`](../../benchmarks/codex-mcp/tasks/longvale-part9-pilot.json).
+It contains scene, action, environmental-sound, and speech cases, including
+events that require more than one channel. The full LongVALE denominator remains
+the later official target; this deliberately selected pilot validates the
+integration and cannot support a LongVALE quality claim.
+
+OVSD remains a separate, open-licensed scene-boundary regression source. It can
+test segmentation and temporal-unit construction, but it has no natural-language
+retrieval, action-label, environmental-sound, speech, or cross-modal task. OVSD
+therefore does not replace LongVALE in this ablation.
+
+## Prepare the isolated environment
+
+Promptfoo 0.122.2 requires Node.js 22.22.0 or newer. The benchmark-local
+`.npmrc` enforces that requirement so an unsupported runtime fails during
+installation instead of failing after Codex runs have begun. Install VidXP and
+the local evaluation dependencies:
+
+```powershell
+uv sync --frozen --extra local-worker --extra mcp --extra benchmarks
+npm --prefix benchmarks/codex-mcp ci
+```
+
+Create all mutable state outside the checkout. The paths below are examples;
+keep the same values for both conditions:
+
+```powershell
+$evalRoot = Join-Path $env:LOCALAPPDATA 'VidXP\benchmarks\codex-mcp'
+$env:VIDXP_EVAL_CODEX_HOME = Join-Path $evalRoot 'codex-home'
+$env:VIDXP_EVAL_WORKSPACE = Join-Path $evalRoot 'workspace'
+$env:VIDXP_EVAL_DATA_DIR = Join-Path $evalRoot 'vidxp-data'
+$env:VIDXP_EVAL_INDEX_DIR = Join-Path $evalRoot 'vidxp-index'
+$env:VIDXP_MCP_COMMAND = (Resolve-Path '.venv\Scripts\vidxp-mcp.exe').Path
+$env:VIDXP_EVAL_REPOSITORY = 'default'
+$env:VIDXP_EVAL_DEVICE = 'cpu'
+$env:VIDXP_EVAL_MODEL = 'gpt-5.6-sol'
+$env:VIDXP_EVAL_REASONING = 'medium'
+
+New-Item -ItemType Directory -Force `
+ $env:VIDXP_EVAL_CODEX_HOME, `
+ $env:VIDXP_EVAL_WORKSPACE, `
+ (Join-Path $env:VIDXP_EVAL_WORKSPACE 'media'), `
+ $env:VIDXP_EVAL_DATA_DIR, `
+ $env:VIDXP_EVAL_INDEX_DIR | Out-Null
+
+$env:CODEX_HOME = $env:VIDXP_EVAL_CODEX_HOME
+codex login
+Remove-Item Env:CODEX_HOME
+```
+
+Do not copy or commit `auth.json`. The preflight rejects an isolated Codex
+configuration that declares any ambient `[mcp_servers]` section.
+
+## Fetch and index the pilot media
+
+Accept the LongVALE dataset terms before downloading. Fetch only the pinned
+annotation and part-nine evaluation archive:
+
+The commands below require the Hugging Face `hf` CLI. Install it separately if
+it is not already available; it is a dataset-transfer tool and is not part of
+VidXP's runtime dependency set.
+
+```powershell
+$artifactRoot = Join-Path $evalRoot 'longvale-artifacts'
+hf download ttgeng233/LongVALE `
+ longvale-annotations-eval.json `
+ raw_videos_test/LongVALE_test_1171_part_9.zip `
+ --repo-type dataset `
+ --revision 18889b01886e30c36b0d1c650ac4439ad460ee73 `
+ --local-dir $artifactRoot
+
+$archive = Join-Path $artifactRoot `
+ 'raw_videos_test\LongVALE_test_1171_part_9.zip'
+(Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
+Expand-Archive -LiteralPath $archive -DestinationPath $artifactRoot
+```
+
+The printed hash must equal the pinned SHA-256 above. Copy the five selected
+MP4s into `$env:VIDXP_EVAL_WORKSPACE\media`, preserving their filenames:
+
+```powershell
+$sourceMedia = Join-Path $artifactRoot 'video_test_1171'
+$videoIds = @(
+ 'ZYTmgi1pAIE',
+ 'ZIdFAGJrlCw',
+ 'ZGXCr5n8Frg',
+ '_py1WXVX4oc',
+ 'ZVUAC3m48G0'
+)
+foreach ($videoId in $videoIds) {
+ Copy-Item -LiteralPath (Join-Path $sourceMedia "$videoId.mp4") `
+ -Destination (Join-Path $env:VIDXP_EVAL_WORKSPACE 'media')
+}
+```
+
+Prepare and index all four evidence paths:
+
+```powershell
+uv run --no-sync vidxp `
+ --data-dir $env:VIDXP_EVAL_DATA_DIR `
+ --index-dir $env:VIDXP_EVAL_INDEX_DIR `
+ prepare --modalities scene,action,sound,speech --yes
+
+foreach ($videoId in $videoIds) {
+ $mediaPath = Join-Path $env:VIDXP_EVAL_WORKSPACE "media\$videoId.mp4"
+ $asset = uv run --no-sync vidxp `
+ --data-dir $env:VIDXP_EVAL_DATA_DIR `
+ --index-dir $env:VIDXP_EVAL_INDEX_DIR `
+ media import $mediaPath --json | ConvertFrom-Json
+
+ uv run --no-sync vidxp `
+ --data-dir $env:VIDXP_EVAL_DATA_DIR `
+ --index-dir $env:VIDXP_EVAL_INDEX_DIR `
+ index create $asset.media_id `
+ --modality scene `
+ --modality action `
+ --modality sound `
+ --modality speech
+}
+```
+
+## Validate before spending runs
+
+The following commands perform no Codex inference:
+
+```powershell
+npm --prefix benchmarks/codex-mcp run check
+npm --prefix benchmarks/codex-mcp run preflight
+```
+
+Preflight verifies the dedicated Codex authentication, absence of ambient MCP
+configuration, all five media files, the index paths, and a real VidXP MCP
+handshake. Do not run the matrix if it fails.
+
+The first paid/allowance-consuming smoke is one task in both conditions: two
+Codex runs total.
+
+```powershell
+npm --prefix benchmarks/codex-mcp run eval:smoke
+```
+
+Inspect both outputs and their trajectories before continuing. The pilot command
+runs ten tasks in two conditions with three repetitions: 60 Codex runs total.
+
+```powershell
+npm --prefix benchmarks/codex-mcp run eval:pilot
+```
+
+Promptfoo Community and the repository's Python evaluation code are no-cost
+open-source software. The local MCP server and local VidXP processing create no
+OpenAI or Anthropic inference charge, but downloading and indexing consume local
+bandwidth, disk, electricity, and any paid infrastructure the operator chooses;
+the dataset and model licenses still apply. Codex inference authenticated
+through the dedicated ChatGPT login consumes the account's Codex plan allowance
+or credits. API-key authentication instead incurs API usage charges. No
+LLM-as-judge assertion is enabled, so this scaffold does not add grader calls.
+The run count is therefore exactly two for the smoke and 60 for the pilot.
+Promptfoo reports usage, but it cannot determine the remaining ChatGPT-plan
+allowance or convert subscription-authenticated runs into an exact dollar
+charge; use the Codex account usage display for that limit.
+
+## Scoring and interpretation
+
+Each response must identify one interval. The deterministic scorer records
+temporal IoU, R@1 at tIoU 0.3/0.5/0.7, interval validity, and whether the expected
+MCP boundary was respected. Report at least:
+
+- success rate and mean IoU by condition;
+- results by scene, action, sound, speech, and joint-modality task;
+- token usage, latency, failures, and retries;
+- VidXP MCP tool trajectories for MCP-on;
+- indexing time, index size, model preparation, and machine details; and
+- every excluded or failed task.
+
+Do not call the ten-task pilot a LongVALE result. A publishable result requires
+the complete official evaluation split, its one-interval output conversion, and
+the official evaluator. A centralized benchmark would additionally need frozen
+agent versions, provider-independent authentication, portable environments, and
+public result governance.
+
+The MCP-off condition is intentionally a local-agent baseline, not a native
+video-model benchmark. The Codex SDK accepts text and local images but does not
+accept video or audio inputs directly. With the network disabled and the
+workspace read-only, MCP-off may use installed read-only shell inspection tools
+but cannot call VidXP or persist extracted media. Report this limitation with
+the results; component-model quality remains covered by the published benchmark
+record elsewhere in this collection.
diff --git a/docs/benchmarking/benchmark_catalog.md b/docs/benchmarking/benchmark_catalog.md
index 5bf609b1..f31614ad 100644
--- a/docs/benchmarking/benchmark_catalog.md
+++ b/docs/benchmarking/benchmark_catalog.md
@@ -57,6 +57,15 @@ review and benchmark stability improve.
See [execution readiness](execution_readiness.md) for the corrected implementation
boundary and per-benchmark engineering classification.
+## Agent-level system ablation
+
+Published model and dataset benchmarks establish component quality, but they do
+not measure the value of exposing this repository through MCP to a working
+agent. The separate [Codex MCP-on/MCP-off protocol](agent_ablation.md) uses
+Promptfoo to run paired LongVALE-derived tasks while keeping the model, prompt,
+media, permissions, and scoring fixed. It is a VidXP system experiment, not a
+new model leaderboard or a substitute for LongVALE's official evaluator.
+
## Verdict meanings
- **Directly runnable:** no task-definition changes are needed.
@@ -77,7 +86,7 @@ protocol. It does not mean that a similar published score is already comparable.
| Dialogue | [TVR `sub-only` queries](https://github.com/jayleicn/TVRetrieval) | Subtitle-related paraphrastic query to ranked video intervals, both known-video and corpus-wide | Whether the configured transcript embedding path can identify and temporally localize relevant TV moments; a subtitle-only run isolates embedding and retrieval | Verbatim quote lookup, scene understanding, actor clustering, generic audio, or end-to-end ASR quality when supplied subtitles are used | Engineering A; raw TV media gated |
| Visual | [TVR `video-only` queries](https://github.com/jayleicn/TVRetrieval) | Visual-language query to ranked video intervals | Whether the configured frame/clip embedding path can retrieve visual moments across a TV corpus | Dialogue retrieval, actor identity, or performance on non-TV domains | Engineering A; raw TV media gated |
| Combined | [TVR `video+sub` queries](https://github.com/jayleicn/TVRetrieval) | Queries annotators judged to need both visual and subtitle evidence | Whether fixed late fusion improves corpus moment retrieval when both implemented paths contain useful evidence | Generic sound understanding, actor clustering, learned cross-modal reasoning, or intended `video+sub` coverage from a dialogue-only ablation | Engineering A with fixed fusion; raw TV media gated |
-| Speech-backed instructional | [HiREST](https://github.com/j-min/HiREST) | Instructional goal to ranked videos, one relevant interval, moment segmentation, and step captioning | Whether VidXP's chunking, dialogue embeddings, vector index, and interval selection retrieve semantically relevant spoken procedural content | Verbatim dialogue search, entertainment-video generalization, actors, transcription accuracy in released-ASR mode, or full video retrieval when negative candidates lack ASR | Legacy MiniLM validation complete; current Qwen3 two-video smoke complete; released test predictions unscored because public bounds are placeholders; full video retrieval gated |
+| Speech-backed instructional | [HiREST](https://github.com/j-min/HiREST) | Instructional goal to ranked videos, one relevant interval, moment segmentation, and step captioning | Whether VidXP's chunking, speech embeddings, vector index, and interval selection retrieve semantically relevant spoken procedural content | Verbatim dialogue search, entertainment-video generalization, actors, transcription accuracy in released-ASR mode, or full video retrieval when negative candidates lack ASR | Earlier MiniLM validation complete; current Qwen3 two-video smoke complete; released test predictions unscored because public bounds are placeholders; full video retrieval gated |
| Narrated retrieval | [QuerYD](https://www.robots.ox.ac.uk/~vgg/data/queryd/) | Paragraph text↔video retrieval and narration text↔localized-clip ranking over supplied ground-truth proposals | Whether VidXP visual representations rank the correct narrated video or oracle segment proposal once source media are processed | In-scene conversational dialogue, overlapping speech, unrestricted boundary prediction, or VidXP quality from the released Collaborative Experts features | Protocol/reference artifacts ready; true VidXP run raw-media gated; narration audio unresolved |
| Ranked search | [TVR-Ranking](https://huggingface.co/axgroup/TVR-Ranking) | Graded ranking of multiple relevant corpus moments for imprecise queries | Whether VidXP orders several partially relevant moments usefully, not merely whether its top result overlaps one answer | ASR quality when subtitles are supplied, actors, or generalization outside TVR | Engineering A after TVR; media/license gated |
| Speech-backed whole-video | [How2R](https://aclanthology.org/2020.emnlp-main.161/) | Instructional-video retrieval using video plus aligned speech/subtitles, introduced with HERO | Whether VidXP can rank instructional clips from transcript and scene evidence under HERO's retrieval setup | Conversational dialogue, temporal boundary prediction, actors, or immediate executability before artifacts/licenses are rechecked | Relevant benchmark; current artifact/access status unresolved |
diff --git a/docs/benchmarking/core_contract.md b/docs/benchmarking/core_contract.md
index 6a2897e8..d9c975ab 100644
--- a/docs/benchmarking/core_contract.md
+++ b/docs/benchmarking/core_contract.md
@@ -88,15 +88,15 @@ config = IndexConfig(
split="test",
run_id="released-asr",
generation_id="423456781234423481234567890abcde",
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
)
run_index([source], config)
```
Scene-only runs do not load a transcription or actor model. Supplied-transcript
-dialogue runs load the dialogue encoder but do not decode video. Actor-only runs
-do not load scene or transcription models. Scene inference, dialogue encoding,
+speech runs load the speech encoder but do not decode video. Actor-only runs do
+not load scene or transcription models. Scene inference, speech encoding,
and Chroma writes use their configured batch sizes. Cancellation is cooperative
and is checked between batches. The Streamlit process exposes cancellation for
indexing workers it started and reports that the current batch must finish
@@ -134,7 +134,7 @@ IDs are lowercase UUID4 hex. Dataset adapters retain official video keys at
their input/evaluator boundary and deterministically map them to valid internal
IDs.
-- Dialogue records store text, start/end, phrase ID, video ID, modality, source
+- Speech records store text, start/end, phrase ID, video ID, modality, source
ID, dataset, split, and run ID.
- Scene records store frame index, timestamp, start/end, FPS, duration, video ID,
modality, source ID, dataset, split, and run ID.
@@ -166,7 +166,7 @@ for hit in result.hits:
Passing a media UUID through `video_id` restricts retrieval to one video; omitting it
searches the run corpus. Results are deterministically ordered by raw distance and
-then source ID. Scene vectors and, by default, dialogue vectors are normalized
+then source ID. Scene vectors and, by default, speech vectors are normalized
before the explicitly configured Chroma distance (`vector_distance`, default
`l2`), making the default ordering cosine-equivalent. The distance is stored in
the run configuration and Chroma collection rather than relying on Chroma's
diff --git a/docs/benchmarking/direction.md b/docs/benchmarking/direction.md
index 2b8eae23..8e08d445 100644
--- a/docs/benchmarking/direction.md
+++ b/docs/benchmarking/direction.md
@@ -54,7 +54,7 @@ keyframe or shot detection, and distributed indexing.
The current CLI and return types are not fixed research constraints. Benchmark work
may add stable corpus IDs, top-k results, scores, richer metadata, start/end
intervals, filtering, deterministic window aggregation, serializers, timing hooks,
-and non-learned late fusion over existing scene and dialogue rankings.
+and non-learned late fusion over existing scene and speech rankings.
These are ordinary adapters. A candidate was not rejected merely because the
pre-refactor application returned one timestamp or stored too little metadata.
diff --git a/docs/benchmarking/execution_readiness.md b/docs/benchmarking/execution_readiness.md
index 74c959d8..6fe43e22 100644
--- a/docs/benchmarking/execution_readiness.md
+++ b/docs/benchmarking/execution_readiness.md
@@ -36,7 +36,7 @@ Allowed benchmark plumbing includes:
- temporal de-duplication or non-maximum suppression;
- fixed sliding-window proposals;
- benchmark-specific prediction serializers and evaluator invocation;
-- non-learned score or rank fusion over existing scene and dialogue outputs;
+- non-learned score or rank fusion over existing scene and speech outputs;
- modality-specific indexing, batching, resumability, and timing instrumentation.
These changes do not alter the benchmark task and do not invalidate comparison with
@@ -253,7 +253,7 @@ approved.
### LongVALE and FLARE
-Fixed late fusion over existing scene and dialogue rankings is permitted baseline
+Fixed late fusion over existing scene and speech rankings is permitted baseline
logic. It makes official retrieval or temporal-grounding runs technically possible.
VidXP still lacks generic sound-event recognition; the result must say so and must
not be described as full omni-modal coverage.
diff --git a/docs/benchmarking/model_selection.md b/docs/benchmarking/model_selection.md
index de3acf68..4f339f2c 100644
--- a/docs/benchmarking/model_selection.md
+++ b/docs/benchmarking/model_selection.md
@@ -5,7 +5,7 @@ Collection index: [Benchmarking research](README.md)
Status: Current decision record; FineLAP and VideoPrism are implemented, while
other candidate providers remain planned unless architecture says otherwise
-Last verified: 2026-08-27
+Last verified: 2026-08-30
## Product requirement
@@ -43,8 +43,11 @@ leaderboards before implementation.
Local evaluation has a narrower purpose: verify preprocessing, timestamps,
memory, latency, index size, failure behavior, and regressions in this repository.
It does not substitute a tiny private sample for broad published comparisons.
-Promptfoo is therefore not required for component-model selection; an agent
-evaluation harness would answer a different question.
+Promptfoo is therefore not required for component-model selection. It is the
+selected runner for the separate [Codex MCP-on/MCP-off agent
+ablation](agent_ablation.md), where paired task execution, repetitions, traces,
+and usage accounting are part of the question. VidXP's Python benchmark code
+continues to own dataset preparation and deterministic temporal scoring.
## Current provider direction
@@ -56,6 +59,8 @@ evaluation harness would answer a different question.
| Visual scene/action retrieval | Evaluate [Qwen3-VL-Embedding-2B](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B) as the practical candidate | Qwen3-VL-Embedding-8B as the quality ceiling; VideoPrism as the incumbent control | MVEB's text-video table ranks Qwen 8B and 2B first and second. The checked table has no directly comparable VideoPrism row, so this is stronger current selection evidence, not proof that VideoPrism lost a head-to-head. |
| Visual temporal grounding | Evaluate [TimeLens2-4B](https://github.com/MCG-NJU/TimeLens2) after candidate retrieval | TimeLens2-8B and existing temporal baselines | The published 4B average nearly matches 8B at much lower cost. TimeLens2 is visual-only and cannot replace the sound or speech channels. |
| Cross-modal fusion | Keep modality-specific providers and fuse timestamped candidates | A unified permissive audio-video-text encoder can be a later comparison | Separate providers preserve provenance, allow independent upgrades, and match the evidence that different model families lead different modalities and tasks. |
+| Query planning and answer synthesis | [Qwen3.5 4B](https://huggingface.co/Qwen/Qwen3.5-4B) through official Ollama `qwen3.5:4b-q4_K_M` | Qwen3.5 9B as a higher-memory comparison | The 4B model has strong published instruction-following and agent results while its official Q4_K_M artifact is approximately 3.4 GB, about half the 9B artifact. VidXP needs bounded schema generation over retrieved evidence, not a second retrieval encoder. |
+| Future media evidence enrichment | Reuse Qwen3.5 vision for selected keyframes before adding another model | Evaluate an audio-video model only for top uncitable sound/action hits | The current adapter sends JSON evidence, so multimodal model support alone changes nothing. Media inputs must remain timestamp-bound derived evidence and must not replace FineLAP, scene, action, or speech retrieval. |
Before promotion, every new checkpoint still needs an immutable revision, artifact
hash, license review, safe-loading review, dependency fit, and a bounded real-media
@@ -71,6 +76,8 @@ Scores are comparable only within the named paper and task.
| [MVEB text-video leaderboard](https://arxiv.org/abs/2606.14958) | Qwen3-VL-Embedding-8B: 60.9 mean; 2B: 58.1; LCO-Embedding-Omni-7B: 56.8 | Prefer Qwen 2B for the practical visual candidate and 8B only when maximizing published quality. |
| [TimeLens2 visual grounding](https://github.com/MCG-NJU/TimeLens2) | Seven-dataset average mIoU: 47.7 for 4B and 48.0 for 8B | Start with 4B; the 0.3-point gain does not justify making 8B the default candidate. |
| [AEGBench](https://arxiv.org/abs/2607.04383) | PE-A-Frame Large: 0.389 mIoU, 0.407 event-F1, 0.607 segment-F1 in the checked table | Use a released specialist to test exact open-vocabulary sound intervals. |
+| [Qwen3.5 4B model card](https://huggingface.co/Qwen/Qwen3.5-4B) | Vendor-reported MMLU-Pro 79.1, IFEval 89.8, BFCL-V4 50.3, and TAU2-Bench 79.9; native 262,144-token context | Select the first local planner/synthesizer from published quality evidence; validate only schema retention, grounding, resource use, and failure behavior in VidXP. |
+| [Official Ollama Q4_K_M artifact](https://ollama.com/library/qwen3.5:4b-q4_K_M) | 4.66B parameters, Q4_K_M, approximately 3.4 GB, Apache-2.0 | Use the official cross-platform build and an explicit pull instead of bundling weights or relying on a community conversion. |
VideoPrism remains a credible multi-frame video encoder. The decision above does
not reject it on quality. It rejects two unsupported claims: that implementation
diff --git a/docs/benchmarking/results.md b/docs/benchmarking/results.md
index 0a2e8be7..bb1c9782 100644
--- a/docs/benchmarking/results.md
+++ b/docs/benchmarking/results.md
@@ -34,7 +34,7 @@ this rerun:
- NVIDIA GeForce RTX 3060 Laptop GPU with 4 GiB VRAM present but unused;
- CPU-only PyTorch execution.
-| Generation | Dialogue embedding | Scene embedding | Sampling/window | Transcription in these benchmarks |
+| Generation | Speech embedding | Scene embedding | Sampling/window | Transcription in these benchmarks |
|---|---|---|---|---|
| Legacy full, 2026-07-27 | `all-MiniLM-L6-v2` | OpenAI CLIP `ViT-B/32` through `clip-anytorch` | HiREST 0.8-duration window; DiDeMo fixed 30-frame stride and max chunk pooling | Released HiREST SRTs; WhisperX `large-v2` was not exercised |
| Current smoke, 2026-07-30 | `Qwen/Qwen3-Embedding-0.6B` at `97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3` | `google/siglip2-base-patch16-224` at `75de2d55ec2d0b4efc50b3e9ad70dba96a7b2fa2` | HiREST same 0.8-duration window; DiDeMo source-aware 1.0 sample/sec and max chunk pooling | Released HiREST SRTs; faster-whisper `large-v3-turbo` was not exercised |
@@ -48,14 +48,14 @@ legacy revision metadata after the fact.
## Multimodal comparison contract
Natural-language answer prose is not an official benchmark prediction format.
-Benchmark runs continue to preserve atomic scene and dialogue hits, raw
+Benchmark runs continue to preserve atomic scene and speech hits, raw
distances, and the existing dataset serializers. When a dataset contains both
eligible modalities, reports must show three fixed rows:
| Retrieval path | What is compared |
|---|---|
| Scene only | The existing visual retrieval output |
-| Dialogue only | The existing transcript retrieval output |
+| Speech only | The existing transcript retrieval output |
| Fixed RRF fusion | Overlap-connected intervals ranked with `rrf_v1`, `k=60` |
No fused benchmark score is reported until the same frozen dataset inputs and
@@ -159,7 +159,7 @@ result yet. LongVALE is the primary next experiment because it contains visual,
generic-audio, and spoken evidence in long videos. The work is ordered as follows:
1. Complete a bounded real-media FineLAP integration smoke and record resource use.
-2. Convert LongVALE event descriptions into visual, sound, and dialogue searches.
+2. Convert LongVALE event descriptions into visual, sound, and speech searches.
3. Combine those result lists using one fixed, provenance-preserving rule.
4. Return the single start/end range required by the official evaluator.
5. Process one of the nine evaluation archives to measure runtime, temporary
diff --git a/docs/benchmarking/runtime_validation.md b/docs/benchmarking/runtime_validation.md
index 49ce5c76..7895746a 100644
--- a/docs/benchmarking/runtime_validation.md
+++ b/docs/benchmarking/runtime_validation.md
@@ -91,9 +91,9 @@ selects the older installed TestPyPI package instead.
| Source Streamlit UI | Source selected through `PYTHONPATH`, real browser session | Page rendered with upload, index, status, and search controls; no import exception |
| Built-wheel UI | Fresh wheel installed without VidXP source on its import path; browser interface opened from the installed package | Page rendered successfully and the Streamlit health endpoint returned `ok` |
| Built-wheel CLI | Final wheel installed into an isolated target while using the validated dependency environment; wheel target placed first on `PYTHONPATH` | Import resolved inside the wheel target and `python -m vidxp --help` listed the expected commands |
-| Dependency doctor | `vidxp doctor --modalities dialogue,scene,actor` | ChromaDB, MiniLM, CLIP, NumPy, OpenCV, Pillow, PyTorch, face recognition, MoviePy, WhisperX, and FFmpeg imports resolved |
-| Released-transcript path | Real MiniLM encoding, Chroma writes, and top-2 dialogue search over three timestamped segments | Run completed; two hits returned; first interval was `[0.0, 2.0]`; full run/source metadata present |
-| Raw-video dialogue path | Five-second derived video with audio, real WhisperX `large-v2`, English alignment, MiniLM, and Chroma writes | Language detected as English; four dialogue phrases indexed; run state completed |
+| Dependency doctor | `vidxp doctor --modalities speech,scene,actor` | ChromaDB, MiniLM, CLIP, NumPy, OpenCV, Pillow, PyTorch, face recognition, MoviePy, WhisperX, and FFmpeg imports resolved |
+| Released-transcript path | Real MiniLM encoding, Chroma writes, and top-2 speech search over three timestamped segments | Run completed; two hits returned; first interval was `[0.0, 2.0]`; full run/source metadata present |
+| Raw-video speech path | Five-second derived video with audio, real WhisperX `large-v2`, English alignment, MiniLM, and Chroma writes | Language detected as English; four speech phrases indexed; run state completed |
| Scene-only path | Four-frame derived clip, real CLIP encoding and Chroma search | Four frames indexed; two hits returned; neither WhisperX nor face recognition loaded |
| Actor-only path | Same derived clip, real face detection/clustering | Four frames indexed; eight detections retained |
| Shared visual path | Same clip with scene and actor enabled | Four source frames advanced once; four scene and four actor frame operations recorded |
diff --git a/docs/benchmarking_research.md b/docs/benchmarking_research.md
index d960bd24..861e1fe9 100644
--- a/docs/benchmarking_research.md
+++ b/docs/benchmarking_research.md
@@ -110,7 +110,7 @@ VidXP currently returns a **point timestamp**, for which IoU is undefined. For a
- median and 90th-percentile temporal error;
- interval tIoU metrics only if VidXP is changed to return intervals.
-For word alignment as a component diagnostic, the WhisperX paper defines a true positive as an exact word match whose predicted and reference segments overlap within a 200 ms collar, and reports precision and recall ([Bain et al., 2023, §3.2](https://arxiv.org/abs/2303.00747)). This 200 ms collar is suitable for **word-alignment evaluation**, not automatically for user-facing scene/dialogue search; search tolerances should reflect the declared navigation use case.
+For word alignment as a component diagnostic, the WhisperX paper defines a true positive as an exact word match whose predicted and reference segments overlap within a 200 ms collar, and reports precision and recall ([Bain et al., 2023, §3.2](https://arxiv.org/abs/2303.00747)). This 200 ms collar is suitable for **word-alignment evaluation**, not automatically for user-facing scene/speech search; search tolerances should reflect the declared navigation use case.
## 3. Retrieval metrics
@@ -219,7 +219,7 @@ For indexing, report:
- total wall time and real-time factor \(RTF=\text{wall seconds}/\text{video seconds}\);
- processed video minutes per wall-clock minute and frames/s;
-- stage times: audio extraction, WhisperX transcription, alignment, dialogue embedding/write, video decode, CLIP embedding/write, face detection/encoding/clustering/write;
+- stage times: audio extraction, WhisperX transcription, alignment, speech embedding/write, video decode, CLIP embedding/write, face detection/encoding/clustering/write;
- final database/index bytes.
For queries, report separately:
diff --git a/docs/deployment/coolify.md b/docs/deployment/coolify.md
index 997d8380..d248508f 100644
--- a/docs/deployment/coolify.md
+++ b/docs/deployment/coolify.md
@@ -175,14 +175,14 @@ submit a model-preparation job through the authenticated API.
| Feature | Approximate download |
|---|---:|
-| Dialogue search | 2.64 GiB |
+| Speech search | 2.64 GiB |
| Scene search | 1.43 GiB |
| Action search | 0.93 GiB |
| Actor matching | 37 MiB |
The example below prepares every built-in search feature. In the shell running
the request, set `VIDXP_API_TOKEN` to the private API token configured above.
-The API name for action search is `videoprism`.
+The API capability for multi-frame action and motion search is `action`.
```bash
curl --fail-with-body \
@@ -191,7 +191,7 @@ curl --fail-with-body \
--header "Authorization: Bearer ${VIDXP_API_TOKEN}" \
--header "Idempotency-Key: initial-cpu-models-v1" \
--header "Content-Type: application/json" \
- --data '{"modalities":["dialogue","scene","videoprism","actor"],"capability_options":{}}'
+ --data '{"modalities":["speech","scene","action","actor"],"capability_options":{}}'
```
The `202 Accepted` response includes a `job_id`. Insert it into the wait
@@ -236,27 +236,29 @@ being loaded completely into memory.
## 7. Optional generated answers
Search and timestamped evidence work without a language model. To let VidXP
-generate written claims from that evidence, first evaluate a model's resource
-use, license, structured-output reliability, and grounding behavior.
-
-Then configure the selected model:
+plan searches and generate written claims from citable textual evidence,
+configure the internal Ollama address:
```dotenv
VIDXP_SLM_BASE_URL=http://ollama:11434/v1
-VIDXP_SLM_MODEL=
```
+VidXP uses the official `qwen3.5:4b-q4_K_M` Ollama build by default. Set
+`VIDXP_SLM_MODEL` only to make an intentional operator override.
+
Start Ollama and explicitly download the model:
```bash
docker compose --env-file .env -f compose.coolify.yaml --profile slm up -d ollama
docker compose --env-file .env -f compose.coolify.yaml --profile slm exec ollama \
- ollama pull
+ ollama pull qwen3.5:4b-q4_K_M
docker compose --env-file .env -f compose.coolify.yaml up -d worker
```
Do not publish Ollama through the proxy. Compose never downloads this model
-automatically.
+automatically. The Q4_K_M artifact is approximately 3.4 GB and local inference
+has no per-request API charge, but it consumes the server's storage, memory,
+compute time, and electricity.
## Back up and upgrade
diff --git a/docs/deployment/gpu-evaluation.md b/docs/deployment/gpu-evaluation.md
index c0e5f199..759644af 100644
--- a/docs/deployment/gpu-evaluation.md
+++ b/docs/deployment/gpu-evaluation.md
@@ -53,8 +53,8 @@ URL.
| Capability operation | Device policy | Precision gate |
| --- | --- | --- |
| Scene SigLIP2 embedding/search | `cuda:` | Start with the current float32 contract; evaluate lower precision separately |
-| Dialogue Qwen3 embedding/search | `cuda:` | Use bfloat16 only when `torch.cuda.is_bf16_supported()` passes; otherwise use an explicitly tested fallback |
-| Dialogue transcription | CUDA device and index through faster-whisper | float16 |
+| Speech Qwen3 embedding/search | `cuda:` | Use bfloat16 only when `torch.cuda.is_bf16_supported()` passes; otherwise use an explicitly tested fallback |
+| Speech transcription | CUDA device and index through faster-whisper | float16 |
| Actor detection/recognition and overlays | CPU | float32 |
| Media import and snippet extraction | CPU | Not applicable |
| SLM query planning/synthesis | External Ollama endpoint or deterministic fallback | Owned by the Ollama deployment |
@@ -114,7 +114,7 @@ The release gate is correctness and failure behavior:
- start on a clean NVIDIA host and prove explicit device isolation;
- run dependency/readiness checks without model downloads;
- with already prepared model assets, smoke one scene index/search and one
- dialogue transcription/search;
+ speech transcription/search;
- verify actor processing remains on CPU;
- compare CPU and CUDA result shape, provenance, and retrieval tolerances;
- translate PyTorch OOM into a typed resource-limit failure;
diff --git a/docs/integrations/openai-plugin.md b/docs/integrations/openai-plugin.md
index 784a9108..a45d8406 100644
--- a/docs/integrations/openai-plugin.md
+++ b/docs/integrations/openai-plugin.md
@@ -43,8 +43,10 @@ connecting an assistant.
4. Start a new Codex task.
Desktop installs the VidXP plugin and registers the selected installation's
-exact `vidxp-mcp` executable, repository, and data paths. It does not depend on
-that executable being available on the shell's `PATH`.
+exact `vidxp-mcp` executable, repository, data paths, and any Desktop-managed
+local grounded-answer settings. It does not depend on that executable being
+available on the shell's `PATH`, and a later Codex-launched stdio server does
+not have to inherit environment variables from the Desktop process.
Select **Copy MCP setup** instead when another compatible local assistant needs
the MCP connection settings without the Codex plugin.
diff --git a/docs/integrations/premiere-pro.md b/docs/integrations/premiere-pro.md
index 4acb48a1..7648ebfc 100644
--- a/docs/integrations/premiere-pro.md
+++ b/docs/integrations/premiere-pro.md
@@ -2,7 +2,7 @@
The VidXP Premiere Pro extension lets an editor select clips or bins from the
open project, index their existing source files, and search the resulting
-library without leaving Premiere. It discovers dialogue, sound, scene, actor,
+library without leaving Premiere. It discovers speech, sound, scene, actor,
and future search features from the connected VidXP runtime instead of keeping
a fixed capability list in the extension.
@@ -70,15 +70,23 @@ Offline clips, sequences, generated items without a media path, and duplicate
source paths are not submitted. Large selections are split into durable VidXP
ingestion sessions.
-## Search indexed moments
+## Ask about indexed media
-Enter a description, choose one indexed video or the complete active library,
-and select any searchable features reported by VidXP. Results show the source
+Enter a question or description, choose one indexed video or the complete
+active library, and select any searchable features reported by VidXP. The
+extension uses VidXP's shared grounded-query workflow and shows each generated
+statement with numbered supporting evidence. Results also retain the source
video, time range, contributing features, and fused score.
+Enable **Local grounded answers** during Desktop setup to generate cited answers
+with the approved local model. If that model is unavailable or cannot return a
+valid cited response, the same workflow falls back to ranked indexed evidence
+instead of inventing an answer. This fallback does not prevent ordinary scene,
+action, sound, or speech retrieval.
+
Timeline navigation, Source Monitor actions, marker creation, and snippet
insertion remain future host-adapter operations. They can be added without
-changing the VidXP client or shared search workflow.
+changing the VidXP client or shared grounded-query workflow.
## Current release limits
diff --git a/docs/local-api.md b/docs/local-api.md
index b48aed5f..d74d5703 100644
--- a/docs/local-api.md
+++ b/docs/local-api.md
@@ -84,6 +84,61 @@ The generated configuration runs `vidxp-mcp` as a local process. See
[Connect VidXP to Codex or ChatGPT](integrations/openai-plugin.md) for Codex
setup and for the different requirements of hosted AI clients.
+### Enable local grounded answers
+
+Ordinary search and timestamped evidence do not require a language model.
+`query_video` and `vidxp query` can additionally use a self-hosted Ollama model
+to rewrite a question into searches and draft claims from citable textual
+evidence. VidXP falls back to deterministic evidence retrieval when Ollama is
+not configured or unavailable.
+
+For VidXP Desktop, open **Setup options** and enable **Local grounded
+answers**. Desktop performs the Ollama health check, asks before installing a
+supported system package, downloads the approved model with visible progress,
+and carries the non-secret local provider settings into every managed surface,
+including copied stdio MCP JSON and **Set up in Codex**. If Desktop starts
+`ollama serve`, it supervises and stops only that owned process. It never stops
+an Ollama app or service that was already running.
+
+The commands below are only for command-line installations and custom
+deployments.
+
+Install and start [Ollama](https://ollama.com/download), then explicitly
+download VidXP's recommended model:
+
+```bash
+ollama pull qwen3.5:4b-q4_K_M
+```
+
+Set the Ollama OpenAI-compatible address before starting `vidxp-mcp`,
+`vidxp-api`, or a CLI query. VidXP selects `qwen3.5:4b-q4_K_M` when the model
+setting is omitted:
+
+```bash
+export VIDXP_SLM_BASE_URL=http://127.0.0.1:11434/v1
+vidxp query "When does the taxi arrive?"
+```
+
+In PowerShell, set the same value with:
+
+```powershell
+$env:VIDXP_SLM_BASE_URL = "http://127.0.0.1:11434/v1"
+vidxp query "When does the taxi arrive?"
+```
+
+The official Q4_K_M model download is approximately 3.4 GB. It runs locally,
+so there is no model API fee or numbered hosted-model run; it still uses local
+storage, memory, compute time, and electricity. Desktop downloads it only when
+the user selects the feature; CLI users pull it explicitly. VidXP never bundles
+the model with the Python or Desktop packages. A reused external Ollama service
+continues to own its model storage; VidXP does not claim those files are in its
+search-model cache.
+
+The current query adapter sends structured search evidence, not video or audio
+bytes, to Qwen. Speech transcripts can support generated factual claims.
+Scene, action, and sound matches remain timestamped, inspectable retrieval
+evidence until a later media-enrichment layer supplies citable descriptions.
+
## Add videos through MCP
The available method depends on how the MCP client connects.
@@ -115,8 +170,8 @@ features.
Use the `sound` modality to index or search music, environmental sounds, and
other non-speech audio events. CLI, HTTP, local stdio MCP, remote MCP, browser,
and Desktop all resolve that name through the same capability contract. Spoken
-words remain a separate `dialogue` modality, while multi-frame visible actions
-use `videoprism`.
+words use the `speech` capability, while multi-frame visible actions and motion
+use `action`.
## Connect from another computer
diff --git a/plugins/vidxp/skills/vidxp-ingest-video/SKILL.md b/plugins/vidxp/skills/vidxp-ingest-video/SKILL.md
index 3a45354b..743d167c 100644
--- a/plugins/vidxp/skills/vidxp-ingest-video/SKILL.md
+++ b/plugins/vidxp/skills/vidxp-ingest-video/SKILL.md
@@ -9,8 +9,8 @@ description: Use VidXP to upload, import, register, and automatically index vide
1. Resolve the `vidxp` MCP tools and call `get_workspace`. Do not import a video
that is already registered or indexed.
-2. Choose indexable modalities from the workspace. Use `dialogue` and `scene`
- for ordinary content retrieval. Add `videoprism` when the request depends on
+2. Choose indexable capabilities from the workspace. Use `speech` and `scene`
+ for ordinary content retrieval. Add `action` when the request depends on
actions or events spanning multiple frames. Add `actor` only when anonymous
recurring-face clusters are wanted; it does not identify people by name.
3. Call `get_runtime_readiness`. If selected models are missing, submit
diff --git a/premiere/README.md b/premiere/README.md
index c782d80c..1a5bf295 100644
--- a/premiere/README.md
+++ b/premiere/README.md
@@ -19,7 +19,9 @@ Shared React workflow (`src/ui`)
The UI receives a `PremiereAdapter`; it does not import a host API. The UXP
adapter alone imports Premiere's `premierepro` module. The CEP adapter alone
uses `evalScript`, and its ExtendScript bridge returns bounded JSON. Indexing,
-polling, search, status, capability discovery, and selection rules are shared.
+polling, grounded queries, status, capability discovery, and selection rules
+are shared. Grounded queries use the public durable job contract and preserve
+ranked moments when answer generation falls back to evidence-only mode.
UXP renders Adobe's built-in Spectrum widgets through the typed control
wrapper. CEP renders native HTML controls through that same wrapper because
diff --git a/premiere/docs/MANUAL_TEST_CHECKLIST.md b/premiere/docs/MANUAL_TEST_CHECKLIST.md
index c4d7de6b..36e2a166 100644
--- a/premiere/docs/MANUAL_TEST_CHECKLIST.md
+++ b/premiere/docs/MANUAL_TEST_CHECKLIST.md
@@ -72,14 +72,19 @@ newer before claiming host support.
- Close and reopen the panel during a job. Record the current POC behavior; job
recovery across panel reload is not yet implemented.
-## Search
+## Grounded queries
-- Search all indexed media with each searchable capability individually and in
- combination.
-- Search one selected VidXP media item and confirm every result belongs to it.
-- Confirm each result shows the correct source name, time range, capability
- labels, rank, and score.
-- Search for no-match text, stop the worker, and use an unprepared capability;
+- Ask about all indexed media with each searchable capability individually and
+ in combination.
+- Ask about one selected VidXP media item and confirm every evidence item and
+ ranked moment belongs to it.
+- With **Local grounded answers** enabled, confirm generated statements show
+ numbered citations that resolve to the displayed source name, time range,
+ capability, and evidence text.
+- Stop the local answer model and confirm the panel labels the result as
+ evidence-only while retaining ranked moments, capability labels, ranks, and
+ scores.
+- Ask a no-match question, stop the worker, and use an unprepared capability;
confirm empty, unavailable, and model-remediation states are readable.
## Safety and cleanup
diff --git a/premiere/src/services/vidxp/client.ts b/premiere/src/services/vidxp/client.ts
index bbcdc454..8e765cbf 100644
--- a/premiere/src/services/vidxp/client.ts
+++ b/premiere/src/services/vidxp/client.ts
@@ -1,6 +1,7 @@
import type {
CapabilitySummary,
MediaIngestionStatus,
+ QueryAnswer,
VidXPJob,
WorkspaceOverview,
} from "./types";
@@ -15,8 +16,8 @@ export interface VidXPClientOptions {
sleep?: Sleep;
}
-export interface SearchRequest {
- query: string;
+export interface QueryRequest {
+ question: string;
modalities: string[];
mediaId?: string;
topK?: number;
@@ -101,14 +102,17 @@ export class VidXPClient {
return status;
}
- submitSearch(request: SearchRequest, idempotencyKey: string): Promise {
- const query = request.query.trim();
- if (!query) throw new Error("Enter something to search for.");
- return this.request("/api/v1/jobs/search", {
+ submitQuery(
+ request: QueryRequest,
+ idempotencyKey: string,
+ ): Promise> {
+ const question = request.question.trim();
+ if (!question) throw new Error("Enter a question or description.");
+ return this.request>("/api/v1/jobs/query", {
method: "POST",
headers: { "Idempotency-Key": idempotencyKey },
body: JSON.stringify({
- query,
+ question,
modalities: request.modalities,
media_id: request.mediaId || null,
top_k: request.topK ?? 20,
@@ -116,20 +120,22 @@ export class VidXPClient {
});
}
- getJob(jobId: string): Promise {
- return this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}`);
+ getJob(jobId: string): Promise> {
+ return this.request>(
+ `/api/v1/jobs/${encodeURIComponent(jobId)}`,
+ );
}
- async waitForJob(
- initial: VidXPJob,
- onProgress: (job: VidXPJob) => void,
+ async waitForJob(
+ initial: VidXPJob,
+ onProgress: (job: VidXPJob) => void,
signal?: AbortSignal,
- ): Promise {
+ ): Promise> {
let job = initial;
while (!job.terminal) {
onProgress(job);
await this.wait(job.poll_after_seconds, signal);
- job = await this.getJob(job.job_id);
+ job = await this.getJob(job.job_id);
}
onProgress(job);
if (job.state !== "succeeded") {
diff --git a/premiere/src/services/vidxp/types.ts b/premiere/src/services/vidxp/types.ts
index e85a39a2..edda01f6 100644
--- a/premiere/src/services/vidxp/types.ts
+++ b/premiere/src/services/vidxp/types.ts
@@ -97,14 +97,56 @@ export interface FusedSearchResult {
moments: FusedMoment[];
}
-export interface VidXPJob {
+export interface GroundedClaim {
+ text: string;
+ evidence_ids: string[];
+}
+
+export interface QueryModelIdentity {
+ provider: "ollama";
+ model: string;
+}
+
+export interface MomentEvidence {
+ kind: "moment";
+ evidence_id: string;
+ media_id: string;
+ modality: string;
+ start: number;
+ end: number;
+ display_text?: string;
+}
+
+export interface ActorEvidence {
+ kind: "actor";
+ evidence_id: string;
+ media_id: string;
+ modality: "actor";
+ start: number;
+ end: number;
+ display_text: string;
+}
+
+export type QueryEvidence = MomentEvidence | ActorEvidence;
+
+export interface QueryAnswer {
+ question: string;
+ mode: "generated" | "evidence_only" | "no_evidence";
+ model?: QueryModelIdentity;
+ claims: GroundedClaim[];
+ evidence: QueryEvidence[];
+ moments: FusedMoment[];
+ fallback_reason?: string;
+}
+
+export interface VidXPJob {
job_id: string;
kind: string;
state: string;
progress?: JobProgress;
result?: {
kind: string;
- result: FusedSearchResult;
+ result: TResult;
};
error?: ErrorDetail;
terminal: boolean;
diff --git a/premiere/src/ui/App.tsx b/premiere/src/ui/App.tsx
index 27fe6e64..140a5cd2 100644
--- a/premiere/src/ui/App.tsx
+++ b/premiere/src/ui/App.tsx
@@ -19,6 +19,8 @@ import {
import type {
CapabilitySummary,
FusedMoment,
+ QueryAnswer,
+ QueryEvidence,
WorkspaceOverview,
} from "../services/vidxp/types";
import {
@@ -73,6 +75,7 @@ export function App({ fetchImpl, premiere }: AppProps) {
const [notice, setNotice] = useState();
const [query, setQuery] = useState("");
const [mediaScope, setMediaScope] = useState("");
+ const [answer, setAnswer] = useState();
const [moments, setMoments] = useState([]);
const loadLibrary = useCallback(async () => {
@@ -207,10 +210,12 @@ export function App({ fetchImpl, premiere }: AppProps) {
if (!client || !query.trim()) return;
const controller = beginOperation();
setNotice(undefined);
+ setAnswer(undefined);
+ setMoments([]);
try {
- const initial = await client.submitSearch(
+ const initial = await client.submitQuery(
{
- query,
+ question: query,
modalities: searchModalities,
mediaId: mediaScope || undefined,
topK: 20,
@@ -228,18 +233,26 @@ export function App({ fetchImpl, premiere }: AppProps) {
controller.signal,
);
const result = completed.result?.result;
- if (!result) throw new Error("VidXP completed the search without a result payload.");
+ if (!result) throw new Error("VidXP completed the question without a result payload.");
+ setAnswer(result);
setMoments(result.moments);
- if (result.moments.length === 0) {
+ if (result.mode === "no_evidence") {
setNotice({
tone: "warning",
- title: "No matching moments",
- message: "Try a broader description, another search feature, or the complete indexed library.",
+ title: "No matching evidence",
+ message: "Try a broader question, another search feature, or the complete indexed library.",
+ });
+ } else if (result.mode === "evidence_only") {
+ setNotice({
+ tone: "warning",
+ title: "Showing evidence without a generated answer",
+ message: "VidXP ranked the supporting moments, but the local answer model was unavailable or could not produce a valid cited response.",
});
}
setOperation({ status: "idle" });
} catch (error) {
if (!controller.signal.aborted) {
+ setAnswer(undefined);
setMoments([]);
setOperation({ status: "error", message: messageOf(error) });
}
@@ -405,15 +418,15 @@ export function App({ fetchImpl, premiere }: AppProps) {
-
Search moments
-
Search one indexed video or the complete active VidXP library.
+
Ask indexed media
+
Get a cited answer and its supporting moments from one video or the complete library.
@@ -446,7 +459,7 @@ export function App({ fetchImpl, premiere }: AppProps) {
}
onPress={() => void search()}
>
- {operation.status === "searching" ? "Searching…" : "Search indexed media"}
+ {operation.status === "searching" ? "Answering…" : "Ask VidXP"}
@@ -481,11 +494,87 @@ export function App({ fetchImpl, premiere }: AppProps) {
)}
+
);
}
+export function GroundedAnswer({
+ answer,
+ workspace,
+}: {
+ answer?: QueryAnswer;
+ workspace?: WorkspaceOverview;
+}) {
+ if (!answer || answer.mode === "no_evidence") return null;
+ const names = mediaNames(workspace);
+ const evidenceNumbers = new Map(
+ answer.evidence.map((evidence, index) => [evidence.evidence_id, index + 1]),
+ );
+ return (
+
+
+
+
{answer.mode === "generated" ? "Grounded answer" : "Retrieved evidence"}
+
+ {answer.mode === "generated" && answer.model
+ ? `Generated locally with ${answer.model.model}; every statement links to indexed evidence.`
+ : "VidXP returned ranked evidence without generating an answer."}
+
+
+
+ {answer.claims.length > 0 && (
+
+ {answer.claims.map((claim, index) => (
+
+ {claim.text}
+
+ {claim.evidence_ids.map((evidenceId) => (
+
+ [{evidenceNumbers.get(evidenceId) ?? "?"}]
+
+ ))}
+
+
+ ))}
+
+ )}
+
+ {answer.evidence.map((evidence, index) => (
+
+ ))}
+
+
+ );
+}
+
+function EvidenceItem({
+ evidence,
+ number,
+ mediaName,
+}: {
+ evidence: QueryEvidence;
+ number: number;
+ mediaName: string;
+}) {
+ return (
+
+ [{number}]
+
+
{mediaName}
+
{formatTime(evidence.start)} – {formatTime(evidence.end)} · {evidence.modality}
+ {evidence.display_text &&
{evidence.display_text}
}
+
+
+ );
+}
+
function ConnectionBadge({ state }: { state: ConnectionState }) {
const label =
state.status === "ready"
@@ -605,9 +694,7 @@ function SearchResults({
workspace?: WorkspaceOverview;
}) {
if (moments.length === 0) return null;
- const names = new Map(
- workspace?.media.map((media) => [media.media_id, media.original_filename]) ?? [],
- );
+ const names = mediaNames(workspace);
return (
@@ -635,6 +722,12 @@ function SearchResults({
);
}
+function mediaNames(workspace?: WorkspaceOverview): Map
{
+ return new Map(
+ workspace?.media.map((media) => [media.media_id, media.original_filename]) ?? [],
+ );
+}
+
function formatTime(seconds: number): string {
const whole = Math.max(0, Math.floor(seconds));
const hours = Math.floor(whole / 3600);
diff --git a/premiere/src/ui/styles.css b/premiere/src/ui/styles.css
index 987d5288..f6202374 100644
--- a/premiere/src/ui/styles.css
+++ b/premiere/src/ui/styles.css
@@ -424,6 +424,72 @@ sp-checkbox {
line-height: 1.4;
}
+.claims,
+.evidence-list {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.claims li {
+ color: var(--text);
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+.citations {
+ display: inline-flex;
+ gap: 3px;
+ margin-left: 5px;
+ color: var(--accent-bright);
+ font-size: 9px;
+ font-weight: 700;
+}
+
+.evidence-list {
+ padding-top: 8px;
+ border-top: 1px solid var(--border);
+}
+
+.evidence-list li {
+ display: flex;
+ align-items: flex-start;
+ gap: 7px;
+}
+
+.evidence-list li > div {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.evidence-list strong {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: 10px;
+}
+
+.evidence-list li > div > span,
+.evidence-list p {
+ margin: 0;
+ color: var(--muted);
+ font-size: 9px;
+ line-height: 1.4;
+}
+
+.evidence-number {
+ flex: none;
+ color: var(--accent-bright);
+ font-size: 9px;
+ font-weight: 700;
+}
+
.results ol {
display: flex;
flex-direction: column;
diff --git a/premiere/tests/app-query.test.tsx b/premiere/tests/app-query.test.tsx
new file mode 100644
index 00000000..ca77e31f
--- /dev/null
+++ b/premiere/tests/app-query.test.tsx
@@ -0,0 +1,58 @@
+import { renderToStaticMarkup } from "react-dom/server";
+import { describe, expect, it } from "vitest";
+
+import type { QueryAnswer } from "../src/services/vidxp/types";
+import { GroundedAnswer } from "../src/ui/App";
+
+const evidence = {
+ kind: "moment" as const,
+ evidence_id: "evidence-1",
+ media_id: "media-1",
+ modality: "sound",
+ start: 12,
+ end: 15,
+ display_text: "A door slams.",
+};
+
+describe("Premiere grounded answers", () => {
+ it("renders generated claims with numbered supporting evidence", () => {
+ const answer: QueryAnswer = {
+ question: "What happens after the door slams?",
+ mode: "generated",
+ model: { provider: "ollama", model: "qwen3.5:4b-q4_K_M" },
+ claims: [
+ {
+ text: "Someone says they need to leave.",
+ evidence_ids: ["evidence-1"],
+ },
+ ],
+ evidence: [evidence],
+ moments: [],
+ };
+
+ const markup = renderToStaticMarkup( );
+
+ expect(markup).toContain("Grounded answer");
+ expect(markup).toContain("Someone says they need to leave.");
+ expect(markup).toContain("[1]");
+ expect(markup).toContain("A door slams.");
+ expect(markup).toContain("qwen3.5:4b-q4_K_M");
+ });
+
+ it("labels deterministic fallback results as evidence rather than an answer", () => {
+ const answer: QueryAnswer = {
+ question: "What happens after the door slams?",
+ mode: "evidence_only",
+ claims: [],
+ evidence: [evidence],
+ moments: [],
+ fallback_reason: "provider_unavailable",
+ };
+
+ const markup = renderToStaticMarkup( );
+
+ expect(markup).toContain("Retrieved evidence");
+ expect(markup).toContain("without generating an answer");
+ expect(markup).not.toContain("Grounded answer");
+ });
+});
diff --git a/premiere/tests/vidxp-client.test.ts b/premiere/tests/vidxp-client.test.ts
index 0f8247c9..55c8828b 100644
--- a/premiere/tests/vidxp-client.test.ts
+++ b/premiere/tests/vidxp-client.test.ts
@@ -5,7 +5,11 @@ import {
VidXPClient,
type VidXPFetch,
} from "../src/services/vidxp/client";
-import type { MediaIngestionStatus, VidXPJob } from "../src/services/vidxp/types";
+import type {
+ MediaIngestionStatus,
+ QueryAnswer,
+ VidXPJob,
+} from "../src/services/vidxp/types";
const ingestion: MediaIngestionStatus = {
session_id: "ingestion-1",
@@ -51,19 +55,20 @@ describe("VidXPClient", () => {
});
});
- it("polls a search job and returns its typed result", async () => {
- const completed: VidXPJob = {
+ it("polls a grounded query job and returns its typed result", async () => {
+ const completed: VidXPJob = {
job_id: "job-1",
- kind: "search",
+ kind: "query",
state: "succeeded",
terminal: true,
poll_after_seconds: 0,
result: {
- kind: "search",
+ kind: "query",
result: {
- query_id: "query-1",
- query: "door opens",
- modalities: ["scene"],
+ question: "What happens after the door opens?",
+ mode: "evidence_only",
+ claims: [],
+ evidence: [],
moments: [],
},
},
@@ -75,9 +80,9 @@ describe("VidXPClient", () => {
fetchImpl: fetchImpl as VidXPFetch,
sleep,
});
- const queued: VidXPJob = {
+ const queued: VidXPJob = {
job_id: "job-1",
- kind: "search",
+ kind: "query",
state: "queued",
terminal: false,
poll_after_seconds: 1,
@@ -86,7 +91,45 @@ describe("VidXPClient", () => {
const result = await client.waitForJob(queued, vi.fn());
expect(sleep).toHaveBeenCalledWith(1000);
- expect(result.result?.result.query_id).toBe("query-1");
+ expect(result.result?.result.question).toBe("What happens after the door opens?");
+ });
+
+ it("submits Premiere questions to the grounded query contract", async () => {
+ const queued: VidXPJob = {
+ job_id: "query-job-1",
+ kind: "query",
+ state: "queued",
+ terminal: false,
+ poll_after_seconds: 1,
+ };
+ const fetchImpl = vi.fn().mockResolvedValue(jsonResponse(queued, 202));
+ const client = new VidXPClient({
+ baseUrl: "http://127.0.0.1:32191",
+ fetchImpl: fetchImpl as VidXPFetch,
+ });
+
+ await client.submitQuery(
+ {
+ question: " What happens after the door slams? ",
+ modalities: ["scene", "sound", "speech"],
+ mediaId: "media-1",
+ topK: 20,
+ },
+ "query-request-key",
+ );
+
+ const [url, init] = fetchImpl.mock.calls[0] as [string, RequestInit];
+ const headers = new Headers(init.headers);
+ expect(url).toBe("http://127.0.0.1:32191/api/v1/jobs/query");
+ expect(init.method).toBe("POST");
+ expect(headers.get("Idempotency-Key")).toBe("query-request-key");
+ if (typeof init.body !== "string") throw new Error("Expected a JSON request body.");
+ expect(JSON.parse(init.body)).toEqual({
+ question: "What happens after the door slams?",
+ modalities: ["scene", "sound", "speech"],
+ media_id: "media-1",
+ top_k: 20,
+ });
});
it("surfaces safe API remediation without exposing the bearer token", async () => {
diff --git a/premiere/vite.config.ts b/premiere/vite.config.ts
index 2905d15e..6e8b9aba 100644
--- a/premiere/vite.config.ts
+++ b/premiere/vite.config.ts
@@ -54,7 +54,7 @@ export default defineConfig(({ mode }) => {
},
test: {
environment: "node",
- include: ["tests/**/*.test.ts"],
+ include: ["tests/**/*.test.{ts,tsx}"],
},
};
});
diff --git a/pyproject.toml b/pyproject.toml
index 78ce7670..9ee89112 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ authors = [
{ name = "Saad Bazaz" },
]
-description = "VidXP - Search video by dialogue, sound, scenes, actions, and actors"
+description = "VidXP - Search video by speech, sound, scenes, actions, and actors"
readme = "README.md"
requires-python = ">=3.11,<3.15"
license = "MIT"
@@ -83,9 +83,9 @@ vidxp = [
[tool.setuptools.dynamic.optional-dependencies]
storage = { file = ["src/vidxp/requirements/storage.txt"] }
-dialogue = { file = [
+speech = { file = [
"src/vidxp/requirements/storage.txt",
- "src/vidxp/capabilities/dialogue/requirements.txt",
+ "src/vidxp/capabilities/speech/requirements.txt",
] }
scene = { file = [
"src/vidxp/requirements/storage.txt",
@@ -95,9 +95,9 @@ actor = { file = [
"src/vidxp/requirements/storage.txt",
"src/vidxp/capabilities/actor/requirements.txt",
] }
-videoprism = { file = [
+action = { file = [
"src/vidxp/requirements/storage.txt",
- "src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/action/requirements.txt",
] }
sound = { file = [
"src/vidxp/requirements/storage.txt",
@@ -105,19 +105,19 @@ sound = { file = [
] }
all = { file = [
"src/vidxp/requirements/storage.txt",
- "src/vidxp/capabilities/dialogue/requirements.txt",
+ "src/vidxp/capabilities/speech/requirements.txt",
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
- "src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/action/requirements.txt",
"src/vidxp/capabilities/sound/requirements.txt",
] }
local-worker = { file = [
"src/vidxp/requirements/storage.txt",
"src/vidxp/requirements/slm.txt",
- "src/vidxp/capabilities/dialogue/requirements.txt",
+ "src/vidxp/capabilities/speech/requirements.txt",
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
- "src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/action/requirements.txt",
"src/vidxp/capabilities/sound/requirements.txt",
] }
mcp = { file = ["src/vidxp/requirements/mcp.txt"] }
@@ -130,10 +130,10 @@ server-worker = { file = [
"src/vidxp/requirements/server.txt",
"src/vidxp/requirements/server-storage.txt",
"src/vidxp/requirements/slm.txt",
- "src/vidxp/capabilities/dialogue/requirements.txt",
+ "src/vidxp/capabilities/speech/requirements.txt",
"src/vidxp/capabilities/scene/requirements.txt",
"src/vidxp/capabilities/actor/requirements.txt",
- "src/vidxp/capabilities/videoprism/requirements.txt",
+ "src/vidxp/capabilities/action/requirements.txt",
"src/vidxp/capabilities/sound/requirements.txt",
] }
test = { file = ["src/vidxp/requirements/test.txt"] }
diff --git a/src/vidxp/benchmarks/agent_ablation_score.py b/src/vidxp/benchmarks/agent_ablation_score.py
new file mode 100644
index 00000000..c22bd31f
--- /dev/null
+++ b/src/vidxp/benchmarks/agent_ablation_score.py
@@ -0,0 +1,168 @@
+from __future__ import annotations
+
+import json
+import re
+from collections.abc import Mapping
+from typing import Any
+
+
+VIDXP_TOOL_NAMES = frozenset(
+ {
+ "get_workspace",
+ "list_capabilities",
+ "get_capability",
+ "get_runtime_readiness",
+ "list_media",
+ "get_media",
+ "get_index_status",
+ "search_moments",
+ "query_video",
+ "get_job",
+ "wait_job",
+ "get_job_evidence",
+ "create_clip",
+ "create_keyframe",
+ }
+)
+_VIDXP_COMMAND = re.compile(
+ r"(?:^|[\s'\"/\\])vidxp(?:-mcp)?(?:\.exe)?(?:\s|$)",
+ re.IGNORECASE,
+)
+
+
+def interval_iou(
+ predicted_start: float,
+ predicted_end: float,
+ expected_start: float,
+ expected_end: float,
+) -> float:
+ """Return temporal intersection over union for two valid intervals."""
+
+ intersection = max(
+ 0.0,
+ min(predicted_end, expected_end) - max(predicted_start, expected_start),
+ )
+ union = max(predicted_end, expected_end) - min(
+ predicted_start, expected_start
+ )
+ return 0.0 if union <= 0 else intersection / union
+
+
+def score_temporal_grounding(
+ output: str,
+ context: Mapping[str, Any],
+) -> dict[str, Any]:
+ """Score the single predicted interval using LongVALE grounding metrics."""
+
+ variables = context.get("vars", {})
+ try:
+ result = json.loads(output)
+ except (TypeError, json.JSONDecodeError) as exc:
+ return _failed(f"Output is not valid JSON: {exc}")
+ if not isinstance(result, dict):
+ return _failed("Output must be a JSON object.")
+ if result.get("video_id") != variables.get("video_id"):
+ return _failed("The returned video_id does not match the task.")
+
+ start = _finite_number(result.get("start_seconds"))
+ end = _finite_number(result.get("end_seconds"))
+ duration = _finite_number(variables.get("duration_seconds"))
+ expected_start = _finite_number(variables.get("expected_start"))
+ expected_end = _finite_number(variables.get("expected_end"))
+ if None in (start, end, duration, expected_start, expected_end):
+ return _failed("The result or task has a missing/non-numeric interval.")
+ assert start is not None
+ assert end is not None
+ assert duration is not None
+ assert expected_start is not None
+ assert expected_end is not None
+ if start < 0 or end <= start or end > duration + 0.001:
+ return _failed("The predicted interval is outside the video bounds.")
+
+ iou = interval_iou(start, end, expected_start, expected_end)
+ scores = {
+ "valid_interval": 1.0,
+ "temporal_iou": iou,
+ "r1_tiou_0_3": float(iou >= 0.3),
+ "r1_tiou_0_5": float(iou >= 0.5),
+ "r1_tiou_0_7": float(iou >= 0.7),
+ }
+ return {
+ "pass": iou >= 0.3,
+ "score": iou,
+ "reason": f"Temporal IoU is {iou:.4f}.",
+ "namedScores": scores,
+ }
+
+
+def score_ablation_boundary(
+ _output: str,
+ context: Mapping[str, Any],
+) -> dict[str, Any]:
+ """Prove MCP-on used VidXP and MCP-off did not bypass the condition."""
+
+ variables = context.get("vars", {})
+ expected_mcp = variables.get("expected_mcp") is True
+ trace = context.get("trace")
+ spans = trace.get("spans", []) if isinstance(trace, Mapping) else []
+ if not spans:
+ return _failed("No trace spans were captured; isolation is unproven.")
+
+ used_tools: set[str] = set()
+ invoked_vidxp_command = False
+ for span in spans:
+ if not isinstance(span, Mapping):
+ continue
+ attributes = span.get("attributes")
+ if not isinstance(attributes, Mapping):
+ attributes = {}
+ candidates = [
+ span.get("name"),
+ attributes.get("tool.name"),
+ attributes.get("gen_ai.tool.name"),
+ attributes.get("ai.toolCall.name"),
+ attributes.get("mcp.tool.name"),
+ ]
+ for candidate in candidates:
+ if isinstance(candidate, str) and _is_vidxp_tool(candidate):
+ used_tools.add(candidate)
+ for key, value in attributes.items():
+ if "command" not in str(key).casefold():
+ continue
+ text = value if isinstance(value, str) else json.dumps(value)
+ if _VIDXP_COMMAND.search(text):
+ invoked_vidxp_command = True
+
+ if invoked_vidxp_command:
+ return _failed(
+ "The agent invoked VidXP through the shell and bypassed the condition."
+ )
+ used_mcp = bool(used_tools)
+ passed = used_mcp is expected_mcp
+ expected = "at least one VidXP MCP call" if expected_mcp else "no VidXP MCP call"
+ observed = ", ".join(sorted(used_tools)) if used_tools else "none"
+ return {
+ "pass": passed,
+ "score": float(passed),
+ "reason": f"Expected {expected}; observed {observed}.",
+ "namedScores": {"ablation_boundary": float(passed)},
+ }
+
+
+def _is_vidxp_tool(value: str) -> bool:
+ normalized = value.casefold().replace("-", "_")
+ return "mcp__vidxp__" in normalized or any(
+ normalized == name or normalized.endswith(f".{name}")
+ for name in VIDXP_TOOL_NAMES
+ )
+
+
+def _finite_number(value: Any) -> float | None:
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
+ return None
+ number = float(value)
+ return number if number == number and abs(number) != float("inf") else None
+
+
+def _failed(reason: str) -> dict[str, Any]:
+ return {"pass": False, "score": 0.0, "reason": reason}
diff --git a/src/vidxp/benchmarks/agent_ablation_tests.py b/src/vidxp/benchmarks/agent_ablation_tests.py
new file mode 100644
index 00000000..0f267475
--- /dev/null
+++ b/src/vidxp/benchmarks/agent_ablation_tests.py
@@ -0,0 +1,124 @@
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Any
+
+
+_SCORER = "file://../../src/vidxp/benchmarks/agent_ablation_score.py"
+_MODALITIES = frozenset({"scene", "action", "sound", "speech"})
+
+
+def generate_tests(config: dict[str, Any] | None = None) -> list[dict[str, Any]]:
+ """Expand one task manifest into matched MCP-on and MCP-off cases."""
+
+ options = config or {}
+ manifest = Path(options.get("manifest", ""))
+ if not manifest.is_file():
+ raise ValueError(f"Agent-ablation task manifest was not found: {manifest}")
+ tasks = json.loads(manifest.read_text(encoding="utf-8"))
+ if not isinstance(tasks, list):
+ raise ValueError("The agent-ablation manifest must be a JSON array.")
+ if not tasks:
+ raise ValueError("The agent-ablation manifest must not be empty.")
+ providers = options.get("providers", {})
+ conditions = (
+ ("mcp-on", providers.get("mcp_on", "codex-vidxp-mcp"), True),
+ ("mcp-off", providers.get("mcp_off", "codex-no-mcp"), False),
+ )
+
+ generated: list[dict[str, Any]] = []
+ task_ids: set[str] = set()
+ for task in tasks:
+ _validate_task(task)
+ if task["id"] in task_ids:
+ raise ValueError(f"Duplicate agent-ablation task ID: {task['id']}")
+ task_ids.add(task["id"])
+ for condition, provider, expected_mcp in conditions:
+ variables = dict(task)
+ variables["condition"] = condition
+ variables["expected_mcp"] = expected_mcp
+ generated.append(
+ {
+ "description": f"{task['id']} [{condition}]",
+ "providers": [provider],
+ "vars": variables,
+ "metadata": {
+ "dataset": task["dataset"],
+ "task_id": task["id"],
+ "condition": condition,
+ "modalities": task["modalities"],
+ },
+ "assert": [
+ {"type": "is-json"},
+ {
+ "type": "python",
+ "value": f"{_SCORER}:score_temporal_grounding",
+ "metric": "temporal_grounding",
+ },
+ {
+ "type": "python",
+ "value": f"{_SCORER}:score_ablation_boundary",
+ "metric": "ablation_boundary",
+ },
+ ],
+ }
+ )
+ return generated
+
+
+def _validate_task(task: Any) -> None:
+ required = {
+ "id",
+ "dataset",
+ "video_id",
+ "media_relpath",
+ "duration_seconds",
+ "event_index",
+ "query",
+ "expected_start",
+ "expected_end",
+ "modalities",
+ }
+ if not isinstance(task, dict):
+ raise ValueError("Every agent-ablation task must be an object.")
+ missing = sorted(required.difference(task))
+ if missing:
+ raise ValueError(
+ f"Agent-ablation task {task.get('id', '')} is missing: "
+ f"{', '.join(missing)}"
+ )
+ for key in ("id", "dataset", "video_id", "media_relpath", "query"):
+ if not isinstance(task[key], str) or not task[key].strip():
+ raise ValueError(f"Agent-ablation task field {key} must be text.")
+
+ duration = task["duration_seconds"]
+ start = task["expected_start"]
+ end = task["expected_end"]
+ if any(
+ isinstance(value, bool) or not isinstance(value, (int, float))
+ for value in (duration, start, end)
+ ):
+ raise ValueError("Agent-ablation durations and bounds must be numeric.")
+ if duration <= 0 or start < 0 or end <= start or end > duration + 0.001:
+ raise ValueError(f"Agent-ablation task {task['id']} has invalid bounds.")
+ if (
+ isinstance(task["event_index"], bool)
+ or not isinstance(task["event_index"], int)
+ or task["event_index"] < 0
+ ):
+ raise ValueError(
+ "Agent-ablation event_index must be a non-negative integer."
+ )
+
+ modalities = task["modalities"]
+ if (
+ not isinstance(modalities, list)
+ or not modalities
+ or any(
+ not isinstance(modality, str) or modality not in _MODALITIES
+ for modality in modalities
+ )
+ or len(modalities) != len(set(modalities))
+ ):
+ raise ValueError(f"Agent-ablation task {task['id']} has invalid modalities.")
diff --git a/src/vidxp/benchmarks/cli.py b/src/vidxp/benchmarks/cli.py
index 34fa5e2d..f6a30128 100644
--- a/src/vidxp/benchmarks/cli.py
+++ b/src/vidxp/benchmarks/cli.py
@@ -552,7 +552,7 @@ def hirest_command(
param_hint="--temporal-window-fraction",
)
_require_benchmark_dependencies(
- "dialogue",
+ "speech",
include_benchmark_extra=True,
)
state = state_from_context(ctx)
diff --git a/src/vidxp/benchmarks/hirest.py b/src/vidxp/benchmarks/hirest.py
index 15ece166..dbe97985 100644
--- a/src/vidxp/benchmarks/hirest.py
+++ b/src/vidxp/benchmarks/hirest.py
@@ -20,8 +20,8 @@
run_logged_evaluator,
verify_artifact,
)
-from vidxp.capabilities.dialogue.config import dialogue_config
-from vidxp.capabilities.dialogue.operations import search_dialogue
+from vidxp.capabilities.speech.config import speech_config
+from vidxp.capabilities.speech.operations import search_speech
from vidxp.capabilities.schemas import SearchHit
from vidxp.capabilities.registry import create_capability_registry
from vidxp.core.contracts import IndexConfig, VideoSource
@@ -177,7 +177,7 @@ def rank_interval(
"HiREST temporal window fraction must be between zero and one."
)
if not hits:
- raise ValueError("HiREST temporal ranking requires dialogue hits.")
+ raise ValueError("HiREST temporal ranking requires speech hits.")
second_count = max(1, math.ceil(duration))
hit_scores = [float(hit.score) for hit in hits]
@@ -335,7 +335,7 @@ def _generate_predictions(
predictions: dict[str, dict[str, dict[str, list[float]]]] = {}
for prompt, video in ordered_pairs:
media_id = benchmark_media_id("hirest", video)
- hits = search_dialogue(
+ hits = search_speech(
prompt,
config=config,
top_k=dialogue_counts[media_id],
@@ -445,7 +445,7 @@ def run_hirest(
dataset="hirest",
split=split,
run_id=run_id,
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
device=device,
output_root=output_root,
generation_id=benchmark_generation_id("hirest", split, run_id),
@@ -520,7 +520,7 @@ def run_hirest(
"prediction_format_validated": True,
"input_mode": "released_timestamped_asr",
"dialogue_words_per_phrase": (
- dialogue_config(config).words_per_phrase
+ speech_config(config).words_per_phrase
),
"segment_word_timestamps": (
"linear_interpolation_within_srt_cue"
diff --git a/src/vidxp/capabilities/action/__init__.py b/src/vidxp/capabilities/action/__init__.py
new file mode 100644
index 00000000..1359e6be
--- /dev/null
+++ b/src/vidxp/capabilities/action/__init__.py
@@ -0,0 +1 @@
+"""Action and motion search powered by VideoPrism."""
diff --git a/src/vidxp/capabilities/videoprism/config.py b/src/vidxp/capabilities/action/config.py
similarity index 82%
rename from src/vidxp/capabilities/videoprism/config.py
rename to src/vidxp/capabilities/action/config.py
index f415e2a9..97431dfa 100644
--- a/src/vidxp/capabilities/videoprism/config.py
+++ b/src/vidxp/capabilities/action/config.py
@@ -12,4 +12,4 @@ class VideoPrismConfig(CapabilityConfig):
def videoprism_config(config: IndexConfig) -> VideoPrismConfig:
- return VideoPrismConfig.model_validate(config.options_for("videoprism"))
+ return VideoPrismConfig.model_validate(config.options_for("action"))
diff --git a/src/vidxp/capabilities/videoprism/definition.py b/src/vidxp/capabilities/action/definition.py
similarity index 81%
rename from src/vidxp/capabilities/videoprism/definition.py
rename to src/vidxp/capabilities/action/definition.py
index 566d8823..535e54b7 100644
--- a/src/vidxp/capabilities/videoprism/definition.py
+++ b/src/vidxp/capabilities/action/definition.py
@@ -12,11 +12,11 @@
module_import_check,
)
from vidxp.capabilities.schemas import SearchInput, SearchResult
-from vidxp.capabilities.videoprism.config import VideoPrismConfig
-from vidxp.capabilities.videoprism.indexing import VISUAL_PROCESSOR
-from vidxp.capabilities.videoprism.models import get_videoprism_model
-from vidxp.capabilities.videoprism.operations import search_operation
-from vidxp.capabilities.videoprism.specs import VIDEOPRISM_MODEL
+from vidxp.capabilities.action.config import VideoPrismConfig
+from vidxp.capabilities.action.indexing import VISUAL_PROCESSOR
+from vidxp.capabilities.action.models import get_videoprism_model
+from vidxp.capabilities.action.operations import search_operation
+from vidxp.capabilities.action.specs import VIDEOPRISM_MODEL
from vidxp.capabilities.visual import index_capabilities
from vidxp.core.contracts import IndexConfig, VideoSource
from vidxp.core.indexing_common import ProgressCallback, report_preparation
@@ -40,16 +40,16 @@ def model_manifest(
config: IndexConfig,
_sources: tuple[VideoSource, ...],
) -> Mapping[str, Any]:
- return {"videoprism": VIDEOPRISM_MODEL.identity()}
+ return {"action": VIDEOPRISM_MODEL.identity()}
DEFINITION = CapabilityDefinition(
- name="videoprism",
- label="Temporal video search",
- description="Index and search temporal video clips with VideoPrism.",
- extra="videoprism",
+ name="action",
+ label="Action and motion search",
+ description="Index and search multi-frame actions and motion.",
+ extra="action",
config_model=VideoPrismConfig,
- collection_name="videoprism",
+ collection_name="action",
index_stage="visual_indexing",
execution_group="visual",
prepares_models=True,
diff --git a/src/vidxp/capabilities/videoprism/indexing.py b/src/vidxp/capabilities/action/indexing.py
similarity index 95%
rename from src/vidxp/capabilities/videoprism/indexing.py
rename to src/vidxp/capabilities/action/indexing.py
index 53df5f49..cf397418 100644
--- a/src/vidxp/capabilities/videoprism/indexing.py
+++ b/src/vidxp/capabilities/action/indexing.py
@@ -3,13 +3,13 @@
from dataclasses import dataclass, field
from typing import Any, Sequence
-from vidxp.capabilities.videoprism.config import videoprism_config
-from vidxp.capabilities.videoprism.models import (
+from vidxp.capabilities.action.config import videoprism_config
+from vidxp.capabilities.action.models import (
VideoPrismModel,
get_videoprism_model,
normalize_pooled_output,
)
-from vidxp.capabilities.videoprism.specs import VIDEOPRISM_MODEL
+from vidxp.capabilities.action.specs import VIDEOPRISM_MODEL
from vidxp.core.contracts import (
CancellationToken,
IndexConfig,
@@ -74,7 +74,7 @@ def videoprism_records(
source_id = stable_source_id(
config.run_id,
str(config.video_id),
- "videoprism",
+ "action",
f"f{first.frame_index:012d}-f{last.frame_index:012d}",
generation_id=config.generation_id,
)
@@ -83,7 +83,7 @@ def videoprism_records(
source_id=source_id,
embedding=list(vector),
metadata={
- **config.record_identity("videoprism", source_id),
+ **config.record_identity("action", source_id),
"frame_index": first.frame_index,
"end_frame_index": last.frame_index,
"timestamp": first.timestamp,
@@ -116,7 +116,7 @@ def _store_clips(
]
vectors = encode_video_clips(model_clips, state.provider)
state.stored_clips += storage.upsert(
- "videoprism",
+ "action",
videoprism_records(group, vectors, info, config),
batch_size=config.storage_batch_size,
cancellation=cancellation,
diff --git a/src/vidxp/capabilities/videoprism/models.py b/src/vidxp/capabilities/action/models.py
similarity index 94%
rename from src/vidxp/capabilities/videoprism/models.py
rename to src/vidxp/capabilities/action/models.py
index dde6e0fb..7a89cd86 100644
--- a/src/vidxp/capabilities/videoprism/models.py
+++ b/src/vidxp/capabilities/action/models.py
@@ -3,7 +3,7 @@
from dataclasses import dataclass
from typing import Any, Callable
-from vidxp.capabilities.videoprism.specs import VIDEOPRISM_MODEL
+from vidxp.capabilities.action.specs import VIDEOPRISM_MODEL
from vidxp.core.indexing_common import report_preparation
from vidxp.model_contracts import loaded_compute_precision
from vidxp.ports import ModelRuntimePort
@@ -28,7 +28,7 @@ def get_videoprism_model(
download: bool = False,
progress: Callable[[dict[str, Any]], None] | None = None,
) -> VideoPrismModel:
- device = runtime.device_for("videoprism")
+ device = runtime.device_for("action")
key = VIDEOPRISM_MODEL.key(device)
def load() -> VideoPrismModel:
diff --git a/src/vidxp/capabilities/videoprism/operations.py b/src/vidxp/capabilities/action/operations.py
similarity index 97%
rename from src/vidxp/capabilities/videoprism/operations.py
rename to src/vidxp/capabilities/action/operations.py
index dc7b804c..34a342bd 100644
--- a/src/vidxp/capabilities/videoprism/operations.py
+++ b/src/vidxp/capabilities/action/operations.py
@@ -5,7 +5,7 @@
from vidxp.capabilities.contracts import CapabilityContext
from vidxp.capabilities.schemas import SearchInput, SearchResult
from vidxp.capabilities.search import search_embeddings
-from vidxp.capabilities.videoprism.models import (
+from vidxp.capabilities.action.models import (
get_videoprism_model,
normalize_pooled_output,
)
@@ -70,7 +70,7 @@ def search_videoprism(
raise ValueError("top_k must be greater than zero.")
return search_embeddings(
cleaned,
- "videoprism",
+ "action",
videoprism_embedding(cleaned, runtime),
config=config,
required_metadata=REQUIRED_METADATA,
diff --git a/src/vidxp/capabilities/videoprism/requirements.txt b/src/vidxp/capabilities/action/requirements.txt
similarity index 100%
rename from src/vidxp/capabilities/videoprism/requirements.txt
rename to src/vidxp/capabilities/action/requirements.txt
diff --git a/src/vidxp/capabilities/videoprism/specs.py b/src/vidxp/capabilities/action/specs.py
similarity index 93%
rename from src/vidxp/capabilities/videoprism/specs.py
rename to src/vidxp/capabilities/action/specs.py
index f2f9209d..cda9a00b 100644
--- a/src/vidxp/capabilities/videoprism/specs.py
+++ b/src/vidxp/capabilities/action/specs.py
@@ -2,7 +2,7 @@
VIDEOPRISM_MODEL = ModelSpec(
- capability="videoprism",
+ capability="action",
provider="transformers",
model_id="google/videoprism-lvt-base-f16r288",
revision="fb6de9f0eb7bc285be86bdca1cf7daa3e3ef51ff",
diff --git a/src/vidxp/capabilities/dialogue/__init__.py b/src/vidxp/capabilities/dialogue/__init__.py
deleted file mode 100644
index 0573c3c2..00000000
--- a/src/vidxp/capabilities/dialogue/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Dialogue capability implementation."""
diff --git a/src/vidxp/capabilities/registry.py b/src/vidxp/capabilities/registry.py
index c9ed84c1..c1503704 100644
--- a/src/vidxp/capabilities/registry.py
+++ b/src/vidxp/capabilities/registry.py
@@ -501,12 +501,12 @@ def runtime_distributions(self) -> tuple[str, ...]:
def _builtin_plugins() -> tuple[CapabilityPlugin, ...]:
from vidxp.capabilities.actor.definition import PLUGIN as actor
- from vidxp.capabilities.dialogue.definition import PLUGIN as dialogue
+ from vidxp.capabilities.speech.definition import PLUGIN as speech
from vidxp.capabilities.scene.definition import PLUGIN as scene
from vidxp.capabilities.sound.definition import PLUGIN as sound
- from vidxp.capabilities.videoprism.definition import PLUGIN as videoprism
+ from vidxp.capabilities.action.definition import PLUGIN as action
- return dialogue, sound, scene, actor, videoprism
+ return speech, sound, scene, actor, action
def _external_entry_points(allowlist: tuple[str, ...]) -> tuple[EntryPoint, ...]:
diff --git a/src/vidxp/capabilities/speech/__init__.py b/src/vidxp/capabilities/speech/__init__.py
new file mode 100644
index 00000000..e21af745
--- /dev/null
+++ b/src/vidxp/capabilities/speech/__init__.py
@@ -0,0 +1 @@
+"""Timestamped speech transcription and semantic search."""
diff --git a/src/vidxp/capabilities/dialogue/config.py b/src/vidxp/capabilities/speech/config.py
similarity index 68%
rename from src/vidxp/capabilities/dialogue/config.py
rename to src/vidxp/capabilities/speech/config.py
index 8f688332..618df26e 100644
--- a/src/vidxp/capabilities/dialogue/config.py
+++ b/src/vidxp/capabilities/speech/config.py
@@ -6,12 +6,12 @@
from vidxp.core.contracts import IndexConfig
-class DialogueConfig(CapabilityConfig):
+class SpeechConfig(CapabilityConfig):
words_per_phrase: int = Field(default=5, gt=0)
embedding_batch_size: int = Field(default=128, gt=0)
transcription_batch_size: int = Field(default=16, gt=0)
normalize_embeddings: bool = True
-def dialogue_config(config: IndexConfig) -> DialogueConfig:
- return DialogueConfig.model_validate(config.options_for("dialogue"))
+def speech_config(config: IndexConfig) -> SpeechConfig:
+ return SpeechConfig.model_validate(config.options_for("speech"))
diff --git a/src/vidxp/capabilities/dialogue/definition.py b/src/vidxp/capabilities/speech/definition.py
similarity index 83%
rename from src/vidxp/capabilities/dialogue/definition.py
rename to src/vidxp/capabilities/speech/definition.py
index 0c0ba465..63771bca 100644
--- a/src/vidxp/capabilities/dialogue/definition.py
+++ b/src/vidxp/capabilities/speech/definition.py
@@ -14,13 +14,13 @@
PreparationContext,
module_import_check,
)
-from vidxp.capabilities.dialogue.config import DialogueConfig
-from vidxp.capabilities.dialogue.models import get_embedder, get_whisper_model
-from vidxp.capabilities.dialogue.specs import (
+from vidxp.capabilities.speech.config import SpeechConfig
+from vidxp.capabilities.speech.models import get_embedder, get_whisper_model
+from vidxp.capabilities.speech.specs import (
FASTER_WHISPER_MODEL,
QWEN3_EMBEDDING_MODEL,
)
-from vidxp.capabilities.dialogue.operations import (
+from vidxp.capabilities.speech.operations import (
index_capability,
search_operation,
)
@@ -45,15 +45,15 @@ def prepare_models(
context: PreparationContext,
progress: ProgressCallback | None,
) -> tuple[str, ...]:
- DialogueConfig.model_validate(context.settings)
+ SpeechConfig.model_validate(context.settings)
prepared = []
def report(stage: str, message: str) -> None:
report_preparation(progress, stage, message)
report(
- "dialogue_model",
- f"Preparing dialogue model: {QWEN3_EMBEDDING_MODEL.model_id}",
+ "speech_model",
+ f"Preparing speech-search model: {QWEN3_EMBEDDING_MODEL.model_id}",
)
get_embedder(context.runtime, download=True, progress=progress)
prepared.append(QWEN3_EMBEDDING_MODEL.model_id)
@@ -72,7 +72,7 @@ def model_manifest(
sources: tuple[VideoSource, ...],
) -> Mapping[str, Any]:
result: dict[str, Any] = {
- "dialogue": {
+ "speech": {
**QWEN3_EMBEDDING_MODEL.identity(),
}
}
@@ -82,14 +82,14 @@ def model_manifest(
DEFINITION = CapabilityDefinition(
- name="dialogue",
- label="Dialogue search",
- description="Index and search spoken dialogue.",
- extra="dialogue",
- config_model=DialogueConfig,
- collection_name="dialogue",
- index_stage="dialogue_indexing",
- execution_group="dialogue",
+ name="speech",
+ label="Speech search",
+ description="Transcribe and search spoken words with timestamps.",
+ extra="speech",
+ config_model=SpeechConfig,
+ collection_name="speech",
+ index_stage="speech_indexing",
+ execution_group="speech",
prepares_models=True,
roles=(CapabilityRole.searchable, CapabilityRole.queryable),
model_specs=(QWEN3_EMBEDDING_MODEL, FASTER_WHISPER_MODEL),
diff --git a/src/vidxp/capabilities/dialogue/indexing.py b/src/vidxp/capabilities/speech/indexing.py
similarity index 90%
rename from src/vidxp/capabilities/dialogue/indexing.py
rename to src/vidxp/capabilities/speech/indexing.py
index f90919ab..c8ee42d6 100644
--- a/src/vidxp/capabilities/dialogue/indexing.py
+++ b/src/vidxp/capabilities/speech/indexing.py
@@ -4,9 +4,9 @@
from pathlib import Path
from typing import Any, Mapping, Sequence
-from vidxp.capabilities.dialogue.config import dialogue_config
-from vidxp.capabilities.dialogue.models import get_embedder, get_whisper_model
-from vidxp.capabilities.dialogue.specs import (
+from vidxp.capabilities.speech.config import speech_config
+from vidxp.capabilities.speech.models import get_embedder, get_whisper_model
+from vidxp.capabilities.speech.specs import (
FASTER_WHISPER_MODEL,
QWEN3_EMBEDDING_MODEL,
)
@@ -123,14 +123,14 @@ def transcribe_video(
import av
from faster_whisper import BatchedInferencePipeline
- settings = dialogue_config(config)
+ settings = speech_config(config)
cancellation.raise_if_cancelled()
with av.open(str(input_path)) as container:
if not container.streams.audio:
report_progress(
progress,
"dialogue_skipped",
- "No audio stream was found; dialogue indexing was skipped.",
+ "No audio stream was found; speech indexing was skipped.",
)
return [], None
report_progress(
@@ -175,7 +175,7 @@ def transcribe_video(
return result, str(info.language)
-def _dialogue_records(
+def _speech_records(
phrases,
vectors,
config: IndexConfig,
@@ -185,7 +185,7 @@ def _dialogue_records(
source_id = stable_source_id(
config.run_id,
str(config.video_id),
- "dialogue",
+ "speech",
f"p{phrase.phrase_id:08d}",
generation_id=config.generation_id,
)
@@ -195,7 +195,7 @@ def _dialogue_records(
embedding=vector.tolist(),
document=phrase.text,
metadata={
- **config.record_identity("dialogue", source_id),
+ **config.record_identity("speech", source_id),
"phrase_id": phrase.phrase_id,
"text": phrase.text,
"start": phrase.start,
@@ -206,7 +206,7 @@ def _dialogue_records(
return records
-def index_dialogue(
+def index_speech(
source: VideoSource,
*,
config: IndexConfig,
@@ -217,7 +217,7 @@ def index_dialogue(
) -> dict[str, Any]:
if config.video_id is None:
raise ValueError("IndexConfig.video_id is required for indexing.")
- settings = dialogue_config(config)
+ settings = speech_config(config)
language = None
if source.transcript is not None:
@@ -225,7 +225,7 @@ def index_dialogue(
else:
if source.path is None:
raise ValueError(
- "Dialogue indexing requires a transcript or video path."
+ "Speech indexing requires a transcript or video path."
)
segments, language = transcribe_video(
source.path,
@@ -244,16 +244,16 @@ def index_dialogue(
report_progress(
progress,
- "preparing_dialogue_model",
- f"Preparing dialogue model: {QWEN3_EMBEDDING_MODEL.model_id}.",
+ "preparing_speech_model",
+ f"Preparing speech-search model: {QWEN3_EMBEDDING_MODEL.model_id}.",
0,
len(phrases),
)
encoder = get_embedder(runtime)
report_progress(
progress,
- "dialogue_indexing",
- "Indexing dialogue phrases.",
+ "speech_indexing",
+ "Indexing speech phrases.",
0,
len(phrases),
)
@@ -268,15 +268,15 @@ def index_dialogue(
normalize_embeddings=settings.normalize_embeddings,
)
stored += storage.upsert(
- "dialogue",
- _dialogue_records(group, vectors, config),
+ "speech",
+ _speech_records(group, vectors, config),
batch_size=config.storage_batch_size,
cancellation=cancellation,
)
report_progress(
progress,
- "dialogue_indexing",
- "Indexing dialogue phrases.",
+ "speech_indexing",
+ "Indexing speech phrases.",
stored,
len(phrases),
)
diff --git a/src/vidxp/capabilities/dialogue/models.py b/src/vidxp/capabilities/speech/models.py
similarity index 94%
rename from src/vidxp/capabilities/dialogue/models.py
rename to src/vidxp/capabilities/speech/models.py
index 6979fd3f..9dff1eb5 100644
--- a/src/vidxp/capabilities/dialogue/models.py
+++ b/src/vidxp/capabilities/speech/models.py
@@ -5,7 +5,7 @@
from vidxp.ports import ModelRuntimePort
from vidxp.core.indexing_common import report_preparation
from vidxp.model_contracts import loaded_compute_precision
-from vidxp.capabilities.dialogue.specs import (
+from vidxp.capabilities.speech.specs import (
FASTER_WHISPER_MODEL,
QWEN3_EMBEDDING_MODEL,
whisper_compute_type,
@@ -18,7 +18,7 @@ def get_embedder(
download: bool = False,
progress: Callable[[dict[str, Any]], None] | None = None,
) -> Any:
- device = runtime.device_for("dialogue.embedding")
+ device = runtime.device_for("speech.embedding")
key = QWEN3_EMBEDDING_MODEL.key(device)
def load() -> Any:
@@ -58,7 +58,7 @@ def get_whisper_model(
download: bool = False,
progress: Callable[[dict[str, Any]], None] | None = None,
) -> Any:
- device = runtime.device_for("dialogue.transcription")
+ device = runtime.device_for("speech.transcription")
compute_type = whisper_compute_type(device)
key = FASTER_WHISPER_MODEL.key(f"{device}:{compute_type}")
diff --git a/src/vidxp/capabilities/dialogue/operations.py b/src/vidxp/capabilities/speech/operations.py
similarity index 82%
rename from src/vidxp/capabilities/dialogue/operations.py
rename to src/vidxp/capabilities/speech/operations.py
index 7c6e2e9c..b5b5431c 100644
--- a/src/vidxp/capabilities/dialogue/operations.py
+++ b/src/vidxp/capabilities/speech/operations.py
@@ -7,9 +7,9 @@
CapabilityIndexResult,
)
from vidxp.capabilities.registry import CapabilityRegistry
-from vidxp.capabilities.dialogue.config import dialogue_config
-from vidxp.capabilities.dialogue.indexing import index_dialogue
-from vidxp.capabilities.dialogue.models import get_embedder
+from vidxp.capabilities.speech.config import speech_config
+from vidxp.capabilities.speech.indexing import index_speech
+from vidxp.capabilities.speech.models import get_embedder
from vidxp.capabilities.schemas import SearchInput, SearchResult
from vidxp.capabilities.search import search_embeddings
from vidxp.core.contracts import (
@@ -46,12 +46,12 @@ def index_capability(
registry: CapabilityRegistry,
runtime: ModelRuntimePort,
progress: ProgressCallback | None = None,
- modalities: tuple[str, ...] = ("dialogue",),
+ modalities: tuple[str, ...] = ("speech",),
) -> CapabilityIndexResult:
- if modalities != ("dialogue",):
- raise ValueError("The dialogue indexer only accepts dialogue.")
+ if modalities != ("speech",):
+ raise ValueError("The speech indexer only accepts speech.")
return CapabilityIndexResult(
- summary=index_dialogue(
+ summary=index_speech(
source,
config=config,
storage=storage,
@@ -62,12 +62,12 @@ def index_capability(
)
-def dialogue_embedding(
+def speech_embedding(
query: str,
config: IndexConfig,
runtime: ModelRuntimePort,
) -> list[float]:
- settings = dialogue_config(config)
+ settings = speech_config(config)
encoder = get_embedder(runtime)
encoded = encoder.encode_query(
[query],
@@ -77,7 +77,7 @@ def dialogue_embedding(
return encoded[0].tolist()
-def search_dialogue(
+def search_speech(
query: str,
*,
config: IndexConfig,
@@ -95,8 +95,8 @@ def search_dialogue(
raise ValueError("top_k must be greater than zero.")
return search_embeddings(
cleaned,
- "dialogue",
- dialogue_embedding(cleaned, config, runtime),
+ "speech",
+ speech_embedding(cleaned, config, runtime),
config=config,
required_metadata=REQUIRED_METADATA,
top_k=top_k,
@@ -112,7 +112,7 @@ def search_operation(
request: SearchInput,
) -> SearchResult:
config = context.require_config()
- return search_dialogue(
+ return search_speech(
request.query,
config=config,
top_k=request.top_k,
diff --git a/src/vidxp/capabilities/dialogue/requirements.txt b/src/vidxp/capabilities/speech/requirements.txt
similarity index 100%
rename from src/vidxp/capabilities/dialogue/requirements.txt
rename to src/vidxp/capabilities/speech/requirements.txt
diff --git a/src/vidxp/capabilities/dialogue/specs.py b/src/vidxp/capabilities/speech/specs.py
similarity index 92%
rename from src/vidxp/capabilities/dialogue/specs.py
rename to src/vidxp/capabilities/speech/specs.py
index ac889c8d..4dc5a8db 100644
--- a/src/vidxp/capabilities/dialogue/specs.py
+++ b/src/vidxp/capabilities/speech/specs.py
@@ -2,7 +2,7 @@
QWEN3_EMBEDDING_MODEL = ModelSpec(
- capability="dialogue.embedding",
+ capability="speech.embedding",
provider="sentence-transformers",
model_id="Qwen/Qwen3-Embedding-0.6B",
revision="97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3",
@@ -16,7 +16,7 @@
)
FASTER_WHISPER_MODEL = ModelSpec(
- capability="dialogue.transcription",
+ capability="speech.transcription",
provider="faster-whisper",
model_id="dropbox-dash/faster-whisper-large-v3-turbo",
revision="0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf",
diff --git a/src/vidxp/capabilities/videoprism/__init__.py b/src/vidxp/capabilities/videoprism/__init__.py
deleted file mode 100644
index 7e891a3d..00000000
--- a/src/vidxp/capabilities/videoprism/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""VideoPrism temporal video search capability."""
diff --git a/src/vidxp/codex_plugin.py b/src/vidxp/codex_plugin.py
index 4f2bfa00..87711171 100644
--- a/src/vidxp/codex_plugin.py
+++ b/src/vidxp/codex_plugin.py
@@ -472,12 +472,18 @@ def install_codex_plugin(
data_directory=data_directory,
device=device,
)["mcpServers"][PLUGIN_NAME]
+ environment_arguments = [
+ argument
+ for name, value in mcp.get("env", {}).items()
+ for argument in ("--env", f"{name}={value}")
+ ]
_run_codex(
command,
[
"mcp",
"add",
PLUGIN_NAME,
+ *environment_arguments,
"--",
str(mcp["command"]),
*(str(argument) for argument in mcp["args"]),
diff --git a/src/vidxp/core/contracts.py b/src/vidxp/core/contracts.py
index 406337f4..c58d632f 100644
--- a/src/vidxp/core/contracts.py
+++ b/src/vidxp/core/contracts.py
@@ -11,7 +11,7 @@
from urllib.parse import quote
-INDEX_SCHEMA_VERSION = 6
+INDEX_SCHEMA_VERSION = 7
MANIFEST_SCHEMA_VERSION = 2
@@ -211,7 +211,7 @@ def local(cls, **changes: Any) -> "IndexConfig":
"storage_directory": "chroma_data",
}
if "enabled_modalities" not in changes:
- defaults["enabled_modalities"] = ("dialogue", "scene", "actor")
+ defaults["enabled_modalities"] = ("speech", "scene", "actor")
defaults.update(changes)
return cls(**defaults)
diff --git a/src/vidxp/frontend.py b/src/vidxp/frontend.py
index ae3d4b04..7cf4294b 100644
--- a/src/vidxp/frontend.py
+++ b/src/vidxp/frontend.py
@@ -130,11 +130,11 @@ def _settings_from_arguments(
}
CAPABILITY_LABELS = {
"actor": "Actor groups",
- "dialogue": "Dialogue search",
+ "speech": "Speech search",
"natural-language": "Ask a question",
"scene": "Scene search",
"sound": "Sound event search (FineLAP)",
- "videoprism": "Temporal action search (VideoPrism)",
+ "action": "Action and motion search",
}
@@ -409,7 +409,7 @@ def _run_indexing(
scene_sample_fps=scene_sample_fps,
capability_options=(
{
- "videoprism": {
+ "action": {
"sample_fps": videoprism_sample_fps,
}
}
@@ -466,7 +466,7 @@ def _videoprism_sample_fps_control(
*,
disabled: bool,
) -> float | None:
- if "videoprism" not in modalities:
+ if "action" not in modalities:
return None
return float(
st.selectbox(
@@ -710,7 +710,7 @@ def poll_search_job():
"Closest sampled scene"
if search_type == "scene"
else "Closest temporal action clip"
- if search_type == "videoprism"
+ if search_type == "action"
else "Closest supporting evidence"
if search_type == "natural-language"
else f"Closest {search_type} match"
@@ -722,7 +722,7 @@ def poll_search_job():
"It does not identify the first occurrence and is not reliable "
"for counting people."
)
- elif search_type == "videoprism":
+ elif search_type == "action":
st.caption(
"VideoPrism ranks short multi-frame clips, making it better suited "
"to actions and events than single-frame scene search."
@@ -881,7 +881,7 @@ def _search_controls(ready, uploaded_video, available_modalities):
else "For example: What happens after the taxi arrives?"
if search_type == "natural-language"
else "For example: A person opens a door and walks out."
- if search_type == "videoprism"
+ if search_type == "action"
else "For example: Chef makes pizza and cuts it up."
),
disabled=not ready,
@@ -908,7 +908,7 @@ def run():
service = _configured_service()
st.title("VidXP")
st.caption(
- "Index and search video by dialogue, sound, scenes, actions, and actor."
+ "Index and search video by speech, sound, scenes, actions, and actors."
)
st.caption(f"Index repository: {service.layout.root}")
if notice := st.session_state.pop(MEDIA_NOTICE_KEY, None):
diff --git a/src/vidxp/infrastructure/ollama_query.py b/src/vidxp/infrastructure/ollama_query.py
index 9347d04b..93f8b1dd 100644
--- a/src/vidxp/infrastructure/ollama_query.py
+++ b/src/vidxp/infrastructure/ollama_query.py
@@ -64,6 +64,7 @@ def __init__(
retries=retries,
model_settings={
"temperature": 0,
+ "openai_reasoning_effort": "none",
"max_tokens": 1024,
"timeout": timeout_seconds,
},
@@ -75,6 +76,7 @@ def __init__(
retries=retries,
model_settings={
"temperature": 0,
+ "openai_reasoning_effort": "none",
"max_tokens": 2048,
"timeout": timeout_seconds,
},
diff --git a/src/vidxp/mcp_cli.py b/src/vidxp/mcp_cli.py
index 3834f833..d68e6d9d 100644
--- a/src/vidxp/mcp_cli.py
+++ b/src/vidxp/mcp_cli.py
@@ -34,6 +34,7 @@ def stdio_client_config(
index_directory: str | None = None,
data_directory: Path | None = None,
device: str | None = None,
+ environment: dict[str, str] | None = None,
) -> dict[str, Any]:
"""Build Claude Desktop/compatible stdio ``mcpServers`` JSON."""
@@ -47,12 +48,21 @@ def stdio_client_config(
):
if value is not None:
arguments.extend((flag, str(value)))
+ server: dict[str, Any] = {
+ "command": command or mcp_executable(),
+ "args": arguments,
+ }
+ selected_environment = environment if environment is not None else os.environ
+ local_query_environment = {
+ name: value
+ for name in ("VIDXP_SLM_BASE_URL", "VIDXP_SLM_MODEL")
+ if (value := selected_environment.get(name))
+ }
+ if local_query_environment:
+ server["env"] = local_query_environment
return {
"mcpServers": {
- "vidxp": {
- "command": command or mcp_executable(),
- "args": arguments,
- }
+ "vidxp": server,
}
}
diff --git a/src/vidxp/runtime.py b/src/vidxp/runtime.py
index a02ba495..2b304365 100644
--- a/src/vidxp/runtime.py
+++ b/src/vidxp/runtime.py
@@ -241,7 +241,7 @@ def _configure_cpu_threads(self) -> None:
cv2.setNumThreads(self.cpu_thread_budget)
def device_for(self, capability: str) -> str:
- if capability == "dialogue.transcription":
+ if capability == "speech.transcription":
return self.backends.transcription_device
if capability == "actor":
return self.backends.actor_device
diff --git a/src/vidxp/settings.py b/src/vidxp/settings.py
index 32326250..c5893183 100644
--- a/src/vidxp/settings.py
+++ b/src/vidxp/settings.py
@@ -27,6 +27,7 @@
DEFAULT_HTTP_PORT = 32191
+DEFAULT_LOCAL_QUERY_MODEL = "qwen3.5:4b-q4_K_M"
_TUSD_EXACT_ORIGIN = re.compile(
r"(?Phttps|http)://(?P"
r"[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?"
@@ -333,7 +334,7 @@ def _empty_auth_values_are_unset(cls, value):
@model_validator(mode="before")
@classmethod
- def _derive_storage_paths(cls, value):
+ def _derive_storage_paths_and_query_model(cls, value):
if not isinstance(value, dict):
return value
configured = dict(value)
@@ -349,6 +350,10 @@ def _derive_storage_paths(cls, value):
"model_cache",
default_model_directory(data_directory),
)
+ if configured.get("slm_base_url") not in {None, ""} and configured.get(
+ "slm_model"
+ ) in {None, ""}:
+ configured["slm_model"] = DEFAULT_LOCAL_QUERY_MODEL
return configured
@field_validator("slm_base_url", "slm_model", mode="before")
diff --git a/tests/test_agent_ablation.py b/tests/test_agent_ablation.py
new file mode 100644
index 00000000..a8f21616
--- /dev/null
+++ b/tests/test_agent_ablation.py
@@ -0,0 +1,158 @@
+from __future__ import annotations
+
+import json
+from pathlib import Path
+
+import pytest
+
+from vidxp.benchmarks.agent_ablation_score import (
+ interval_iou,
+ score_ablation_boundary,
+ score_temporal_grounding,
+)
+from vidxp.benchmarks.agent_ablation_tests import generate_tests
+
+
+def test_interval_iou_matches_temporal_overlap() -> None:
+ assert interval_iou(10, 20, 15, 25) == pytest.approx(1 / 3)
+ assert interval_iou(0, 5, 6, 10) == 0
+
+
+def test_temporal_grounding_reports_longvale_metrics() -> None:
+ output = json.dumps(
+ {
+ "video_id": "video-1",
+ "start_seconds": 10,
+ "end_seconds": 20,
+ }
+ )
+ result = score_temporal_grounding(
+ output,
+ {
+ "vars": {
+ "video_id": "video-1",
+ "duration_seconds": 30,
+ "expected_start": 15,
+ "expected_end": 25,
+ }
+ },
+ )
+
+ assert result["pass"] is True
+ assert result["namedScores"]["temporal_iou"] == pytest.approx(1 / 3)
+ assert result["namedScores"]["r1_tiou_0_3"] == 1
+ assert result["namedScores"]["r1_tiou_0_5"] == 0
+
+
+def test_temporal_grounding_rejects_null_or_out_of_bounds_intervals() -> None:
+ context = {
+ "vars": {
+ "video_id": "video-1",
+ "duration_seconds": 30,
+ "expected_start": 1,
+ "expected_end": 2,
+ }
+ }
+ null_result = score_temporal_grounding(
+ '{"video_id":"video-1","start_seconds":null,"end_seconds":null}',
+ context,
+ )
+ bounds_result = score_temporal_grounding(
+ '{"video_id":"video-1","start_seconds":20,"end_seconds":31}',
+ context,
+ )
+
+ assert null_result["pass"] is False
+ assert bounds_result["pass"] is False
+
+
+def test_ablation_boundary_requires_mcp_only_in_the_on_condition() -> None:
+ trace = {
+ "spans": [
+ {
+ "name": "MCP tool call",
+ "attributes": {"tool.name": "mcp__vidxp__search_moments"},
+ }
+ ]
+ }
+
+ assert score_ablation_boundary(
+ "{}", {"vars": {"expected_mcp": True}, "trace": trace}
+ )["pass"]
+ assert not score_ablation_boundary(
+ "{}", {"vars": {"expected_mcp": False}, "trace": trace}
+ )["pass"]
+
+
+def test_ablation_boundary_rejects_direct_vidxp_cli_bypass() -> None:
+ trace = {
+ "spans": [
+ {
+ "name": "command",
+ "attributes": {"command": "vidxp search sound alarm"},
+ }
+ ]
+ }
+
+ result = score_ablation_boundary(
+ "{}", {"vars": {"expected_mcp": False}, "trace": trace}
+ )
+
+ assert result["pass"] is False
+ assert "bypassed" in result["reason"]
+
+
+def test_generator_pairs_each_manifest_task_across_conditions(
+ tmp_path: Path,
+) -> None:
+ manifest = tmp_path / "tasks.json"
+ manifest.write_text(
+ json.dumps(
+ [
+ {
+ "id": "task-1",
+ "dataset": "example",
+ "video_id": "video-1",
+ "media_relpath": "media/video-1.mp4",
+ "duration_seconds": 10,
+ "event_index": 0,
+ "query": "an event",
+ "expected_start": 1,
+ "expected_end": 2,
+ "modalities": ["sound"],
+ }
+ ]
+ ),
+ encoding="utf-8",
+ )
+
+ tests = generate_tests(
+ {
+ "manifest": str(manifest),
+ "providers": {"mcp_on": "on", "mcp_off": "off"},
+ }
+ )
+
+ assert [test["providers"] for test in tests] == [["on"], ["off"]]
+ assert [test["vars"]["expected_mcp"] for test in tests] == [True, False]
+
+
+def test_committed_pilot_expands_to_ten_matched_pairs(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ benchmark = Path(__file__).parents[1] / "benchmarks" / "codex-mcp"
+ monkeypatch.chdir(benchmark)
+
+ tests = generate_tests(
+ {
+ "manifest": "tasks/longvale-part9-pilot.json",
+ "providers": {"mcp_on": "on", "mcp_off": "off"},
+ }
+ )
+
+ assert len(tests) == 20
+ assert {test["metadata"]["condition"] for test in tests} == {
+ "mcp-on",
+ "mcp-off",
+ }
+ assert len({test["metadata"]["task_id"] for test in tests}) == 10
diff --git a/tests/test_api.py b/tests/test_api.py
index 7bd6d735..a5794f8f 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -119,7 +119,7 @@ def ingestion_status() -> MediaUploadSessionStatus:
transfer_backend=UploadTransferBackend.local_path,
resumable=False,
index_after_import=True,
- index_modalities=("scene", "dialogue"),
+ index_modalities=("scene", "speech"),
expires_at=now.replace(year=now.year + 1),
maximum_files=10,
maximum_file_bytes=50 * 1024 * 1024 * 1024,
@@ -466,7 +466,7 @@ def test_local_ingestion_api_delegates_to_durable_batch_workflow(self):
status = ingestion_status()
context.application.select_index_modalities.return_value = (
"scene",
- "dialogue",
+ "speech",
)
assert context.uploads is not None
context.uploads.create_local_ingestion.return_value = status
@@ -481,7 +481,7 @@ def test_local_ingestion_api_delegates_to_durable_batch_workflow(self):
"C:/Premiere/b-roll.mov",
],
"index_after_import": True,
- "modalities": ["scene", "dialogue"],
+ "modalities": ["scene", "speech"],
},
)
fetched = client.get(
@@ -496,7 +496,7 @@ def test_local_ingestion_api_delegates_to_durable_batch_workflow(self):
self.assertEqual(created.json()["transfer_backend"], "local_path")
self.assertEqual(fetched.status_code, 200)
context.application.select_index_modalities.assert_called_once_with(
- ("scene", "dialogue")
+ ("scene", "speech")
)
call = context.uploads.create_local_ingestion.call_args
self.assertEqual(
@@ -509,7 +509,7 @@ def test_local_ingestion_api_delegates_to_durable_batch_workflow(self):
self.assertTrue(call.kwargs["index_after_import"])
self.assertEqual(
call.kwargs["index_modalities"],
- ("scene", "dialogue"),
+ ("scene", "speech"),
)
context.uploads.get_status.assert_called_once_with(
INGESTION_ID,
@@ -728,7 +728,7 @@ def test_failed_model_preparation_job_is_structured_over_http(self):
details={
"model": "publisher/model",
"partial_files_preserved": True,
- "remediation": "vidxp prepare --modalities dialogue",
+ "remediation": "vidxp prepare --modalities speech",
},
retryable=True,
),
@@ -895,7 +895,7 @@ def test_grounded_query_submission_uses_the_durable_boundary(self):
json={
"question": "What happens after the taxi arrives?",
"media_id": MEDIA_ID,
- "modalities": ["scene", "dialogue"],
+ "modalities": ["scene", "speech"],
"top_k": 5,
},
)
@@ -908,7 +908,7 @@ def test_grounded_query_submission_uses_the_durable_boundary(self):
QueryVideoCommand(
question="What happens after the taxi arrives?",
media_id=MEDIA_ID,
- modalities=("scene", "dialogue"),
+ modalities=("scene", "speech"),
top_k=5,
),
)
diff --git a/tests/test_application.py b/tests/test_application.py
index 23fd0cc2..57a38cdc 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -660,7 +660,7 @@ def handler(_context, request):
registry = CapabilityRegistry(
(
search_plugin("scene"),
- search_plugin("dialogue"),
+ search_plugin("speech"),
actor,
)
)
@@ -671,10 +671,10 @@ def handler(_context, request):
registry=registry,
)
backend.active_config.return_value = IndexConfig.local(
- enabled_modalities=("scene", "dialogue", "actor"),
+ enabled_modalities=("scene", "speech", "actor"),
collection_names={
"scene": "scene",
- "dialogue": "dialogue",
+ "speech": "speech",
"actor": "actor",
},
)
@@ -682,8 +682,8 @@ def handler(_context, request):
result = application.search(SearchCommand(query="taxi"))
- self.assertEqual(searched, ["scene", "dialogue"])
- self.assertEqual(result.modalities, ("scene", "dialogue"))
+ self.assertEqual(searched, ["scene", "speech"])
+ self.assertEqual(result.modalities, ("scene", "speech"))
def test_application_boundary_returns_stable_validation_error(self):
application, _ = self.application("unused")
diff --git a/tests/test_benchmark_cli.py b/tests/test_benchmark_cli.py
index 4b0b7e68..aff61724 100644
--- a/tests/test_benchmark_cli.py
+++ b/tests/test_benchmark_cli.py
@@ -179,12 +179,12 @@ def test_hirest_dependency_hint_includes_benchmark_parser(self):
self.assertRaises(typer.BadParameter) as raised,
):
benchmark_cli._require_benchmark_dependencies(
- "dialogue",
+ "speech",
include_benchmark_extra=True,
)
self.assertIn(
- 'pip install "vidxp[dialogue,benchmarks]"',
+ 'pip install "vidxp[speech,benchmarks]"',
str(raised.exception),
)
diff --git a/tests/test_benchmarks.py b/tests/test_benchmarks.py
index 42303dbe..cfb31be0 100644
--- a/tests/test_benchmarks.py
+++ b/tests/test_benchmarks.py
@@ -61,7 +61,7 @@ def timed_hit(start, end, score, rank=1):
end=end,
score=score,
raw_distance=-score,
- modality="dialogue",
+ modality="speech",
source_id=f"hit-{rank}",
metadata={},
)
diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py
index 29d45659..41427bbf 100644
--- a/tests/test_capabilities.py
+++ b/tests/test_capabilities.py
@@ -14,11 +14,12 @@
CapabilityOutput,
CapabilityPlugin,
CapabilityProvenance,
+ CapabilityRequestError,
OperationDefinition,
RuntimeCheck,
module_import_check,
)
-from vidxp.capabilities.dialogue.config import DialogueConfig
+from vidxp.capabilities.speech.config import SpeechConfig
from vidxp.capabilities.registry import (
CapabilityRegistry,
create_capability_registry,
@@ -26,7 +27,7 @@
from vidxp.capability_service import CapabilityService
from vidxp.capabilities.scene.config import SceneConfig
from vidxp.capabilities.sound.config import SoundConfig
-from vidxp.capabilities.videoprism.config import VideoPrismConfig
+from vidxp.capabilities.action.config import VideoPrismConfig
from vidxp.core.contracts import IndexConfig
from vidxp.core.runner import _index_groups
@@ -43,6 +44,12 @@ class CapabilityTests(unittest.TestCase):
def setUp(self):
self.registry = create_capability_registry()
+ def test_removed_model_oriented_names_are_not_capabilities(self):
+ for removed in ("dialogue", "videoprism"):
+ with self.subTest(removed=removed):
+ with self.assertRaises(CapabilityRequestError):
+ self.registry.get(removed)
+
def test_module_import_checks_run_in_an_isolated_process(self):
with patch(
"vidxp.capabilities.contracts.subprocess.run",
@@ -64,21 +71,21 @@ def test_module_import_checks_run_in_an_isolated_process(self):
def test_registry_drives_capability_metadata(self):
self.assertEqual(
self.registry.names(),
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
)
self.assertEqual(self.registry.index_names(), self.registry.names())
self.assertEqual(
self.registry.preparable_names(),
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
)
self.assertEqual(
self.registry.collection_names(),
{
- "dialogue": "dialogue",
+ "speech": "speech",
"sound": "sound",
"scene": "scene",
"actor": "actor",
- "videoprism": "videoprism",
+ "action": "action",
},
)
self.assertEqual(
@@ -87,11 +94,11 @@ def test_registry_drives_capability_metadata(self):
for capability in CapabilityService(self.registry).list()
),
(
- "Dialogue search",
+ "Speech search",
"Sound event search",
"Visual scene search",
"Actor recognition",
- "Temporal video search",
+ "Action and motion search",
),
)
@@ -152,14 +159,14 @@ def test_contracts_are_frozen_and_index_metadata_is_complete(self):
def test_built_in_settings_are_owned_and_validated(self):
self.assertIs(
- self.registry.get("dialogue").config_model,
- DialogueConfig,
+ self.registry.get("speech").config_model,
+ SpeechConfig,
)
self.assertIs(self.registry.get("scene").config_model, SceneConfig)
self.assertIs(self.registry.get("actor").config_model, ActorConfig)
self.assertIs(self.registry.get("sound").config_model, SoundConfig)
self.assertIs(
- self.registry.get("videoprism").config_model,
+ self.registry.get("action").config_model,
VideoPrismConfig,
)
@@ -229,13 +236,13 @@ def test_operation_only_capability_needs_no_index_metadata(self):
def test_visual_execution_group_is_explicit(self):
self.assertEqual(
_index_groups(
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
self.registry,
),
(
- ("dialogue",),
+ ("speech",),
("sound",),
- ("scene", "actor", "videoprism"),
+ ("scene", "actor", "action"),
),
)
self.assertIsNotNone(
@@ -245,7 +252,7 @@ def test_visual_execution_group_is_explicit(self):
self.registry.executor("actor").index_processor
)
self.assertIsNone(
- self.registry.executor("dialogue").index_processor
+ self.registry.executor("speech").index_processor
)
self.assertIsNone(self.registry.executor("sound").index_processor)
diff --git a/tests/test_cli.py b/tests/test_cli.py
index be5165c2..afdebf86 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -164,7 +164,7 @@ def test_failed_model_preparation_job_is_structured_in_cli_json(self):
details={
"model": "publisher/model",
"partial_files_preserved": True,
- "remediation": "vidxp prepare --modalities dialogue",
+ "remediation": "vidxp prepare --modalities speech",
},
retryable=True,
),
@@ -854,7 +854,7 @@ def test_index_list_joins_active_ids_to_registered_metadata(self):
snapshot_id=SNAPSHOT_ID,
media_count=1,
media_ids=(MEDIA_ID,),
- modalities=("scene", "dialogue"),
+ modalities=("scene", "speech"),
),
)
self.service.get_media.return_value = MediaAsset(
@@ -887,7 +887,7 @@ def test_index_list_joins_active_ids_to_registered_metadata(self):
payload = json.loads(result.output)
self.assertEqual(payload["snapshot_id"], SNAPSHOT_ID)
self.assertEqual(payload["media_count"], 1)
- self.assertEqual(payload["modalities"], ["scene", "dialogue"])
+ self.assertEqual(payload["modalities"], ["scene", "speech"])
self.assertEqual(payload["items"][0]["original_filename"], "video.mp4")
def test_doctor_and_prepare_use_shared_models(self):
@@ -943,14 +943,14 @@ def test_doctor_and_prepare_use_shared_models(self):
def test_doctor_accepts_repeated_modality_options(self):
self.service.check_dependencies.return_value = DependencyCheckResult(
ok=True,
- modalities=("dialogue", "scene"),
+ modalities=("speech", "scene"),
checks=(),
)
result = self.invoke(
[
"doctor",
"--modalities",
- "dialogue",
+ "speech",
"--modalities",
"scene",
"--json",
@@ -959,7 +959,7 @@ def test_doctor_accepts_repeated_modality_options(self):
self.assertEqual(result.exit_code, 0, result.output)
command = self.service.check_dependencies.call_args.args[0]
- self.assertEqual(command.modalities, ("dialogue", "scene"))
+ self.assertEqual(command.modalities, ("speech", "scene"))
def test_doctor_can_skip_model_readiness_for_install_validation(self):
self.service.check_dependencies.return_value = DependencyCheckResult(
diff --git a/tests/test_codex_plugin.py b/tests/test_codex_plugin.py
index 5fecbc32..674f6458 100644
--- a/tests/test_codex_plugin.py
+++ b/tests/test_codex_plugin.py
@@ -116,6 +116,42 @@ def runner(command: list[str], **_: object) -> subprocess.CompletedProcess[str]:
assert "skills and local MCP server" in result.detail
+def test_install_codex_plugin_registers_local_query_environment(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ calls: list[list[str]] = []
+
+ def runner(command: list[str], **_: object) -> subprocess.CompletedProcess[str]:
+ calls.append(command)
+ if command[1:4] == ["plugin", "marketplace", "add"]:
+ payload = {"marketplaceName": "vidxp-local"}
+ elif command[1:3] == ["plugin", "add"]:
+ payload = {"pluginId": "vidxp@vidxp-local", "version": "0.4.0"}
+ else:
+ return subprocess.CompletedProcess(command, 0, "ok", "")
+ return subprocess.CompletedProcess(command, 0, json.dumps(payload), "")
+
+ monkeypatch.setenv("VIDXP_SLM_BASE_URL", "http://127.0.0.1:11434/v1")
+ monkeypatch.setenv("VIDXP_SLM_MODEL", "qwen3.5:4b-q4_K_M")
+ with TemporaryDirectory() as directory:
+ install_codex_plugin(
+ Path(directory) / "marketplace",
+ codex_command="codex-test",
+ runner=runner,
+ )
+
+ assert calls[2][1:9] == [
+ "mcp",
+ "add",
+ "vidxp",
+ "--env",
+ "VIDXP_SLM_BASE_URL=http://127.0.0.1:11434/v1",
+ "--env",
+ "VIDXP_SLM_MODEL=qwen3.5:4b-q4_K_M",
+ "--",
+ ]
+
+
def test_install_codex_plugin_uses_git_marketplace_and_migrates_local_source() -> None:
calls: list[list[str]] = []
diff --git a/tests/test_contracts.py b/tests/test_contracts.py
index fb22c7df..54216c3f 100644
--- a/tests/test_contracts.py
+++ b/tests/test_contracts.py
@@ -116,18 +116,18 @@ def test_invalid_config_is_rejected(self):
).run_directory
with self.assertRaisesRegex(ValueError, "distinct"):
IndexConfig(
- enabled_modalities=("dialogue", "scene", "actor"),
+ enabled_modalities=("speech", "scene", "actor"),
collection_names={
- "dialogue": "shared",
+ "speech": "shared",
"scene": "shared",
"actor": "actor",
}
)
with self.assertRaisesRegex(ValueError, "3-512"):
IndexConfig(
- enabled_modalities=("dialogue", "scene", "actor"),
+ enabled_modalities=("speech", "scene", "actor"),
collection_names={
- "dialogue": "a",
+ "speech": "a",
"scene": "scene",
"actor": "actor",
}
diff --git a/tests/test_control_plane.py b/tests/test_control_plane.py
index 93a46345..93714bac 100644
--- a/tests/test_control_plane.py
+++ b/tests/test_control_plane.py
@@ -71,7 +71,7 @@ def test_select_index_modalities_defaults_and_rejects_non_indexable_names(self):
self.assertEqual(
defaults,
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
)
self.assertEqual(selected, ("scene", "sound"))
self.assertEqual(raised.exception.detail.code, "invalid_request")
diff --git a/tests/test_frontend.py b/tests/test_frontend.py
index d03329a0..07ea17e8 100644
--- a/tests/test_frontend.py
+++ b/tests/test_frontend.py
@@ -90,12 +90,12 @@ def test_query_modalities_use_real_capability_service_contracts(self):
return_value=service,
):
available = frontend._available_query_modalities(
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
)
self.assertEqual(
available,
- ("dialogue", "sound", "scene", "actor", "videoprism"),
+ ("speech", "sound", "scene", "actor", "action"),
)
def tearDown(self):
@@ -341,7 +341,7 @@ def check(command):
self.assertEqual(
available,
- ("dialogue", "sound", "scene", "videoprism"),
+ ("speech", "sound", "scene", "action"),
)
self.assertTrue(
all(
@@ -357,7 +357,7 @@ def test_scene_detail_control_is_conditional_and_defaults_to_balanced(self):
return_value=2.0,
) as selectbox:
selected = frontend._scene_sample_fps_control(
- ("dialogue", "scene"),
+ ("speech", "scene"),
disabled=False,
)
@@ -370,7 +370,7 @@ def test_scene_detail_control_is_conditional_and_defaults_to_balanced(self):
with patch.object(frontend.st, "selectbox") as selectbox:
selected = frontend._scene_sample_fps_control(
- ("dialogue",),
+ ("speech",),
disabled=False,
)
@@ -411,7 +411,7 @@ def test_videoprism_clip_control_is_conditional_and_configures_index(self):
return_value=4.0,
) as selectbox:
selected = frontend._videoprism_sample_fps_control(
- ("videoprism",),
+ ("action",),
disabled=False,
)
@@ -435,14 +435,14 @@ def test_videoprism_clip_control_is_conditional_and_configures_index(self):
frontend._run_indexing(
None,
{},
- ("videoprism",),
+ ("action",),
videoprism_sample_fps=selected,
)
command = jobs.submit_index.call_args.args[0]
self.assertEqual(
command.capability_options,
- {"videoprism": {"sample_fps": 4.0}},
+ {"action": {"sample_fps": 4.0}},
)
def test_indexing_omits_scene_sample_rate_without_scene(self):
@@ -456,7 +456,7 @@ def test_indexing_omits_scene_sample_rate_without_scene(self):
patch.object(frontend.st, "query_params", {}),
patch.object(frontend.st, "rerun"),
):
- frontend._run_indexing(None, {}, ("dialogue",))
+ frontend._run_indexing(None, {}, ("speech",))
command = jobs.submit_index.call_args.args[0]
self.assertIsNone(command.scene_sample_fps)
diff --git a/tests/test_frontend_app.py b/tests/test_frontend_app.py
index bb300601..f5383db7 100644
--- a/tests/test_frontend_app.py
+++ b/tests/test_frontend_app.py
@@ -161,7 +161,7 @@ def ready_status() -> IndexStatus:
snapshot_id=SNAPSHOT_ID,
media_count=1,
media_ids=(MEDIA_ID,),
- modalities=("dialogue", "sound", "scene", "actor", "videoprism"),
+ modalities=("speech", "sound", "scene", "actor", "action"),
),
)
@@ -269,26 +269,26 @@ def test_ready_page_rejects_empty_search_without_disabling_form(self):
self.assertEqual(app.warning[-1].value, "Enter a search query.")
self.assertEqual(jobs.submitted_searches, [])
- def test_ready_page_exposes_videoprism_as_temporal_action_search(self):
+ def test_ready_page_exposes_action_and_motion_search(self):
service = FrontendApplicationStub(self.root, ready_status())
jobs = FrontendJobStub()
app = self.app(service, jobs).run()
capability_picker = self.widget(app.multiselect, "Capabilities")
self.assertIn(
- "Temporal action search (VideoPrism)",
+ "Action and motion search",
capability_picker.options,
)
temporal_control = self.widget(app.selectbox, "Temporal clip length")
self.assertEqual(temporal_control.value, 2.0)
search_type = self.widget(app.selectbox, "Search type")
- search_type.select("videoprism")
+ search_type.select("action")
app.text_input(key="video_search_query").input("a person walks out")
self.widget(app.button, "Search").click()
app.run()
- self.assertEqual(jobs.submitted_searches[0].modalities, ("videoprism",))
+ self.assertEqual(jobs.submitted_searches[0].modalities, ("action",))
def test_running_index_keeps_one_preview_and_disables_mutations(self):
service = FrontendApplicationStub(self.root, ready_status())
diff --git a/tests/test_generation_manifest.py b/tests/test_generation_manifest.py
index 1e10efee..091d9f2a 100644
--- a/tests/test_generation_manifest.py
+++ b/tests/test_generation_manifest.py
@@ -35,7 +35,7 @@ def completed_manifest() -> dict:
"config_fingerprint": SHA256,
"execution_fingerprint": OTHER_SHA256,
"configuration": {
- "enabled_modalities": ["scene", "dialogue"],
+ "enabled_modalities": ["scene", "speech"],
"frame_stride": 1,
},
"models": {"runtime": {"requested": "cpu"}},
@@ -75,7 +75,7 @@ def completed_manifest() -> dict:
"failed_videos": [],
"interrupted_videos": [],
"processed_frames": 4,
- "record_counts": {"scene": 4, "dialogue": 2},
+ "record_counts": {"scene": 4, "speech": 2},
"store_size_bytes_at_commit": 4096,
}
@@ -162,8 +162,8 @@ def test_requires_one_consistent_completed_media_without_failures(self):
def test_record_counts_exactly_match_modalities_and_sizes_are_nonnegative(self):
invalid_counts = (
{"scene": 4},
- {"scene": 4, "dialogue": 2, "actor": 1},
- {"scene": -1, "dialogue": 2},
+ {"scene": 4, "speech": 2, "actor": 1},
+ {"scene": -1, "speech": 2},
)
for record_counts in invalid_counts:
with self.subTest(record_counts=record_counts):
diff --git a/tests/test_indexing.py b/tests/test_indexing.py
index 91766c19..b60b2fb4 100644
--- a/tests/test_indexing.py
+++ b/tests/test_indexing.py
@@ -16,9 +16,9 @@
CapabilityExecutor,
CapabilityPlugin,
)
-from vidxp.capabilities.dialogue.indexing import (
+from vidxp.capabilities.speech.indexing import (
build_dialogue_phrases,
- index_dialogue,
+ index_speech,
transcribe_video,
)
from vidxp.capabilities.scene.indexing import (
@@ -141,9 +141,9 @@ def test_transcript_indexing_batches_without_transcription(self):
split="test",
run_id="asr",
video_id="video-1",
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
capability_options={
- "dialogue": {"embedding_batch_size": 2},
+ "speech": {"embedding_batch_size": 2},
},
)
source = VideoSource(
@@ -158,15 +158,15 @@ def test_transcript_indexing_batches_without_transcription(self):
encoder = FakeEncoder()
with (
patch(
- "vidxp.capabilities.dialogue.indexing.get_embedder",
+ "vidxp.capabilities.speech.indexing.get_embedder",
return_value=encoder,
),
patch(
- "vidxp.capabilities.dialogue.indexing.transcribe_video",
+ "vidxp.capabilities.speech.indexing.transcribe_video",
side_effect=AssertionError("transcription was used"),
),
):
- stats = index_dialogue(
+ stats = index_speech(
source,
config=config,
storage=storage,
@@ -202,7 +202,7 @@ def test_silent_video_skips_dialogue_before_loading_whisper(self):
"silent.mp4",
config=IndexConfig(
video_id="video-1",
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
),
cancellation=CancellationToken(),
runtime=self.runtime(),
diff --git a/tests/test_job_contracts.py b/tests/test_job_contracts.py
index 2b152ef8..cb0d51db 100644
--- a/tests/test_job_contracts.py
+++ b/tests/test_job_contracts.py
@@ -490,12 +490,12 @@ def test_failed_model_preparation_error_round_trips_through_job_service(self):
category=ErrorCategory.unavailable,
message="The model download failed after three attempts.",
details={
- "capability": "dialogue.transcription",
+ "capability": "speech.transcription",
"model": "publisher/model",
"attempts": 3,
"reason": "ConnectionError",
"partial_files_preserved": True,
- "remediation": "vidxp prepare --modalities dialogue",
+ "remediation": "vidxp prepare --modalities speech",
},
retryable=True,
)
diff --git a/tests/test_local_probe.py b/tests/test_local_probe.py
index e7bd6364..8c225493 100644
--- a/tests/test_local_probe.py
+++ b/tests/test_local_probe.py
@@ -40,10 +40,10 @@ def build(self, **overrides):
"vidxp.local_probe._installed_search_capabilities",
return_value=[
"actor",
- "dialogue",
+ "speech",
"scene",
"sound",
- "videoprism",
+ "action",
],
),
patch(
@@ -92,7 +92,7 @@ def test_probe_reports_stable_identity_and_contract_compatibility(self):
)
self.assertEqual(
payload["search_capabilities"],
- ["actor", "dialogue", "scene", "sound", "videoprism"],
+ ["actor", "speech", "scene", "sound", "action"],
)
self.assertTrue(all(surface["launchable"] for surface in payload["surfaces"].values()))
diff --git a/tests/test_mcp.py b/tests/test_mcp.py
index d480138d..b5a06ae1 100644
--- a/tests/test_mcp.py
+++ b/tests/test_mcp.py
@@ -1108,6 +1108,7 @@ def test_stdio_help_and_config_are_ready_to_copy(self):
config = stdio_client_config(
command=r"C:\VidXP\vidxp-mcp.exe",
repository="library",
+ environment={},
)
self.assertEqual(
config,
@@ -1140,6 +1141,24 @@ def test_stdio_help_and_config_are_ready_to_copy(self):
["--repository", "library"],
)
+ def test_stdio_config_carries_only_local_query_runtime_environment(self):
+ config = stdio_client_config(
+ command="vidxp-mcp",
+ environment={
+ "VIDXP_SLM_BASE_URL": "http://127.0.0.1:11434/v1",
+ "VIDXP_SLM_MODEL": "qwen3.5:4b-q4_K_M",
+ "VIDXP_HTTP_STATIC_BEARER_TOKEN": "must-not-leak",
+ },
+ )
+
+ self.assertEqual(
+ config["mcpServers"]["vidxp"]["env"],
+ {
+ "VIDXP_SLM_BASE_URL": "http://127.0.0.1:11434/v1",
+ "VIDXP_SLM_MODEL": "qwen3.5:4b-q4_K_M",
+ },
+ )
+
def test_stdio_check_performs_handshake_and_tool_probe(self):
output = io.StringIO()
with TemporaryDirectory() as directory:
@@ -1305,7 +1324,7 @@ async def test_query_video_submits_the_shared_durable_command(self):
"command": {
"question": "What happens after the taxi arrives?",
"media_id": MEDIA_ID,
- "modalities": ["scene", "dialogue"],
+ "modalities": ["scene", "speech"],
},
"idempotency_key": "agent-query-0001",
},
@@ -1318,7 +1337,7 @@ async def test_query_video_submits_the_shared_durable_command(self):
QueryVideoCommand(
question="What happens after the taxi arrives?",
media_id=MEDIA_ID,
- modalities=("scene", "dialogue"),
+ modalities=("scene", "speech"),
evidence_delivery=InitialEvidenceDeliveryPolicy(
mode=EvidenceDeliveryMode.none
),
@@ -1409,7 +1428,7 @@ async def test_failed_model_preparation_job_is_structured_over_mcp(self):
details={
"model": "publisher/model",
"partial_files_preserved": True,
- "remediation": "vidxp prepare --modalities dialogue",
+ "remediation": "vidxp prepare --modalities speech",
},
retryable=True,
),
@@ -2734,7 +2753,7 @@ async def test_stdio_entrypoint_serves_the_filesystem_aware_surface(self):
)
self.assertEqual(
[item["name"] for item in result.structured_content["items"]],
- ["dialogue", "sound", "scene", "actor", "videoprism"],
+ ["speech", "sound", "scene", "actor", "action"],
)
async def test_streamable_http_works_with_the_official_remote_client(self):
diff --git a/tests/test_models.py b/tests/test_models.py
index ea361bee..c0feda3f 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -16,8 +16,8 @@
from pydantic import ValidationError
from vidxp.capabilities.contracts import CapabilityDefinition
-from vidxp.capabilities.dialogue import models as dialogue_models
-from vidxp.capabilities.dialogue.specs import (
+from vidxp.capabilities.speech import models as speech_models
+from vidxp.capabilities.speech.specs import (
FASTER_WHISPER_MODEL,
QWEN3_EMBEDDING_MODEL,
)
@@ -44,7 +44,7 @@
model_artifact_path,
)
from vidxp.runtime import ModelRuntime, resolve_backends
-from vidxp.settings import VidXPSettings
+from vidxp.settings import DEFAULT_LOCAL_QUERY_MODEL, VidXPSettings
class ModelTests(unittest.TestCase):
@@ -84,8 +84,8 @@ def test_model_runtime_reuses_one_provider_instance(self):
sys.modules,
{"sentence_transformers": fake_module},
):
- first = dialogue_models.get_embedder(runtime)
- second = dialogue_models.get_embedder(runtime)
+ first = speech_models.get_embedder(runtime)
+ second = speech_models.get_embedder(runtime)
self.assertIs(first, second)
constructor.assert_called_once_with(
@@ -95,7 +95,7 @@ def test_model_runtime_reuses_one_provider_instance(self):
local_files_only=True,
)
self.assertEqual(
- runtime.describe()["compute_precision"]["dialogue.embedding"],
+ runtime.describe()["compute_precision"]["speech.embedding"],
"bfloat16",
)
@@ -109,7 +109,7 @@ def load(value):
keys = (
ModelKey("scene", "one", "one", "1", "cpu"),
- ModelKey("dialogue", "two", "two", "1", "cpu"),
+ ModelKey("speech", "two", "two", "1", "cpu"),
)
with ThreadPoolExecutor(max_workers=2) as pool:
futures = [
@@ -702,7 +702,7 @@ def test_transcript_only_excludes_transcription_provider(self):
distributions = {
requirement.name
for requirement in registry.requirements_for(
- ("dialogue",),
+ ("speech",),
source=source,
)
}
@@ -732,7 +732,7 @@ def test_requirement_files_are_dependency_contract(self):
"faster-whisper import",
{
check.label
- for check in registry.runtime_checks_for(("dialogue",))
+ for check in registry.runtime_checks_for(("speech",))
},
)
@@ -761,8 +761,13 @@ def test_server_runtime_and_external_allowlist_are_explicit(self):
self.assertEqual(settings.runtime_backend, "cuda:0")
self.assertNotIn("database_url", VidXPSettings.model_fields)
self.assertNotIn("chroma_server_url", VidXPSettings.model_fields)
- with self.assertRaises(ValidationError):
- VidXPSettings(slm_base_url="http://localhost:11434/v1")
+ default_slm = VidXPSettings(
+ slm_base_url="http://localhost:11434/v1"
+ )
+ self.assertEqual(
+ default_slm.slm_model,
+ DEFAULT_LOCAL_QUERY_MODEL,
+ )
with self.assertRaises(ValidationError):
VidXPSettings(
slm_base_url="https://ollama.com/v1",
@@ -779,6 +784,9 @@ def test_server_runtime_and_external_allowlist_are_explicit(self):
)
self.assertEqual(slm.slm_model, "evaluated-model")
+ with self.assertRaises(ValidationError):
+ VidXPSettings(slm_model=DEFAULT_LOCAL_QUERY_MODEL)
+
def test_only_optional_slm_environment_values_ignore_empty_strings(self):
with patch.dict(
os.environ,
@@ -793,6 +801,18 @@ def test_only_optional_slm_environment_values_ignore_empty_strings(self):
self.assertIsNone(settings.slm_base_url)
self.assertIsNone(settings.slm_model)
+ with patch.dict(
+ os.environ,
+ {
+ "VIDXP_SLM_BASE_URL": "http://localhost:11434/v1",
+ "VIDXP_SLM_MODEL": "",
+ },
+ clear=True,
+ ):
+ settings = VidXPSettings(_env_file=None)
+
+ self.assertEqual(settings.slm_model, DEFAULT_LOCAL_QUERY_MODEL)
+
with (
patch.dict(
os.environ,
diff --git a/tests/test_ollama_query.py b/tests/test_ollama_query.py
index 61cf331a..b154e3da 100644
--- a/tests/test_ollama_query.py
+++ b/tests/test_ollama_query.py
@@ -27,7 +27,7 @@ def test_structured_planning_and_synthesis_use_the_provider_contract(self):
"steps": [
{
"kind": "search_moments",
- "modality": "dialogue",
+ "modality": "speech",
"query": "taxi arrival",
}
]
@@ -88,7 +88,7 @@ def handler(request: httpx.Request) -> httpx.Response:
end=2,
score=-0.1,
raw_distance=0.1,
- modality="dialogue",
+ modality="speech",
source_id="dialogue:1",
metadata={"text": "the taxi arrived"},
)
@@ -97,7 +97,7 @@ def handler(request: httpx.Request) -> httpx.Response:
snapshot_id=SNAPSHOT_ID,
media_id=MEDIA_ID,
generation_id=GENERATION_ID,
- modality="dialogue",
+ modality="speech",
source_id="dialogue:1",
start=1,
end=2,
@@ -108,7 +108,7 @@ def handler(request: httpx.Request) -> httpx.Response:
plan = model.plan(
QueryPlanningRequest(
question="When did the taxi arrive?",
- allowed_modalities=("dialogue",),
+ allowed_modalities=("speech",),
)
)
answer = model.synthesize(
@@ -130,6 +130,7 @@ def handler(request: httpx.Request) -> httpx.Response:
"json_schema",
)
self.assertEqual(request["model"], "contract-model")
+ self.assertEqual(request["reasoning_effort"], "none")
if __name__ == "__main__":
diff --git a/tests/test_public_path_contracts.py b/tests/test_public_path_contracts.py
index 66234734..4a65a213 100644
--- a/tests/test_public_path_contracts.py
+++ b/tests/test_public_path_contracts.py
@@ -94,7 +94,7 @@ def test_scene_sampling_requires_a_positive_scene_request(self):
with self.assertRaises(ValidationError):
CreateIndexCommand(
media_id=MEDIA_ID,
- modalities=("dialogue",),
+ modalities=("speech",),
scene_sample_fps=1.0,
)
with self.assertRaises(ValidationError):
diff --git a/tests/test_query_service.py b/tests/test_query_service.py
index a57029dd..e1fa3cd9 100644
--- a/tests/test_query_service.py
+++ b/tests/test_query_service.py
@@ -111,14 +111,14 @@ def test_invalid_model_plan_falls_back_to_complete_closed_plan(self):
plan, reason = service.plan(
self.command,
- search_modalities=("scene", "dialogue"),
+ search_modalities=("scene", "speech"),
actor_overview=False,
)
self.assertEqual(reason, "query_plan_rejected")
self.assertEqual(
[step.modality for step in plan.steps],
- ["scene", "dialogue"],
+ ["scene", "speech"],
)
def test_provider_failure_uses_deterministic_retrieval_plan(self):
@@ -184,7 +184,7 @@ def test_unknown_citation_is_rejected(self):
plan = QueryPlan(
steps=(
SearchMomentsPlanStep(
- modality="dialogue",
+ modality="speech",
query="taxi",
),
)
@@ -202,8 +202,8 @@ def test_unknown_citation_is_rejected(self):
),
)
)
- atomic = result(text="the taxi arrived", modality="dialogue")
- fused_result = fused("dialogue", atomic)
+ atomic = result(text="the taxi arrived", modality="speech")
+ fused_result = fused("speech", atomic)
evidence = service.evidence(
snapshot=self.snapshot,
fused=fused_result,
@@ -225,14 +225,14 @@ def test_valid_textual_citation_is_reconstructed_by_the_application(self):
plan = QueryPlan(
steps=(
SearchMomentsPlanStep(
- modality="dialogue",
+ modality="speech",
query="taxi",
),
)
)
- atomic = result(text="the taxi arrived", modality="dialogue")
+ atomic = result(text="the taxi arrived", modality="speech")
service = GroundedQueryService()
- fused_result = fused("dialogue", atomic)
+ fused_result = fused("speech", atomic)
evidence = service.evidence(
snapshot=self.snapshot,
fused=fused_result,
@@ -265,14 +265,14 @@ def test_valid_textual_citation_is_reconstructed_by_the_application(self):
)
def test_generated_answer_preserves_rejected_plan_provenance(self):
- atomic = result(text="the taxi arrived", modality="dialogue")
- fused_result = fused("dialogue", atomic)
+ atomic = result(text="the taxi arrived", modality="speech")
+ fused_result = fused("speech", atomic)
service = GroundedQueryService(
FakeQueryModel(
QueryPlan(
steps=(
SearchMomentsPlanStep(
- modality="dialogue",
+ modality="speech",
query="taxi",
),
)
@@ -331,7 +331,7 @@ def test_evidence_is_bounded_to_retained_fused_moments(self):
atomic = SearchResult(
query_id="dialogue:many",
query="taxi",
- modality="dialogue",
+ modality="speech",
hits=tuple(
SearchHit(
rank=index + 1,
@@ -342,7 +342,7 @@ def test_evidence_is_bounded_to_retained_fused_moments(self):
end=float(index * 2 + 1),
score=-float(index + 1),
raw_distance=float(index + 1),
- modality="dialogue",
+ modality="speech",
source_id=f"dialogue:{index}",
metadata={"text": f"line {index}"},
)
@@ -351,7 +351,7 @@ def test_evidence_is_bounded_to_retained_fused_moments(self):
)
fused_result = fuse_search_results(
query="taxi",
- requested_modalities=("dialogue",),
+ requested_modalities=("speech",),
results=(atomic,),
top_k=201,
)
diff --git a/tests/test_runner.py b/tests/test_runner.py
index 852d67e0..0296e2e7 100644
--- a/tests/test_runner.py
+++ b/tests/test_runner.py
@@ -11,7 +11,7 @@
)
from vidxp.core.manifest import COMPLETION_FILE, ManifestStore
from vidxp.capabilities.contracts import CapabilityIndexResult
-from vidxp.capabilities.dialogue.specs import FASTER_WHISPER_MODEL
+from vidxp.capabilities.speech.specs import FASTER_WHISPER_MODEL
from vidxp.core.runner import (
_RunLock,
index_video as _index_video,
@@ -364,7 +364,7 @@ def successful_indexer(source, *, config, **_):
def test_transcript_only_run_does_not_request_transcription_dependencies(self):
with TemporaryDirectory() as directory:
- config = self._config(directory, ("dialogue",))
+ config = self._config(directory, ("speech",))
source = VideoSource(
video_id="video-1",
transcript=(
@@ -379,7 +379,7 @@ def test_transcript_only_run_does_not_request_transcription_dependencies(self):
dependency_check,
),
patch(
- "vidxp.capabilities.dialogue.operations.index_dialogue",
+ "vidxp.capabilities.speech.operations.index_speech",
return_value={"dialogue_phrases": 1},
),
patch(
@@ -392,7 +392,7 @@ def test_transcript_only_run_does_not_request_transcription_dependencies(self):
dependency_check.assert_called_once()
self.assertEqual(
dependency_check.call_args.args,
- (("dialogue",),),
+ (("speech",),),
)
self.assertIs(dependency_check.call_args.kwargs["source"], source)
@@ -400,7 +400,7 @@ def test_manifest_adds_transcription_model_when_run_later_needs_it(self):
with TemporaryDirectory() as directory:
path = Path(directory) / "second.mp4"
path.write_bytes(b"video")
- config = self._config(directory, ("dialogue",))
+ config = self._config(directory, ("speech",))
supplied = VideoSource(
video_id="video-1",
transcript=(
@@ -411,7 +411,7 @@ def test_manifest_adds_transcription_model_when_run_later_needs_it(self):
with (
patch("vidxp.core.runner.require_dependencies"),
patch(
- "vidxp.capabilities.dialogue.operations.index_dialogue",
+ "vidxp.capabilities.speech.operations.index_speech",
return_value={"dialogue_phrases": 1},
),
patch(
@@ -473,7 +473,7 @@ def test_changed_supplied_transcript_invalidates_same_video_input(self):
with TemporaryDirectory() as directory:
path = Path(directory) / "video.mp4"
path.write_bytes(b"same-video")
- config = self._config(directory, ("dialogue",))
+ config = self._config(directory, ("speech",))
first = VideoSource(
video_id="video-1",
path=path,
@@ -491,7 +491,7 @@ def test_changed_supplied_transcript_invalidates_same_video_input(self):
with (
patch("vidxp.core.runner.require_dependencies"),
patch(
- "vidxp.capabilities.dialogue.operations.index_dialogue",
+ "vidxp.capabilities.speech.operations.index_speech",
return_value={"dialogue_phrases": 1},
),
patch(
@@ -514,12 +514,12 @@ def test_reset_clears_every_collection_not_only_enabled_modalities(self):
{"text": "hello", "start": 0.0, "end": 1.0},
),
)
- config = self._config(directory, ("dialogue",))
+ config = self._config(directory, ("speech",))
storage = FakeStorage()
with (
patch("vidxp.core.runner.require_dependencies"),
patch(
- "vidxp.capabilities.dialogue.operations.index_dialogue",
+ "vidxp.capabilities.speech.operations.index_speech",
return_value={"dialogue_phrases": 1},
),
patch(
diff --git a/tests/test_search.py b/tests/test_search.py
index be7c58ad..accb7d84 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -6,8 +6,8 @@
from unittest.mock import Mock
import numpy as np
-from vidxp.capabilities.dialogue.operations import search_dialogue
-from vidxp.capabilities.dialogue.operations import dialogue_embedding
+from vidxp.capabilities.speech.operations import search_speech
+from vidxp.capabilities.speech.operations import speech_embedding
from vidxp.capabilities.schemas import SearchResult
from vidxp.capabilities.search import (
distance_to_score,
@@ -48,7 +48,7 @@ def dialogue_row(source_id, distance, video_id=MEDIA_ID):
"end": 2.0,
"text": "fresh bread",
"phrase_id": 3,
- "modality": "dialogue",
+ "modality": "speech",
},
}
@@ -59,7 +59,7 @@ def setUp(self):
dataset="sample",
split="test",
run_id="run-1",
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
)
self.runtime = ModelRuntime(
VidXPSettings(
@@ -77,10 +77,10 @@ def test_top_k_filter_order_distance_and_score_are_preserved(self):
]
)
with patch(
- "vidxp.capabilities.dialogue.operations.dialogue_embedding",
+ "vidxp.capabilities.speech.operations.speech_embedding",
return_value=[0.5, 0.25],
):
- result = search_dialogue(
+ result = search_speech(
"fresh bread",
config=self.config,
runtime=self.runtime,
@@ -116,10 +116,10 @@ def test_dialogue_query_uses_model_owned_query_prompt(self):
encoder = Mock()
encoder.encode_query.return_value = np.asarray([[0.5, 0.25]])
with patch(
- "vidxp.capabilities.dialogue.operations.get_embedder",
+ "vidxp.capabilities.speech.operations.get_embedder",
return_value=encoder,
):
- vector = dialogue_embedding("fresh bread", self.config, self.runtime)
+ vector = speech_embedding("fresh bread", self.config, self.runtime)
self.assertEqual(vector, [0.5, 0.25])
encoder.encode_query.assert_called_once_with(
@@ -129,15 +129,15 @@ def test_dialogue_query_uses_model_owned_query_prompt(self):
)
def test_generated_query_ids_are_scoped_to_the_benchmark_run(self):
- first = stable_query_id("fresh bread", "dialogue", self.config)
+ first = stable_query_id("fresh bread", "speech", self.config)
second = stable_query_id(
"fresh bread",
- "dialogue",
+ "speech",
IndexConfig(
dataset="sample",
split="test",
run_id="run-2",
- enabled_modalities=("dialogue",),
+ enabled_modalities=("speech",),
),
)
@@ -145,7 +145,7 @@ def test_generated_query_ids_are_scoped_to_the_benchmark_run(self):
def test_nonpositive_top_k_is_rejected_before_querying(self):
with self.assertRaisesRegex(ValueError, "top_k"):
- search_dialogue(
+ search_speech(
"query",
config=self.config,
runtime=self.runtime,
@@ -165,12 +165,12 @@ def test_old_metadata_requires_an_explicit_reindex(self):
)
with (
patch(
- "vidxp.capabilities.dialogue.operations.dialogue_embedding",
+ "vidxp.capabilities.speech.operations.speech_embedding",
return_value=[0.5],
),
self.assertRaisesRegex(IndexSchemaError, "must be rebuilt"),
):
- search_dialogue(
+ search_speech(
"query",
config=self.config,
runtime=self.runtime,
@@ -181,7 +181,7 @@ def test_generic_serializer_keeps_empty_queries_and_is_deterministic(self):
empty = SearchResult(
query_id="q-empty",
query="nothing",
- modality="dialogue",
+ modality="speech",
hits=(),
)
with TemporaryDirectory() as directory:
@@ -196,7 +196,7 @@ def test_serializer_rejects_duplicate_query_ids(self):
duplicate = SearchResult(
query_id="q1",
query="query",
- modality="dialogue",
+ modality="speech",
hits=(),
)
with self.assertRaisesRegex(ValueError, "duplicate"):
diff --git a/tests/test_search_fusion.py b/tests/test_search_fusion.py
index 6d83e53d..622e0210 100644
--- a/tests/test_search_fusion.py
+++ b/tests/test_search_fusion.py
@@ -43,13 +43,13 @@ def test_rrf_counts_only_the_best_rank_per_modality_in_a_moment(self):
dialogue = SearchResult(
query_id="dialogue:q",
query="taxi",
- modality="dialogue",
- hits=(hit("dialogue", 1, 2.5, 3.5, "dialogue:1"),),
+ modality="speech",
+ hits=(hit("speech", 1, 2.5, 3.5, "dialogue:1"),),
)
result = fuse_search_results(
query="taxi",
- requested_modalities=("scene", "dialogue"),
+ requested_modalities=("scene", "speech"),
results=(scene, dialogue),
)
@@ -70,12 +70,12 @@ def test_result_order_does_not_change_fusion_identity_or_output(self):
dialogue = SearchResult(
query_id="dialogue:q",
query="taxi",
- modality="dialogue",
- hits=(hit("dialogue", 1, 1, 2, "dialogue:1"),),
+ modality="speech",
+ hits=(hit("speech", 1, 1, 2, "dialogue:1"),),
)
arguments = {
"query": "taxi",
- "requested_modalities": ("scene", "dialogue"),
+ "requested_modalities": ("scene", "speech"),
}
forward = fuse_search_results(
diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py
index 1057dd99..1288eeea 100644
--- a/tests/test_snapshots.py
+++ b/tests/test_snapshots.py
@@ -26,8 +26,8 @@ def generation_reference(self, **changes):
"manifest_sha256": SHA256,
"input_sha256": OTHER_SHA256,
"config_fingerprint": SHA256,
- "modalities": ("dialogue", "scene"),
- "record_counts": {"dialogue": 3, "scene": 4},
+ "modalities": ("speech", "scene"),
+ "record_counts": {"speech": 3, "scene": 4},
"store_size_bytes_at_commit": 2048,
}
values.update(changes)
@@ -41,7 +41,7 @@ def index_snapshot(self, **changes):
"config_fingerprint": SHA256,
"configuration": {
"device": "cpu",
- "modalities": ["dialogue", "scene"],
+ "modalities": ["speech", "scene"],
"options": {"batch_size": 8},
},
"generations": {reference.media_id: reference},
@@ -139,7 +139,7 @@ def test_generation_counts_must_match_modalities(self):
self.generation_reference(record_counts={"scene": 1})
with self.assertRaises(ValidationError):
self.generation_reference(
- record_counts={"dialogue": -1, "scene": 1}
+ record_counts={"speech": -1, "scene": 1}
)
def test_generation_store_size_may_be_unknown(self):
diff --git a/tests/test_storage.py b/tests/test_storage.py
index 3f3b8ec9..d2989fa8 100644
--- a/tests/test_storage.py
+++ b/tests/test_storage.py
@@ -86,7 +86,7 @@ def fake_storage(config, collection):
storage._create = True
storage._collections = {}
storage._names = {
- "dialogue": "dialogue",
+ "speech": "speech",
"scene": "scene",
"actor": "actor",
}
diff --git a/tests/test_upload_service.py b/tests/test_upload_service.py
index be9570c5..5f36872c 100644
--- a/tests/test_upload_service.py
+++ b/tests/test_upload_service.py
@@ -634,7 +634,7 @@ def test_upload_session_idempotency_validates_shared_request_contract(
lambda: service.create_upload_session(
principal=owner,
request_key="a" * 64,
- index_modalities=("dialogue",),
+ index_modalities=("speech",),
),
lambda: service.create_upload_session(
principal=Principal(subject="other", client_id="client-a"),
@@ -1773,12 +1773,12 @@ def test_index_retry_recovery_resubmits_exact_persisted_command(
retry_job_id = uuid4().hex
command = CreateIndexCommand(
media_id=media_id,
- modalities=("dialogue", "scene"),
+ modalities=("speech", "scene"),
frame_stride=7,
scene_sample_fps=2.5,
capability_options={
"scene": {"batch_size": 3},
- "dialogue": {"language": "ur"},
+ "speech": {"language": "ur"},
},
)
diff --git a/tests/test_videoprism.py b/tests/test_videoprism.py
index 6b55cd57..a7140eb9 100644
--- a/tests/test_videoprism.py
+++ b/tests/test_videoprism.py
@@ -3,15 +3,15 @@
from pydantic import ValidationError
-from vidxp.capabilities.videoprism.config import VideoPrismConfig
-from vidxp.capabilities.videoprism.indexing import (
+from vidxp.capabilities.action.config import VideoPrismConfig
+from vidxp.capabilities.action.indexing import (
CLIP_FRAMES,
VISUAL_PROCESSOR,
VideoPrismIndexState,
process_videoprism_samples,
)
-from vidxp.capabilities.videoprism.models import normalize_pooled_output
-from vidxp.capabilities.videoprism.specs import VIDEOPRISM_MODEL
+from vidxp.capabilities.action.models import normalize_pooled_output
+from vidxp.capabilities.action.specs import VIDEOPRISM_MODEL
from vidxp.core.contracts import CancellationToken, IndexConfig
from vidxp.core.video import FrameSample, VideoInfo
@@ -33,7 +33,7 @@ def test_config_rejects_invalid_sampling(self):
def test_streaming_index_groups_clips_and_pads_only_the_tail(self):
config = IndexConfig(
video_id="video-1",
- enabled_modalities=("videoprism",),
+ enabled_modalities=("action",),
)
info = VideoInfo(30.0, 270, 9.0, 2, 2)
samples = [
@@ -47,7 +47,7 @@ def test_streaming_index_groups_clips_and_pads_only_the_tail(self):
)
with patch(
- "vidxp.capabilities.videoprism.indexing.encode_video_clips",
+ "vidxp.capabilities.action.indexing.encode_video_clips",
side_effect=lambda clips, _provider: [[0.1] for _ in clips],
) as encode:
process_videoprism_samples(
diff --git a/utils/verify_runtime.py b/utils/verify_runtime.py
index 1ca099b5..23cdaab6 100644
--- a/utils/verify_runtime.py
+++ b/utils/verify_runtime.py
@@ -15,7 +15,7 @@ def require(condition: bool, message: str) -> None:
def verify_minimal(executable: str) -> None:
from vidxp.capabilities.registry import create_capability_registry
- required_capabilities = {"dialogue", "scene", "actor"}
+ required_capabilities = {"speech", "scene", "actor"}
require(
required_capabilities.issubset(create_capability_registry().names()),
"minimal wheel does not expose the expected capability registry",
diff --git a/uv.lock b/uv.lock
index f1867cd7..7e6a9c9e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -4577,6 +4577,19 @@ dependencies = [
]
[package.optional-dependencies]
+action = [
+ { name = "chromadb" },
+ { name = "huggingface-hub" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
+ { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
+ { name = "opencv-python-headless" },
+ { name = "psutil" },
+ { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
+ { name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
+ { name = "transformers" },
+]
actor = [
{ name = "chromadb" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
@@ -4610,13 +4623,6 @@ all = [
benchmarks = [
{ name = "srt" },
]
-dialogue = [
- { name = "chromadb" },
- { name = "faster-whisper" },
- { name = "huggingface-hub" },
- { name = "psutil" },
- { name = "sentence-transformers" },
-]
frontend = [
{ name = "streamlit" },
]
@@ -4717,6 +4723,13 @@ sound = [
{ name = "torchaudio", version = "2.11.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "transformers" },
]
+speech = [
+ { name = "chromadb" },
+ { name = "faster-whisper" },
+ { name = "huggingface-hub" },
+ { name = "psutil" },
+ { name = "sentence-transformers" },
+]
storage = [
{ name = "chromadb" },
{ name = "psutil" },
@@ -4725,19 +4738,6 @@ test = [
{ name = "httpx" },
{ name = "pytest" },
]
-videoprism = [
- { name = "chromadb" },
- { name = "huggingface-hub" },
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
- { name = "opencv-python-headless" },
- { name = "psutil" },
- { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
- { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
- { name = "torchvision", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
- { name = "torchvision", version = "0.28.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
- { name = "transformers" },
-]
[package.metadata]
requires-dist = [
@@ -4749,50 +4749,50 @@ requires-dist = [
{ name = "av", marker = "extra == 'local-worker'", specifier = ">=18,<19" },
{ name = "av", marker = "extra == 'server-worker'", specifier = ">=18,<19" },
{ name = "av", marker = "extra == 'sound'", specifier = ">=18,<19" },
+ { name = "chromadb", marker = "extra == 'action'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'actor'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'all'", specifier = ">=1.5.9,<2" },
- { name = "chromadb", marker = "extra == 'dialogue'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'local-worker'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'scene'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'sound'", specifier = ">=1.5.9,<2" },
+ { name = "chromadb", marker = "extra == 'speech'", specifier = ">=1.5.9,<2" },
{ name = "chromadb", marker = "extra == 'storage'", specifier = ">=1.5.9,<2" },
- { name = "chromadb", marker = "extra == 'videoprism'", specifier = ">=1.5.9,<2" },
{ name = "chromadb-client", marker = "extra == 'server-worker'", specifier = ">=1.5.9,<2" },
{ name = "dbos", specifier = ">=2.28,<3" },
{ name = "fastapi", marker = "extra == 'server'", specifier = ">=0.140.13,<0.141" },
{ name = "fastapi", marker = "extra == 'server-worker'", specifier = ">=0.140.13,<0.141" },
{ name = "faster-whisper", marker = "extra == 'all'", specifier = ">=1.2.1,<2" },
- { name = "faster-whisper", marker = "extra == 'dialogue'", specifier = ">=1.2.1,<2" },
{ name = "faster-whisper", marker = "extra == 'local-worker'", specifier = ">=1.2.1,<2" },
{ name = "faster-whisper", marker = "extra == 'server-worker'", specifier = ">=1.2.1,<2" },
+ { name = "faster-whisper", marker = "extra == 'speech'", specifier = ">=1.2.1,<2" },
{ name = "filelock", specifier = ">=3.32,<4" },
{ name = "httpx", marker = "extra == 'test'", specifier = ">=0.28.1,<0.29" },
+ { name = "huggingface-hub", marker = "extra == 'action'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'all'", specifier = ">=1.25.1,<2" },
- { name = "huggingface-hub", marker = "extra == 'dialogue'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'local-worker'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'scene'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'server-worker'", specifier = ">=1.25.1,<2" },
{ name = "huggingface-hub", marker = "extra == 'sound'", specifier = ">=1.25.1,<2" },
- { name = "huggingface-hub", marker = "extra == 'videoprism'", specifier = ">=1.25.1,<2" },
+ { name = "huggingface-hub", marker = "extra == 'speech'", specifier = ">=1.25.1,<2" },
{ name = "matplotlib", marker = "extra == 'all'", specifier = ">=3.10,<4" },
{ name = "matplotlib", marker = "extra == 'local-worker'", specifier = ">=3.10,<4" },
{ name = "matplotlib", marker = "extra == 'server-worker'", specifier = ">=3.10,<4" },
{ name = "matplotlib", marker = "extra == 'sound'", specifier = ">=3.10,<4" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = ">=2.0,<3" },
{ name = "mcp", marker = "extra == 'server'", specifier = ">=2.0,<3" },
+ { name = "numpy", marker = "extra == 'action'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'actor'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'all'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'local-worker'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'scene'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'server-worker'", specifier = ">=2.3,<3" },
{ name = "numpy", marker = "extra == 'sound'", specifier = ">=2.3,<3" },
- { name = "numpy", marker = "extra == 'videoprism'", specifier = ">=2.3,<3" },
+ { name = "opencv-python-headless", marker = "extra == 'action'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'actor'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'all'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'local-worker'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'scene'", specifier = ">=5.0.0.93,<6" },
{ name = "opencv-python-headless", marker = "extra == 'server-worker'", specifier = ">=5.0.0.93,<6" },
- { name = "opencv-python-headless", marker = "extra == 'videoprism'", specifier = ">=5.0.0.93,<6" },
{ name = "packaging", specifier = ">=26.2,<27" },
{ name = "pillow", marker = "extra == 'all'", specifier = ">=12.3,<13" },
{ name = "pillow", marker = "extra == 'local-worker'", specifier = ">=12.3,<13" },
@@ -4803,16 +4803,16 @@ requires-dist = [
{ name = "pooch", marker = "extra == 'all'", specifier = ">=1.9,<2" },
{ name = "pooch", marker = "extra == 'local-worker'", specifier = ">=1.9,<2" },
{ name = "pooch", marker = "extra == 'server-worker'", specifier = ">=1.9,<2" },
+ { name = "psutil", marker = "extra == 'action'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'actor'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'all'", specifier = ">=7.2.2,<8" },
- { name = "psutil", marker = "extra == 'dialogue'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'local-worker'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'scene'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'server'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'server-worker'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'sound'", specifier = ">=7.2.2,<8" },
+ { name = "psutil", marker = "extra == 'speech'", specifier = ">=7.2.2,<8" },
{ name = "psutil", marker = "extra == 'storage'", specifier = ">=7.2.2,<8" },
- { name = "psutil", marker = "extra == 'videoprism'", specifier = ">=7.2.2,<8" },
{ name = "psycopg", extras = ["binary"], marker = "extra == 'server'", specifier = ">=3.3.4,<4" },
{ name = "psycopg", extras = ["binary"], marker = "extra == 'server-worker'", specifier = ">=3.3.4,<4" },
{ name = "pydantic", specifier = ">=2.13.4,<3" },
@@ -4828,9 +4828,9 @@ requires-dist = [
{ name = "python-multipart", marker = "extra == 'server-worker'", specifier = ">=0.0.32,<0.1" },
{ name = "rich", specifier = ">=15,<16" },
{ name = "sentence-transformers", marker = "extra == 'all'", specifier = ">=5.6.1,<6" },
- { name = "sentence-transformers", marker = "extra == 'dialogue'", specifier = ">=5.6.1,<6" },
{ name = "sentence-transformers", marker = "extra == 'local-worker'", specifier = ">=5.6.1,<6" },
{ name = "sentence-transformers", marker = "extra == 'server-worker'", specifier = ">=5.6.1,<6" },
+ { name = "sentence-transformers", marker = "extra == 'speech'", specifier = ">=5.6.1,<6" },
{ name = "sqlalchemy", specifier = ">=2.0.51,<2.1" },
{ name = "srt", marker = "extra == 'benchmarks'", specifier = ">=3.5,<4" },
{ name = "streamlit", marker = "extra == 'frontend'", specifier = ">=1.60,<2" },
@@ -4838,18 +4838,18 @@ requires-dist = [
{ name = "timm", marker = "extra == 'local-worker'", specifier = ">=1.0.20,<2" },
{ name = "timm", marker = "extra == 'server-worker'", specifier = ">=1.0.20,<2" },
{ name = "timm", marker = "extra == 'sound'", specifier = ">=1.0.20,<2" },
+ { name = "torch", marker = "(sys_platform == 'linux' and extra == 'action') or (sys_platform == 'win32' and extra == 'action')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'scene') or (sys_platform == 'win32' and extra == 'scene')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torch", marker = "(sys_platform == 'linux' and extra == 'sound') or (sys_platform == 'win32' and extra == 'sound')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
- { name = "torch", marker = "(sys_platform == 'linux' and extra == 'videoprism') or (sys_platform == 'win32' and extra == 'videoprism')", specifier = ">=2.13,<3", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'action'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'all'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'local-worker'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'scene'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'server-worker'", specifier = ">=2.13,<3" },
{ name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'sound'", specifier = ">=2.13,<3" },
- { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'videoprism'", specifier = ">=2.13,<3" },
{ name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchaudio", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=2.11,<2.12", index = "https://download.pytorch.org/whl/cpu" },
@@ -4858,25 +4858,25 @@ requires-dist = [
{ name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'local-worker'", specifier = ">=2.11,<2.12" },
{ name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'server-worker'", specifier = ">=2.11,<2.12" },
{ name = "torchaudio", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'sound'", specifier = ">=2.11,<2.12" },
+ { name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'action') or (sys_platform == 'win32' and extra == 'action')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'all') or (sys_platform == 'win32' and extra == 'all')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'local-worker') or (sys_platform == 'win32' and extra == 'local-worker')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
{ name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'server-worker') or (sys_platform == 'win32' and extra == 'server-worker')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
- { name = "torchvision", marker = "(sys_platform == 'linux' and extra == 'videoprism') or (sys_platform == 'win32' and extra == 'videoprism')", specifier = ">=0.28,<1", index = "https://download.pytorch.org/whl/cpu" },
+ { name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'action'", specifier = ">=0.28,<1" },
{ name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'all'", specifier = ">=0.28,<1" },
{ name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'local-worker'", specifier = ">=0.28,<1" },
{ name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'server-worker'", specifier = ">=0.28,<1" },
- { name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32' and extra == 'videoprism'", specifier = ">=0.28,<1" },
+ { name = "transformers", marker = "extra == 'action'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'all'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'local-worker'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'scene'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'server-worker'", specifier = ">=5.14.1,<6" },
{ name = "transformers", marker = "extra == 'sound'", specifier = ">=5.14.1,<6" },
- { name = "transformers", marker = "extra == 'videoprism'", specifier = ">=5.14.1,<6" },
{ name = "typer", specifier = ">=0.27,<1" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.51,<0.52" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'server-worker'", specifier = ">=0.51,<0.52" },
]
-provides-extras = ["storage", "dialogue", "scene", "actor", "videoprism", "sound", "all", "local-worker", "mcp", "slm", "server", "server-worker", "test", "frontend", "benchmarks"]
+provides-extras = ["storage", "speech", "scene", "actor", "action", "sound", "all", "local-worker", "mcp", "slm", "server", "server-worker", "test", "frontend", "benchmarks"]
[[package]]
name = "watchdog"
From 438ae8aae512343bf81ea3cf25fb07efe0a44322 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Tue, 1 Sep 2026 15:59:09 +0500
Subject: [PATCH 6/9] fix(desktop): use a managed headless Ollama runtime
(#137)
* fix(desktop): use a managed headless Ollama runtime
* fix(packaging): harden dependency safeguards
---
INSTALLATION_GUIDE.md | 23 +-
benchmarks/codex-mcp/.npmrc | 1 +
desktop/THIRD_PARTY_NOTICES.txt | 706 +++++++++++++++++++++++-
desktop/about.toml | 1 +
desktop/runtime-manifest.json | 22 +-
desktop/src-tauri/Cargo.lock | 553 ++++++++++++++++++-
desktop/src-tauri/Cargo.toml | 5 +-
desktop/src-tauri/src/lib.rs | 150 +++--
desktop/src-tauri/src/query_setup.rs | 478 +++++++++++++---
desktop/src/App.test.tsx | 13 +-
desktop/src/components/ManagedSetup.tsx | 17 +-
desktop/src/tauri.ts | 4 +
docs/architecture/platform.md | 27 +-
docs/benchmarking/agent_ablation.md | 3 +
docs/desktop.md | 11 +
docs/local-api.md | 30 +-
tests/test_packaging.py | 25 +
17 files changed, 1898 insertions(+), 171 deletions(-)
diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md
index 8e76da6f..a35ba255 100644
--- a/INSTALLATION_GUIDE.md
+++ b/INSTALLATION_GUIDE.md
@@ -318,21 +318,24 @@ and sharing behavior.
VidXP search does not require a language model. To let CLI, HTTP, or MCP
queries plan searches and draft grounded answers locally, enable **Local
grounded answers** in VidXP Desktop setup. Desktop checks for a compatible
-loopback Ollama service, asks before installing Ollama through the supported
-Windows or macOS package manager when needed, and explicitly downloads the
-approved Qwen3.5 4B model. Linux setup links to Ollama's official installation
-instructions instead of running a privileged script.
+loopback Ollama service and then for an existing Ollama executable. When neither
+is available on Windows x86-64 or macOS Apple Silicon, Desktop asks before
+downloading a pinned, checksum-verified headless runtime into VidXP's private
+data. It does not install the Ollama desktop app. Linux setup links to Ollama's
+official installation instructions instead of running a privileged script.
This optional feature follows Ollama's platform floor: Windows 10 22H2 or
newer, or macOS 14 or newer. VidXP Desktop itself can still run without local
grounded answers on older supported systems.
-The model is an additional approximately 3.4 GB download and has no per-run
-API charge or numbered hosted-model allowance. It uses local storage, memory,
-compute time, and electricity. Desktop configures the private service address
-for its browser, worker, API, Premiere, and generated MCP/Codex setup; there is
-no URL field to fill in. A command-line-only installation remains available
-for developers and custom deployments.
+The model is an additional approximately 3.4 GB download. When Desktop must
+provide the headless runtime, that download is up to approximately 1.36 GiB;
+reusing Ollama avoids it. Local answers have no per-run API charge or numbered
+hosted-model allowance, but they use local storage, memory, compute time, and
+electricity. Desktop configures the private service address for its browser,
+worker, API, Premiere, and generated MCP/Codex setup; there is no URL field to
+fill in. A command-line-only installation remains available for developers and
+custom deployments.
The complete setup and its current evidence limitations are documented under
[Enable local grounded answers](docs/local-api.md#enable-local-grounded-answers).
diff --git a/benchmarks/codex-mcp/.npmrc b/benchmarks/codex-mcp/.npmrc
index b6f27f13..61646997 100644
--- a/benchmarks/codex-mcp/.npmrc
+++ b/benchmarks/codex-mcp/.npmrc
@@ -1 +1,2 @@
engine-strict=true
+omit=optional
diff --git a/desktop/THIRD_PARTY_NOTICES.txt b/desktop/THIRD_PARTY_NOTICES.txt
index be6f47e1..b6aed2e0 100644
--- a/desktop/THIRD_PARTY_NOTICES.txt
+++ b/desktop/THIRD_PARTY_NOTICES.txt
@@ -686,6 +686,7 @@ Used by:
- embed_plist 1.2.2 | https://github.com/nvzqz/embed-plist-rs | registry+https://github.com/rust-lang/crates.io-index
- encoding_rs 0.8.35 | https://github.com/hsivonen/encoding_rs | registry+https://github.com/rust-lang/crates.io-index
- utf8_iter 1.0.4 | https://github.com/hsivonen/utf8_iter | registry+https://github.com/rust-lang/crates.io-index
+- zeroize 1.9.0 | https://github.com/RustCrypto/utils | registry+https://github.com/rust-lang/crates.io-index
Apache License
@@ -905,6 +906,7 @@ Used by:
- windows-link 0.2.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
- windows-numerics 0.2.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
- windows-numerics 0.3.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
+- windows-registry 0.6.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
- windows-result 0.3.4 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
- windows-result 0.4.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
- windows-strings 0.4.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index
@@ -1358,6 +1360,7 @@ Used by:
-------------------------------------------------------------------------------
License: Apache License 2.0 (Apache-2.0)
Used by:
+- rustls-platform-verifier 0.7.0 | https://github.com/rustls/rustls-platform-verifier | registry+https://github.com/rust-lang/crates.io-index
- serialize-to-javascript-impl 0.1.2 | https://github.com/chippers/serialize-to-javascript | registry+https://github.com/rust-lang/crates.io-index
- serialize-to-javascript 0.1.2 | https://github.com/chippers/serialize-to-javascript | registry+https://github.com/rust-lang/crates.io-index
@@ -3854,6 +3857,213 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+-------------------------------------------------------------------------------
+License: Apache License 2.0 (Apache-2.0)
+Used by:
+- tokio-rustls 0.26.4 | https://github.com/rustls/tokio-rustls | registry+https://github.com/rust-lang/crates.io-index
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2017 quininer kel
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
-------------------------------------------------------------------------------
License: Apache License 2.0 (Apache-2.0)
Used by:
@@ -4047,13 +4257,220 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
-Copyright 2019 The CryptoCorrosion Contributors
+Copyright 2019 The CryptoCorrosion Contributors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-------------------------------------------------------------------------------
+License: Apache License 2.0 (Apache-2.0)
+Used by:
+- proc-macro-error-attr 1.0.4 | https://gitlab.com/CreepySkeleton/proc-macro-error | registry+https://github.com/rust-lang/crates.io-index
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2019-2020 CreepySkeleton
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -4064,7 +4481,7 @@ limitations under the License.
-------------------------------------------------------------------------------
License: Apache License 2.0 (Apache-2.0)
Used by:
-- proc-macro-error-attr 1.0.4 | https://gitlab.com/CreepySkeleton/proc-macro-error | registry+https://github.com/rust-lang/crates.io-index
+- rustls-pki-types 1.15.1 | https://github.com/rustls/pki-types | registry+https://github.com/rust-lang/crates.io-index
Apache License
Version 2.0, January 2004
@@ -4254,13 +4671,13 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
-Copyright 2019-2020 CreepySkeleton
+Copyright 2023 Dirkjan Ochtman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -4497,6 +4914,7 @@ Used by:
- concurrent-queue 2.5.0 | https://github.com/smol-rs/concurrent-queue | registry+https://github.com/rust-lang/crates.io-index
- core-foundation-sys 0.8.7 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index
- core-foundation 0.10.1 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index
+- core-foundation 0.9.4 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index
- core-graphics-types 0.2.0 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index
- core-graphics 0.25.0 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index
- crossbeam-channel 0.5.16 | https://github.com/crossbeam-rs/crossbeam | registry+https://github.com/rust-lang/crates.io-index
@@ -4507,7 +4925,8 @@ Used by:
- event-listener-strategy 0.5.4 | https://github.com/smol-rs/event-listener-strategy | registry+https://github.com/rust-lang/crates.io-index
- event-listener 5.4.2 | https://github.com/smol-rs/event-listener | registry+https://github.com/rust-lang/crates.io-index
- fastrand 2.5.0 | https://github.com/smol-rs/fastrand | registry+https://github.com/rust-lang/crates.io-index
-- flate2 1.1.9 | https://github.com/rust-lang/flate2-rs | registry+https://github.com/rust-lang/crates.io-index
+- filetime 0.2.29 | https://github.com/alexcrichton/filetime | registry+https://github.com/rust-lang/crates.io-index
+- flate2 1.1.10 | https://github.com/rust-lang/flate2-rs | registry+https://github.com/rust-lang/crates.io-index
- fnv 1.0.7 | https://github.com/servo/rust-fnv | registry+https://github.com/rust-lang/crates.io-index
- form_urlencoded 1.2.2 | https://github.com/servo/rust-url | registry+https://github.com/rust-lang/crates.io-index
- futures-lite 2.6.1 | https://github.com/smol-rs/futures-lite | registry+https://github.com/rust-lang/crates.io-index
@@ -4518,6 +4937,7 @@ Used by:
- hermit-abi 0.5.2 | https://github.com/hermit-os/hermit-rs | registry+https://github.com/rust-lang/crates.io-index
- html5ever 0.38.0 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index
- httparse 1.10.1 | https://github.com/seanmonstar/httparse | registry+https://github.com/rust-lang/crates.io-index
+- hyper-rustls 0.27.9 | https://github.com/rustls/hyper-rustls | registry+https://github.com/rust-lang/crates.io-index
- idna 1.1.0 | https://github.com/servo/rust-url/ | registry+https://github.com/rust-lang/crates.io-index
- idna_adapter 1.2.1 | https://github.com/hsivonen/idna_adapter | registry+https://github.com/rust-lang/crates.io-index
- indexmap 2.14.0 | https://github.com/indexmap-rs/indexmap | registry+https://github.com/rust-lang/crates.io-index
@@ -4531,6 +4951,7 @@ Used by:
- mime 0.3.17 | https://github.com/hyperium/mime | registry+https://github.com/rust-lang/crates.io-index
- muda 0.19.3 | https://github.com/tauri-apps/muda | registry+https://github.com/rust-lang/crates.io-index
- once_cell 1.21.4 | https://github.com/matklad/once_cell | registry+https://github.com/rust-lang/crates.io-index
+- openssl-probe 0.2.1 | https://github.com/rustls/openssl-probe | registry+https://github.com/rust-lang/crates.io-index
- ordered-stream 0.2.0 | https://github.com/danieldg/ordered-stream | registry+https://github.com/rust-lang/crates.io-index
- parking 2.2.1 | https://github.com/smol-rs/parking | registry+https://github.com/rust-lang/crates.io-index
- parking_lot 0.12.5 | https://github.com/Amanieu/parking_lot | registry+https://github.com/rust-lang/crates.io-index
@@ -4544,18 +4965,26 @@ Used by:
- regex-syntax 0.8.11 | https://github.com/rust-lang/regex | registry+https://github.com/rust-lang/crates.io-index
- regex 1.13.1 | https://github.com/rust-lang/regex | registry+https://github.com/rust-lang/crates.io-index
- rustix 1.1.4 | https://github.com/bytecodealliance/rustix | registry+https://github.com/rust-lang/crates.io-index
+- rustls-native-certs 0.8.4 | https://github.com/rustls/rustls-native-certs | registry+https://github.com/rust-lang/crates.io-index
+- rustls 0.23.43 | https://github.com/rustls/rustls | registry+https://github.com/rust-lang/crates.io-index
- scopeguard 1.2.0 | https://github.com/bluss/scopeguard | registry+https://github.com/rust-lang/crates.io-index
+- security-framework-sys 2.17.0 | https://github.com/kornelski/rust-security-framework | registry+https://github.com/rust-lang/crates.io-index
+- security-framework 3.7.0 | https://github.com/kornelski/rust-security-framework | registry+https://github.com/rust-lang/crates.io-index
- serde_with 3.21.0 | https://github.com/jonasbb/serde_with/ | registry+https://github.com/rust-lang/crates.io-index
- serde_with_macros 3.21.0 | https://github.com/jonasbb/serde_with/ | registry+https://github.com/rust-lang/crates.io-index
- servo_arc 0.4.3 | https://github.com/servo/stylo | registry+https://github.com/rust-lang/crates.io-index
- signal-hook-registry 1.4.8 | https://github.com/vorner/signal-hook | registry+https://github.com/rust-lang/crates.io-index
- signal-hook 0.3.18 | https://github.com/vorner/signal-hook | registry+https://github.com/rust-lang/crates.io-index
+- simd_cesu8 1.2.0 | https://github.com/seancroach/simd_cesu8 | registry+https://github.com/rust-lang/crates.io-index
- smallvec 1.15.2 | https://github.com/servo/rust-smallvec | registry+https://github.com/rust-lang/crates.io-index
- socket2 0.6.5 | https://github.com/rust-lang/socket2 | registry+https://github.com/rust-lang/crates.io-index
- stable_deref_trait 1.2.1 | https://github.com/storyyeller/stable_deref_trait | registry+https://github.com/rust-lang/crates.io-index
- string_cache 0.9.0 | https://github.com/servo/string-cache | registry+https://github.com/rust-lang/crates.io-index
- syn 1.0.109 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index
+- system-configuration-sys 0.6.0 | https://github.com/mullvad/system-configuration-rs | registry+https://github.com/rust-lang/crates.io-index
+- system-configuration 0.7.0 | https://github.com/mullvad/system-configuration-rs | registry+https://github.com/rust-lang/crates.io-index
- tao-macros 0.1.4 | https://github.com/tauri-apps/tao | registry+https://github.com/rust-lang/crates.io-index
+- tar 0.4.46 | https://github.com/composefs/tar-rs | registry+https://github.com/rust-lang/crates.io-index
- tempfile 3.27.0 | https://github.com/Stebalien/tempfile | registry+https://github.com/rust-lang/crates.io-index
- tendril 0.5.1 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index
- toml_datetime 0.6.3 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index
@@ -4574,6 +5003,7 @@ Used by:
- window-vibrancy 0.6.0 | https://github.com/tauri-apps/tauri-plugin-vibrancy | registry+https://github.com/rust-lang/crates.io-index
- wit-bindgen 0.46.0 | https://github.com/bytecodealliance/wit-bindgen | registry+https://github.com/rust-lang/crates.io-index
- wry 0.55.1 | https://github.com/tauri-apps/wry | registry+https://github.com/rust-lang/crates.io-index
+- xattr 1.6.1 | https://github.com/Stebalien/xattr | registry+https://github.com/rust-lang/crates.io-index
Apache License
Version 2.0, January 2004
@@ -7488,6 +7918,7 @@ License: Apache License 2.0 (Apache-2.0)
Used by:
- anyhow 1.0.104 | https://github.com/dtolnay/anyhow | registry+https://github.com/rust-lang/crates.io-index
- async-trait 0.1.91 | https://github.com/dtolnay/async-trait | registry+https://github.com/rust-lang/crates.io-index
+- aws-lc-sys 0.44.0 | https://github.com/aws/aws-lc-rs | registry+https://github.com/rust-lang/crates.io-index
- cesu8 1.1.0 | https://github.com/emk/cesu8-rs | registry+https://github.com/rust-lang/crates.io-index
- dirs-sys 0.5.0 | https://github.com/dirs-dev/dirs-sys-rs | registry+https://github.com/rust-lang/crates.io-index
- dirs 6.0.0 | https://github.com/soc/dirs-rs | registry+https://github.com/rust-lang/crates.io-index
@@ -7500,10 +7931,13 @@ Used by:
- field-offset 0.3.6 | https://github.com/Diggsey/rust-field-offset | registry+https://github.com/rust-lang/crates.io-index
- ident_case 1.0.1 | https://github.com/TedDriggs/ident_case | registry+https://github.com/rust-lang/crates.io-index
- itoa 1.0.18 | https://github.com/dtolnay/itoa | registry+https://github.com/rust-lang/crates.io-index
+- jni-macros 0.22.4 | https://github.com/jni-rs/jni-rs | registry+https://github.com/rust-lang/crates.io-index
- jni-sys-macros 0.4.1 | https://github.com/jni-rs/jni-sys | registry+https://github.com/rust-lang/crates.io-index
+- jni 0.22.4 | https://github.com/jni-rs/jni-rs | registry+https://github.com/rust-lang/crates.io-index
- libappindicator-sys 0.9.0 | | registry+https://github.com/rust-lang/crates.io-index
- libc 0.2.189 | https://github.com/rust-lang/libc | registry+https://github.com/rust-lang/crates.io-index
- miniz_oxide 0.8.9 | https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide | registry+https://github.com/rust-lang/crates.io-index
+- miniz_oxide 0.9.1 | https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide | registry+https://github.com/rust-lang/crates.io-index
- ndk-sys 0.6.0+11769913 | https://github.com/rust-mobile/ndk | registry+https://github.com/rust-lang/crates.io-index
- ndk 0.9.0 | https://github.com/rust-mobile/ndk | registry+https://github.com/rust-lang/crates.io-index
- num-conv 0.2.2 | https://github.com/jhpratt/num-conv | registry+https://github.com/rust-lang/crates.io-index
@@ -7532,6 +7966,7 @@ Used by:
- rand_chacha 0.9.0 | https://github.com/rust-random/rand | registry+https://github.com/rust-lang/crates.io-index
- raw-window-handle 0.6.2 | https://github.com/rust-windowing/raw-window-handle | registry+https://github.com/rust-lang/crates.io-index
- rustc-hash 2.1.3 | https://github.com/rust-lang/rustc-hash | registry+https://github.com/rust-lang/crates.io-index
+- rustls-platform-verifier-android 0.1.1 | https://github.com/rustls/rustls-platform-verifier | registry+https://github.com/rust-lang/crates.io-index
- rustversion 1.0.23 | https://github.com/dtolnay/rustversion | registry+https://github.com/rust-lang/crates.io-index
- semver 1.0.28 | https://github.com/dtolnay/semver | registry+https://github.com/rust-lang/crates.io-index
- serde-untagged 0.1.9 | https://github.com/dtolnay/serde-untagged | registry+https://github.com/rust-lang/crates.io-index
@@ -7541,6 +7976,7 @@ Used by:
- serde_derive_internals 0.29.1 | https://github.com/serde-rs/serde | registry+https://github.com/rust-lang/crates.io-index
- serde_json 1.0.151 | https://github.com/serde-rs/json | registry+https://github.com/rust-lang/crates.io-index
- serde_repr 0.1.21 | https://github.com/dtolnay/serde-repr | registry+https://github.com/rust-lang/crates.io-index
+- simdutf8 0.1.5 | https://github.com/rusticstuff/simdutf8 | registry+https://github.com/rust-lang/crates.io-index
- siphasher 1.0.3 | https://github.com/jedisct1/rust-siphash | registry+https://github.com/rust-lang/crates.io-index
- syn 2.0.119 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index
- syn 3.0.3 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index
@@ -7565,6 +8001,7 @@ Used by:
- time-core 0.1.8 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index
- time-macros 0.2.27 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index
- time 0.3.47 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index
+- typed-path 0.12.3 | https://github.com/chipsenkbeil/typed-path | registry+https://github.com/rust-lang/crates.io-index
- typeid 1.0.3 | https://github.com/dtolnay/typeid | registry+https://github.com/rust-lang/crates.io-index
- unic-char-property 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index
- unic-char-range 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index
@@ -7670,10 +8107,46 @@ Redistribution and use in source and binary forms, with or without modification,
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-------------------------------------------------------------------------------
+License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
+Used by:
+- subtle 2.6.1 | https://github.com/dalek-cryptography/subtle | registry+https://github.com/rust-lang/crates.io-index
+
+Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved.
+Copyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
-------------------------------------------------------------------------------
License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
Used by:
- alloc-stdlib 0.2.4 | https://github.com/dropbox/rust-alloc-no-stdlib | registry+https://github.com/rust-lang/crates.io-index
+- aws-lc-sys 0.44.0 | https://github.com/aws/aws-lc-rs | registry+https://github.com/rust-lang/crates.io-index
Copyright (c) .
@@ -7749,6 +8222,92 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-------------------------------------------------------------------------------
+License: Community Data License Agreement Permissive 2.0 (CDLA-Permissive-2.0)
+Used by:
+- webpki-root-certs 1.0.9 | https://github.com/rustls/webpki-roots | registry+https://github.com/rust-lang/crates.io-index
+
+# Community Data License Agreement - Permissive - Version 2.0
+
+This is the Community Data License Agreement - Permissive, Version
+2.0 (the "agreement"). Data Provider(s) and Data Recipient(s) agree
+as follows:
+
+## 1. Provision of the Data
+
+1.1. A Data Recipient may use, modify, and share the Data made
+available by Data Provider(s) under this agreement if that Data
+Recipient follows the terms of this agreement.
+
+1.2. This agreement does not impose any restriction on a Data
+Recipient's use, modification, or sharing of any portions of the
+Data that are in the public domain or that may be used, modified,
+or shared under any other legal exception or limitation.
+
+## 2. Conditions for Sharing Data
+
+2.1. A Data Recipient may share Data, with or without modifications, so
+long as the Data Recipient makes available the text of this agreement
+with the shared Data.
+
+## 3. No Restrictions on Results
+
+3.1. This agreement does not impose any restriction or obligations
+with respect to the use, modification, or sharing of Results.
+
+## 4. No Warranty; Limitation of Liability
+
+4.1. All Data Recipients receive the Data subject to the following
+terms:
+
+THE DATA IS PROVIDED ON AN "AS IS" BASIS, WITHOUT REPRESENTATIONS,
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED
+INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+NO DATA PROVIDER SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE DATA OR RESULTS,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+## 5. Definitions
+
+5.1. "Data" means the material received by a Data Recipient under
+this agreement.
+
+5.2. "Data Provider" means any person who is the source of Data
+provided under this agreement and in reliance on a Data Recipient's
+agreement to its terms.
+
+5.3. "Data Recipient" means any person who receives Data directly
+or indirectly from a Data Provider and agrees to the terms of this
+agreement.
+
+5.4. "Results" means any outcome obtained by computational analysis
+of Data, including for example machine learning models and models'
+insights.
+
+-------------------------------------------------------------------------------
+License: ISC License (ISC)
+Used by:
+- untrusted 0.9.0 | https://github.com/briansmith/untrusted | registry+https://github.com/rust-lang/crates.io-index
+
+// Copyright 2015-2016 Brian Smith.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
-------------------------------------------------------------------------------
License: ISC License (ISC)
Used by:
@@ -7767,6 +8326,46 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
+-------------------------------------------------------------------------------
+License: ISC License (ISC)
+Used by:
+- rustls-webpki 0.103.15 | https://github.com/rustls/webpki | registry+https://github.com/rust-lang/crates.io-index
+
+Except as otherwise noted, this project is licensed under the following
+(ISC-style) terms:
+
+Copyright 2015 Brian Smith.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+The files under third-party/chromium are licensed as described in
+third-party/chromium/LICENSE.
+
+-------------------------------------------------------------------------------
+License: ISC License (ISC)
+Used by:
+- aws-lc-rs 1.18.0 | https://github.com/aws/aws-lc-rs | registry+https://github.com/rust-lang/crates.io-index
+- aws-lc-sys 0.44.0 | https://github.com/aws/aws-lc-rs | registry+https://github.com/rust-lang/crates.io-index
+
+ISC License:
+
+Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+Copyright (c) 1995-2003 by Internet Software Consortium
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
-------------------------------------------------------------------------------
License: MIT License (MIT)
Used by:
@@ -7962,6 +8561,19 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+-------------------------------------------------------------------------------
+License: MIT License (MIT)
+Used by:
+- schannel 0.1.29 | https://github.com/steffengy/schannel-rs | registry+https://github.com/rust-lang/crates.io-index
+
+Copyright (c) 2015 steffengy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
-------------------------------------------------------------------------------
License: MIT License (MIT)
Used by:
@@ -8969,6 +9581,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+-------------------------------------------------------------------------------
+License: MIT License (MIT)
+Used by:
+- zip 7.2.0 | https://github.com/zip-rs/zip2.git | registry+https://github.com/rust-lang/crates.io-index
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathijs van de Nes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Some files in the "tests/data" subdirectory of this repository are under other
+licences; see files named LICENSE.*.txt for details.
-------------------------------------------------------------------------------
License: MIT License (MIT)
Used by:
@@ -9113,6 +9754,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+-------------------------------------------------------------------------------
+License: MIT License (MIT)
+Used by:
+- aws-lc-sys 0.44.0 | https://github.com/aws/aws-lc-rs | registry+https://github.com/rust-lang/crates.io-index
+
+The MIT License (MIT)
+
+Copyright (c) 2015-2020 the fiat-crypto authors (see
+https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
-------------------------------------------------------------------------------
License: MIT License (MIT)
Used by:
@@ -10562,6 +11231,31 @@ SPDX-License-Identifier: Unicode-3.0
Portions of ICU4X may have been adapted from ICU4C and/or ICU4J.
ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.
+-------------------------------------------------------------------------------
+License: zlib License (Zlib)
+Used by:
+- zlib-rs 0.6.7 | https://github.com/trifectatechfoundation/zlib-rs | registry+https://github.com/rust-lang/crates.io-index
+
+(C) 2024 Trifecta Tech Foundation
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source distribution.
+
-------------------------------------------------------------------------------
License: zlib License (Zlib)
Used by:
diff --git a/desktop/about.toml b/desktop/about.toml
index 5753ea59..9af67e81 100644
--- a/desktop/about.toml
+++ b/desktop/about.toml
@@ -3,6 +3,7 @@ accepted = [
"MIT",
"BSD-2-Clause",
"BSD-3-Clause",
+ "CDLA-Permissive-2.0",
"ISC",
"MPL-2.0",
"Unicode-3.0",
diff --git a/desktop/runtime-manifest.json b/desktop/runtime-manifest.json
index cf4d362d..1937d5b2 100644
--- a/desktop/runtime-manifest.json
+++ b/desktop/runtime-manifest.json
@@ -11,8 +11,28 @@
"engine": "ollama",
"model": "qwen3.5:4b-q4_K_M",
"download_size_bytes": 3650722202,
+ "managed_runtime": {
+ "version": "0.32.5",
+ "maximum_download_size_bytes": 1457824795,
+ "artifacts": {
+ "windows-x86_64": {
+ "url": "https://github.com/ollama/ollama/releases/download/v0.32.5/ollama-windows-amd64.zip",
+ "sha256": "7c941ae084569d298062d29f8139163a3187c76dbca0479c70d085e78fd8c7bb",
+ "download_size_bytes": 1457824795,
+ "archive": "zip",
+ "executable": "ollama.exe"
+ },
+ "macos-aarch64": {
+ "url": "https://github.com/ollama/ollama/releases/download/v0.32.5/ollama-darwin.tgz",
+ "sha256": "5789dd037a86adb328c72c11fc45e6c558452d07e5b50814a8bdb7b0fbdbcd81",
+ "download_size_bytes": 145747028,
+ "archive": "tar_gz",
+ "executable": "ollama"
+ }
+ }
+ },
"label": "Local grounded answers",
- "description": "Turn VidXP search evidence into cited answers on this computer. Setup reuses or installs Ollama and downloads the approved Qwen 3.5 4B model."
+ "description": "Turn VidXP search evidence into cited answers on this computer. Setup reuses Ollama when available or downloads a private headless runtime and the approved Qwen 3.5 4B model."
},
"surfaces": {
"worker": {
diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock
index 30f81226..7157c50b 100644
--- a/desktop/src-tauri/Cargo.lock
+++ b/desktop/src-tauri/Cargo.lock
@@ -231,7 +231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84790c55b5704b0d35130bf16a4ce22a8e70eb0ea773522557524d9a4852663d"
dependencies = [
"nix 0.30.1",
- "rand",
+ "rand 0.9.5",
]
[[package]]
@@ -240,6 +240,29 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+[[package]]
+name = "aws-lc-rs"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "pkg-config",
+]
+
[[package]]
name = "base64"
version = "0.21.7"
@@ -453,6 +476,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
dependencies = [
"find-msvc-tools",
+ "jobserver",
+ "libc",
"shlex",
]
@@ -495,6 +520,17 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
+[[package]]
+name = "chacha20"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.45"
@@ -507,6 +543,15 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "combine"
version = "4.6.7"
@@ -542,6 +587,16 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation"
version = "0.10.1"
@@ -565,7 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
dependencies = [
"bitflags 2.13.1",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-graphics-types",
"foreign-types",
"libc",
@@ -578,7 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.13.1",
- "core-foundation",
+ "core-foundation 0.10.1",
"libc",
]
@@ -1059,6 +1114,16 @@ dependencies = [
"rustc_version",
]
+[[package]]
+name = "filetime"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
+dependencies = [
+ "cfg-if",
+ "libc",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -1067,12 +1132,13 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
-version = "1.1.9"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
- "miniz_oxide",
+ "miniz_oxide 0.9.1",
+ "zlib-rs",
]
[[package]]
@@ -1123,6 +1189,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "futures-channel"
version = "0.3.33"
@@ -1324,8 +1396,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -1347,8 +1421,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 6.0.0",
+ "rand_core 0.10.1",
+ "wasm-bindgen",
]
[[package]]
@@ -1613,6 +1690,21 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.27.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
[[package]]
name = "hyper-util"
version = "0.1.20"
@@ -1631,9 +1723,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
@@ -1648,7 +1742,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
- "windows-core 0.61.2",
+ "windows-core 0.62.2",
]
[[package]]
@@ -1880,6 +1974,36 @@ dependencies = [
"windows-sys 0.45.0",
]
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if",
+ "combine",
+ "jni-macros",
+ "jni-sys 0.4.1",
+ "log",
+ "simd_cesu8",
+ "thiserror 2.0.19",
+ "walkdir",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "jni-macros"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "simd_cesu8",
+ "syn 2.0.119",
+]
+
[[package]]
name = "jni-sys"
version = "0.3.1"
@@ -1908,6 +2032,16 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "jobserver"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
+dependencies = [
+ "getrandom 0.4.3",
+ "libc",
+]
+
[[package]]
name = "js-sys"
version = "0.3.103"
@@ -2037,6 +2171,12 @@ version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
[[package]]
name = "markup5ever"
version = "0.38.0"
@@ -2079,6 +2219,16 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "miniz_oxide"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
[[package]]
name = "mio"
version = "1.2.2"
@@ -2424,6 +2574,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "openssl-probe"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
+
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -2609,7 +2765,7 @@ dependencies = [
"crc32fast",
"fdeflate",
"flate2",
- "miniz_oxide",
+ "miniz_oxide 0.8.9",
]
[[package]]
@@ -2622,7 +2778,7 @@ dependencies = [
"crc32fast",
"fdeflate",
"flate2",
- "miniz_oxide",
+ "miniz_oxide 0.8.9",
]
[[package]]
@@ -2752,6 +2908,63 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "quinn"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.19",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
+dependencies = [
+ "aws-lc-rs",
+ "bytes",
+ "getrandom 0.4.3",
+ "lru-slab",
+ "rand 0.10.2",
+ "rand_pcg",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.19",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "quote"
version = "1.0.47"
@@ -2780,7 +2993,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [
"rand_chacha",
- "rand_core",
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "rand"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.3",
+ "rand_core 0.10.1",
]
[[package]]
@@ -2790,7 +3014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.9.5",
]
[[package]]
@@ -2802,6 +3026,21 @@ dependencies = [
"getrandom 0.3.4",
]
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
+[[package]]
+name = "rand_pcg"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
+dependencies = [
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -2892,15 +3131,21 @@ dependencies = [
"http-body",
"http-body-util",
"hyper",
+ "hyper-rustls",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "rustls-platform-verifier",
"serde",
"serde_json",
"sync_wrapper",
"tokio",
+ "tokio-rustls",
"tokio-util",
"tower",
"tower-http",
@@ -2936,6 +3181,20 @@ dependencies = [
"windows-sys 0.60.2",
]
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "rustc-hash"
version = "2.1.3"
@@ -2964,6 +3223,81 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "rustls"
+version = "0.23.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
+dependencies = [
+ "aws-lc-rs",
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-platform-verifier"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
+dependencies = [
+ "core-foundation 0.10.1",
+ "core-foundation-sys",
+ "jni 0.22.4",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-platform-verifier-android",
+ "rustls-webpki",
+ "security-framework",
+ "security-framework-sys",
+ "webpki-root-certs",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls-platform-verifier-android"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
+dependencies = [
+ "aws-lc-rs",
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
[[package]]
name = "rustversion"
version = "1.0.23"
@@ -2979,6 +3313,15 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "schannel"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "schemars"
version = "0.8.22"
@@ -3036,6 +3379,29 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "security-framework"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
+dependencies = [
+ "bitflags 2.13.1",
+ "core-foundation 0.10.1",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "selectors"
version = "0.36.1"
@@ -3299,6 +3665,22 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
+[[package]]
+name = "simd_cesu8"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
[[package]]
name = "siphasher"
version = "1.0.3"
@@ -3411,6 +3793,12 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
[[package]]
name = "swift-rs"
version = "1.0.7"
@@ -3474,6 +3862,27 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "system-configuration"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
+dependencies = [
+ "bitflags 2.13.1",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -3495,7 +3904,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
dependencies = [
"bitflags 2.13.1",
"block2",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-graphics",
"crossbeam-channel",
"dbus",
@@ -3505,7 +3914,7 @@ dependencies = [
"gdkwayland-sys",
"gdkx11-sys",
"gtk",
- "jni",
+ "jni 0.21.1",
"libc",
"log",
"ndk",
@@ -3538,6 +3947,17 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "tar"
+version = "0.4.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
[[package]]
name = "target-lexicon"
version = "0.12.16"
@@ -3561,7 +3981,7 @@ dependencies = [
"gtk",
"heck 0.5.0",
"http",
- "jni",
+ "jni 0.21.1",
"libc",
"log",
"mime",
@@ -3821,7 +4241,7 @@ dependencies = [
"dpi",
"gtk",
"http",
- "jni",
+ "jni 0.21.1",
"objc2",
"objc2-ui-kit",
"objc2-web-kit",
@@ -3844,7 +4264,7 @@ checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
dependencies = [
"gtk",
"http",
- "jni",
+ "jni 0.21.1",
"log",
"objc2",
"objc2-app-kit",
@@ -4057,6 +4477,16 @@ dependencies = [
"syn 3.0.3",
]
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
[[package]]
name = "tokio-util"
version = "0.7.19"
@@ -4294,6 +4724,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "typed-path"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
+
[[package]]
name = "typeid"
version = "1.0.3"
@@ -4370,6 +4806,12 @@ version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
[[package]]
name = "url"
version = "2.5.8"
@@ -4430,6 +4872,7 @@ name = "vidxp-desktop"
version = "0.4.0"
dependencies = [
"atomic-write-file",
+ "flate2",
"hex",
"log",
"process-wrap",
@@ -4437,6 +4880,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.11.0",
+ "tar",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
@@ -4447,6 +4891,7 @@ dependencies = [
"tauri-plugin-store",
"which",
"windows 0.62.2",
+ "zip",
]
[[package]]
@@ -4581,6 +5026,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "web_atoms"
version = "0.2.5"
@@ -4637,6 +5092,15 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "webview2-com"
version = "0.38.2"
@@ -4873,6 +5337,17 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "windows-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -4918,6 +5393,15 @@ dependencies = [
"windows-targets 0.42.2",
]
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-sys"
version = "0.59.0"
@@ -5225,7 +5709,7 @@ dependencies = [
"gtk",
"http",
"javascriptcore-rs",
- "jni",
+ "jni 0.21.1",
"libc",
"ndk",
"objc2",
@@ -5272,6 +5756,16 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "xattr"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
+dependencies = [
+ "libc",
+ "rustix",
+]
+
[[package]]
name = "yoke"
version = "0.8.3"
@@ -5397,6 +5891,12 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zeroize"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
+
[[package]]
name = "zerotrie"
version = "0.2.4"
@@ -5430,6 +5930,25 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "zip"
+version = "7.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0"
+dependencies = [
+ "crc32fast",
+ "flate2",
+ "indexmap 2.14.0",
+ "memchr",
+ "typed-path",
+]
+
+[[package]]
+name = "zlib-rs"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
+
[[package]]
name = "zmij"
version = "1.0.23"
diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml
index fb270307..e954e2bd 100644
--- a/desktop/src-tauri/Cargo.toml
+++ b/desktop/src-tauri/Cargo.toml
@@ -17,13 +17,15 @@ tauri-build = { version = "2.6.3", features = [] }
[dependencies]
atomic-write-file = "0.3.0"
+flate2 = "1.1.10"
hex = "0.4.3"
log = "0.4.29"
process-wrap = { version = "9.1.0", features = ["std"] }
-reqwest = { version = "0.13.4", default-features = false, features = ["blocking", "json"] }
+reqwest = { version = "0.13.4", default-features = false, features = ["blocking", "json", "rustls", "system-proxy"] }
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
sha2 = "0.11.0"
+tar = "0.4.46"
tauri = { version = "2.11.5", features = ["tray-icon"] }
tauri-plugin-log = "2.9.0"
tauri-plugin-dialog = "2.7.2"
@@ -32,6 +34,7 @@ tauri-plugin-shell = "2.3.5"
tauri-plugin-single-instance = "2.4.3"
tauri-plugin-store = "2.4.4"
which = "8.0.0"
+zip = { version = "7.2.0", default-features = false, features = ["deflate-flate2-zlib-rs"] }
[target.'cfg(windows)'.dependencies]
windows = { version = "0.62.2", features = ["Win32_System_Threading"] }
diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index bb306510..0e897430 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -59,7 +59,6 @@ const PRODUCT_DATA_DIRECTORY_NAME: &str = "VidXP";
const RUNTIME_CONSTRAINTS_FILE_NAME: &str = "runtime-constraints.txt";
const MAX_SETUP_OUTPUT_BYTES: usize = 4 * 1024 * 1024;
const MEDIA_RUNTIME_INSTALL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
-const QUERY_RUNTIME_INSTALL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
const QUERY_MODEL_PULL_TIMEOUT: Duration = Duration::from_secs(2 * 60 * 60);
static READINESS_SEQUENCE: AtomicU64 = AtomicU64::new(0);
@@ -98,6 +97,7 @@ struct LocalAnswersSpec {
engine: String,
model: String,
download_size_bytes: u64,
+ managed_runtime: query_setup::ManagedRuntimeSpec,
label: String,
description: String,
}
@@ -1549,6 +1549,14 @@ fn resolve_system_executable(name: &str) -> Option {
None
}
+fn resolve_query_executable(
+ paths: &DesktopPaths,
+ spec: &query_setup::ManagedRuntimeSpec,
+) -> Option {
+ resolve_system_executable("ollama")
+ .or_else(|| query_setup::managed_executable(&paths.private_data, spec))
+}
+
fn combined_output(output: &background_process::BackgroundOutput) -> String {
format!(
"{}\n{}",
@@ -1643,6 +1651,11 @@ fn ollama_management_url(path: &str) -> String {
format!("http://{}{path}", query_setup::OLLAMA_HOST)
}
+fn human_bytes(bytes: u64) -> String {
+ const GIB: f64 = 1024.0 * 1024.0 * 1024.0;
+ format!("{:.2} GiB", bytes as f64 / GIB)
+}
+
fn ollama_client(timeout: Duration) -> Result {
reqwest::blocking::Client::builder()
.connect_timeout(Duration::from_secs(2))
@@ -1861,35 +1874,31 @@ async fn prepare_local_answers_runtime(
draft_id: &str,
current: u8,
total_steps: u8,
- model: &str,
+ spec: &LocalAnswersSpec,
cancellation: background_process::CancellationToken,
) -> Result<(), String> {
if let Some(error) = local_answer_platform_error() {
return Err(error);
}
let server_ready = ollama_server_version().is_ok();
- let mut executable = resolve_system_executable("ollama");
+ let mut executable = resolve_query_executable(paths, &spec.managed_runtime);
if !server_ready && executable.is_none() {
- let plan = query_setup::system_install_plan(resolve_system_executable).ok_or_else(|| {
- if cfg!(target_os = "macos") {
- "Local grounded answers require Ollama on macOS 14 or newer. Install the official Ollama app from https://ollama.com/download, then retry.".to_string()
- } else if cfg!(target_os = "linux") {
- "Local grounded answers require Ollama. Install it using the official Linux instructions at https://ollama.com/download/linux, then retry.".to_string()
- } else {
- "Local grounded answers require Ollama, but no supported automatic installer was found. Install it from https://ollama.com/download, then retry.".to_string()
- }
+ let artifact = query_setup::current_artifact(&spec.managed_runtime).ok_or_else(|| {
+ "VidXP does not publish a managed headless Ollama runtime for this platform. Install Ollama from https://ollama.com/download, then retry."
+ .to_string()
})?;
let approved = app
.dialog()
.message(format!(
- "Local grounded answers use Ollama and download {model} (approximately 3.4 GB, Apache-2.0).\n\nInstall Ollama with {}?\n\n{}",
- plan.manager,
- display_command(&plan.command)
+ "Local grounded answers require a local inference runtime and {} (approximately 3.4 GB, Apache-2.0).\n\nDownload the verified headless Ollama {} runtime ({}) into VidXP's private data? No Ollama desktop app will be installed.",
+ spec.model,
+ spec.managed_runtime.version,
+ human_bytes(artifact.download_size_bytes)
))
- .title("Install local answer runtime")
+ .title("Download local answer runtime")
.kind(MessageDialogKind::Info)
.buttons(MessageDialogButtons::OkCancelCustom(
- "Install".into(),
+ "Download".into(),
"Not now".into(),
))
.blocking_show();
@@ -1901,40 +1910,56 @@ async fn prepare_local_answers_runtime(
draft_id,
current,
total_steps,
- format!("Installing Ollama with {}", plan.manager),
- None,
- None,
+ format!(
+ "Downloading the headless Ollama {} runtime",
+ spec.managed_runtime.version
+ ),
+ Some(0),
+ Some(artifact.download_size_bytes),
+ );
+ let download_app = app.clone();
+ let download_draft = draft_id.to_owned();
+ let private_data = paths.private_data.clone();
+ let managed_runtime = spec.managed_runtime.clone();
+ let runtime_version = managed_runtime.version.clone();
+ let runtime_cancellation = cancellation.clone();
+ executable = Some(
+ tauri::async_runtime::spawn_blocking(move || {
+ query_setup::install_managed_runtime(
+ &private_data,
+ &managed_runtime,
+ &runtime_cancellation,
+ |downloaded, total| {
+ emit_local_answer_progress(
+ &download_app,
+ &download_draft,
+ current,
+ total_steps,
+ format!("Downloading the headless Ollama {runtime_version} runtime"),
+ Some(downloaded),
+ Some(total),
+ );
+ },
+ )
+ })
+ .await
+ .map_err(|error| {
+ format!("Managed Ollama runtime preparation stopped unexpectedly: {error}")
+ })??,
);
- let command = app
- .shell()
- .command(plan.command[0].clone())
- .args(&plan.command[1..]);
- supervised_output_with_timeout(
- command.into(),
- cancellation.clone(),
- &format!("{} Ollama installation", plan.manager),
- QUERY_RUNTIME_INSTALL_TIMEOUT,
- )
- .await?;
- for _ in 0..10 {
- executable = resolve_system_executable("ollama");
- if executable.is_some() {
- break;
- }
- thread::sleep(Duration::from_secs(1));
- }
}
let model_directory = paths.models.join("ollama");
if let Some(executable) = executable {
ensure_query_service(state, &executable, &model_directory)?;
} else if !server_ready {
return Err(
- "Ollama installation finished, but VidXP could not locate its executable.".to_string(),
+ "The managed Ollama runtime finished downloading, but VidXP could not locate its executable."
+ .to_string(),
);
}
let pull_app = app.clone();
let pull_draft = draft_id.to_owned();
- let pull_model = model.to_owned();
+ let pull_model = spec.model.clone();
let pull_cancellation = cancellation;
let installed = tauri::async_runtime::spawn_blocking(move || {
pull_ollama_model(
@@ -1949,7 +1974,10 @@ async fn prepare_local_answers_runtime(
.await
.map_err(|error| format!("Local answer model preparation stopped unexpectedly: {error}"))??;
if installed.digest.trim().is_empty() {
- return Err(format!("Ollama did not report a digest for {model}."));
+ return Err(format!(
+ "Ollama did not report a digest for {}.",
+ spec.model
+ ));
}
Ok(())
}
@@ -1977,8 +2005,9 @@ fn ensure_active_query_service(state: &DesktopState, paths: &DesktopPaths) -> Re
return Ok(());
}
if ollama_server_version().is_err() {
- let executable = resolve_system_executable("ollama").ok_or_else(|| {
- "Local grounded answers are enabled, but Ollama is no longer installed. Open Setup options and repair VidXP."
+ let local_answers = manifest()?.local_answers;
+ let executable = resolve_query_executable(paths, &local_answers.managed_runtime).ok_or_else(|| {
+ "Local grounded answers are enabled, but their Ollama runtime is no longer available. Open Setup options and repair VidXP."
.to_string()
})?;
ensure_query_service(state, &executable, &paths.models.join("ollama"))?;
@@ -3461,7 +3490,7 @@ async fn install_runtime(
&request.draft_id,
2,
progress_total,
- &manifest.local_answers.model,
+ &manifest.local_answers,
cancellation.token(),
)
.await?;
@@ -6132,6 +6161,41 @@ mod tests {
assert_eq!(manifest.local_answers.engine, "ollama");
assert_eq!(manifest.local_answers.model, "qwen3.5:4b-q4_K_M");
assert_eq!(manifest.local_answers.download_size_bytes, 3_650_722_202);
+ assert_eq!(manifest.local_answers.managed_runtime.version, "0.32.5");
+ assert_eq!(
+ manifest
+ .local_answers
+ .managed_runtime
+ .maximum_download_size_bytes,
+ 1_457_824_795
+ );
+ assert!(
+ manifest
+ .local_answers
+ .managed_runtime
+ .artifacts
+ .contains_key("windows-x86_64")
+ );
+ assert!(
+ manifest
+ .local_answers
+ .managed_runtime
+ .artifacts
+ .contains_key("macos-aarch64")
+ );
+ if let Some(artifact) =
+ super::query_setup::current_artifact(&manifest.local_answers.managed_runtime)
+ {
+ assert_eq!(artifact.sha256.len(), 64);
+ assert!(artifact.download_size_bytes > 0);
+ assert!(
+ artifact.download_size_bytes
+ <= manifest
+ .local_answers
+ .managed_runtime
+ .maximum_download_size_bytes
+ );
+ }
}
#[test]
diff --git a/desktop/src-tauri/src/query_setup.rs b/desktop/src-tauri/src/query_setup.rs
index 0738bad4..ad9260ea 100644
--- a/desktop/src-tauri/src/query_setup.rs
+++ b/desktop/src-tauri/src/query_setup.rs
@@ -1,11 +1,45 @@
-use std::{env, path::PathBuf};
+use std::{
+ collections::BTreeMap,
+ env, fs,
+ fs::File,
+ io::{self, BufReader, Read, Write},
+ path::{Path, PathBuf},
+ time::{Duration, SystemTime, UNIX_EPOCH},
+};
-#[cfg(windows)]
-use std::{fs, path::Path};
+use flate2::read::GzDecoder;
+use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
-use crate::media_setup::SystemInstallPlan;
+use crate::background_process::CancellationToken;
pub(crate) const OLLAMA_HOST: &str = "127.0.0.1:11434";
+const MANAGED_RUNTIME_DIRECTORY: &str = "query-runtimes";
+const RUNTIME_DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(2 * 60 * 60);
+const DOWNLOAD_BUFFER_BYTES: usize = 1024 * 1024;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) struct ManagedRuntimeSpec {
+ pub(crate) version: String,
+ pub(crate) maximum_download_size_bytes: u64,
+ pub(crate) artifacts: BTreeMap,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) struct ManagedRuntimeArtifactSpec {
+ pub(crate) url: String,
+ pub(crate) sha256: String,
+ pub(crate) download_size_bytes: u64,
+ pub(crate) archive: ManagedRuntimeArchive,
+ pub(crate) executable: PathBuf,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub(crate) enum ManagedRuntimeArchive {
+ Zip,
+ TarGz,
+}
pub(crate) fn version_meets_minimum(output: &str, minimum: (u32, u32, u32)) -> Option {
let version = output
@@ -24,45 +58,6 @@ pub(crate) fn version_meets_minimum(output: &str, minimum: (u32, u32, u32)) -> O
Some(actual >= minimum)
}
-pub(crate) fn system_install_plan(
- mut resolve: impl FnMut(&str) -> Option,
-) -> Option {
- if cfg!(windows) {
- resolve("winget")?;
- return Some(SystemInstallPlan {
- manager: "Windows Package Manager".into(),
- command: vec![
- "winget".into(),
- "install".into(),
- "--id".into(),
- "Ollama.Ollama".into(),
- "--exact".into(),
- "--source".into(),
- "winget".into(),
- "--silent".into(),
- "--disable-interactivity".into(),
- "--accept-package-agreements".into(),
- "--accept-source-agreements".into(),
- ],
- automatic: true,
- });
- }
- if cfg!(target_os = "macos") {
- let brew = resolve("brew")?;
- return Some(SystemInstallPlan {
- manager: "Homebrew".into(),
- command: vec![
- brew.to_string_lossy().into_owned(),
- "install".into(),
- "--cask".into(),
- "ollama-app".into(),
- ],
- automatic: true,
- });
- }
- None
-}
-
pub(crate) fn executable_candidates() -> Vec {
let mut candidates = Vec::new();
if cfg!(windows) {
@@ -120,17 +115,315 @@ pub(crate) fn resolve_winget_ollama_executable() -> Option {
.and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate)))
}
+#[cfg(all(windows, target_arch = "x86_64"))]
+fn current_platform_key() -> Option<&'static str> {
+ Some("windows-x86_64")
+}
+
+#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
+fn current_platform_key() -> Option<&'static str> {
+ Some("macos-aarch64")
+}
+
+#[cfg(not(any(
+ all(windows, target_arch = "x86_64"),
+ all(target_os = "macos", target_arch = "aarch64")
+)))]
+fn current_platform_key() -> Option<&'static str> {
+ None
+}
+
+pub(crate) fn current_artifact(spec: &ManagedRuntimeSpec) -> Option<&ManagedRuntimeArtifactSpec> {
+ spec.artifacts.get(current_platform_key()?)
+}
+
+fn managed_runtime_root(private_data: &Path) -> PathBuf {
+ private_data.join(MANAGED_RUNTIME_DIRECTORY)
+}
+
+fn managed_runtime_directory(
+ private_data: &Path,
+ spec: &ManagedRuntimeSpec,
+) -> Result {
+ if spec.version.is_empty()
+ || !spec.version.chars().all(|character| {
+ character.is_ascii_alphanumeric() || matches!(character, '.' | '-' | '_')
+ })
+ {
+ return Err("The managed Ollama runtime version is invalid.".into());
+ }
+ Ok(managed_runtime_root(private_data).join(format!("ollama-{}", spec.version)))
+}
+
+pub(crate) fn managed_executable(
+ private_data: &Path,
+ spec: &ManagedRuntimeSpec,
+) -> Option {
+ let artifact = current_artifact(spec)?;
+ let candidate = managed_runtime_directory(private_data, spec)
+ .ok()?
+ .join(&artifact.executable);
+ candidate
+ .is_file()
+ .then(|| fs::canonicalize(&candidate).unwrap_or(candidate))
+}
+
+fn runtime_download_client() -> Result {
+ reqwest::blocking::Client::builder()
+ .connect_timeout(Duration::from_secs(15))
+ .timeout(RUNTIME_DOWNLOAD_TIMEOUT)
+ .build()
+ .map_err(|error| format!("Could not configure the Ollama runtime download: {error}"))
+}
+
+fn download_archive(
+ artifact: &ManagedRuntimeArtifactSpec,
+ destination: &Path,
+ cancellation: &CancellationToken,
+ mut progress: impl FnMut(u64, u64),
+) -> Result<(), String> {
+ let mut response = runtime_download_client()?
+ .get(&artifact.url)
+ .header(reqwest::header::USER_AGENT, "VidXP-Desktop")
+ .send()
+ .and_then(reqwest::blocking::Response::error_for_status)
+ .map_err(|error| format!("Could not download the managed Ollama runtime: {error}"))?;
+ let response_total = response
+ .content_length()
+ .unwrap_or(artifact.download_size_bytes);
+ let mut output = File::create(destination).map_err(|error| {
+ format!(
+ "Could not create the temporary Ollama runtime archive at {}: {error}",
+ destination.display()
+ )
+ })?;
+ let mut hasher = Sha256::new();
+ let mut buffer = vec![0_u8; DOWNLOAD_BUFFER_BYTES];
+ let mut downloaded = 0_u64;
+ progress(0, response_total);
+ loop {
+ if cancellation.is_cancelled() {
+ return Err("the managed Ollama runtime download was cancelled".into());
+ }
+ let count = response
+ .read(&mut buffer)
+ .map_err(|error| format!("The managed Ollama runtime download failed: {error}"))?;
+ if count == 0 {
+ break;
+ }
+ output.write_all(&buffer[..count]).map_err(|error| {
+ format!("Could not write the managed Ollama runtime archive: {error}")
+ })?;
+ hasher.update(&buffer[..count]);
+ downloaded += count as u64;
+ progress(downloaded, response_total);
+ }
+ output
+ .sync_all()
+ .map_err(|error| format!("Could not finish the managed Ollama runtime archive: {error}"))?;
+ if downloaded != artifact.download_size_bytes {
+ return Err(format!(
+ "The managed Ollama runtime download contained {downloaded} bytes; expected {}.",
+ artifact.download_size_bytes
+ ));
+ }
+ let actual_sha256 = hex::encode(hasher.finalize());
+ if !actual_sha256.eq_ignore_ascii_case(&artifact.sha256) {
+ return Err(format!(
+ "The managed Ollama runtime failed checksum verification: expected {}, received {actual_sha256}.",
+ artifact.sha256
+ ));
+ }
+ Ok(())
+}
+
+fn extract_zip(
+ archive_path: &Path,
+ destination: &Path,
+ cancellation: &CancellationToken,
+) -> Result<(), String> {
+ let archive_file = File::open(archive_path)
+ .map_err(|error| format!("Could not open the managed Ollama archive: {error}"))?;
+ let mut archive = zip::ZipArchive::new(BufReader::new(archive_file))
+ .map_err(|error| format!("Could not read the managed Ollama ZIP archive: {error}"))?;
+ for index in 0..archive.len() {
+ if cancellation.is_cancelled() {
+ return Err("the managed Ollama runtime extraction was cancelled".into());
+ }
+ let mut entry = archive
+ .by_index(index)
+ .map_err(|error| format!("Could not inspect the managed Ollama archive: {error}"))?;
+ let relative = entry
+ .enclosed_name()
+ .ok_or("The managed Ollama archive contains an unsafe path.")?;
+ if entry
+ .unix_mode()
+ .is_some_and(|mode| mode & 0o170000 == 0o120000)
+ {
+ return Err("The managed Ollama ZIP archive contains an unsupported link.".into());
+ }
+ let output = destination.join(relative);
+ if entry.is_dir() {
+ fs::create_dir_all(&output)
+ .map_err(|error| format!("Could not create an Ollama runtime folder: {error}"))?;
+ } else if entry.is_file() {
+ if let Some(parent) = output.parent() {
+ fs::create_dir_all(parent).map_err(|error| {
+ format!("Could not create an Ollama runtime folder: {error}")
+ })?;
+ }
+ let mut file = File::create(&output)
+ .map_err(|error| format!("Could not extract an Ollama runtime file: {error}"))?;
+ io::copy(&mut entry, &mut file)
+ .map_err(|error| format!("Could not extract an Ollama runtime file: {error}"))?;
+ } else {
+ return Err("The managed Ollama ZIP archive contains an unsupported entry.".into());
+ }
+ }
+ Ok(())
+}
+
+fn extract_tar_gz(
+ archive_path: &Path,
+ destination: &Path,
+ cancellation: &CancellationToken,
+) -> Result<(), String> {
+ let archive_file = File::open(archive_path)
+ .map_err(|error| format!("Could not open the managed Ollama archive: {error}"))?;
+ let decoder = GzDecoder::new(BufReader::new(archive_file));
+ let mut archive = tar::Archive::new(decoder);
+ let entries = archive
+ .entries()
+ .map_err(|error| format!("Could not read the managed Ollama archive: {error}"))?;
+ for entry in entries {
+ if cancellation.is_cancelled() {
+ return Err("the managed Ollama runtime extraction was cancelled".into());
+ }
+ let mut entry = entry
+ .map_err(|error| format!("Could not inspect the managed Ollama archive: {error}"))?;
+ let entry_type = entry.header().entry_type();
+ if !entry_type.is_file() && !entry_type.is_dir() {
+ return Err("The managed Ollama archive contains an unsupported entry.".into());
+ }
+ if !entry
+ .unpack_in(destination)
+ .map_err(|error| format!("Could not extract the managed Ollama archive: {error}"))?
+ {
+ return Err("The managed Ollama archive contains an unsafe path.".into());
+ }
+ }
+ Ok(())
+}
+
+fn extract_archive(
+ archive_path: &Path,
+ destination: &Path,
+ archive: ManagedRuntimeArchive,
+ cancellation: &CancellationToken,
+) -> Result<(), String> {
+ fs::create_dir_all(destination).map_err(|error| {
+ format!(
+ "Could not create the managed Ollama runtime folder at {}: {error}",
+ destination.display()
+ )
+ })?;
+ match archive {
+ ManagedRuntimeArchive::Zip => extract_zip(archive_path, destination, cancellation),
+ ManagedRuntimeArchive::TarGz => extract_tar_gz(archive_path, destination, cancellation),
+ }
+}
+
+pub(crate) fn install_managed_runtime(
+ private_data: &Path,
+ spec: &ManagedRuntimeSpec,
+ cancellation: &CancellationToken,
+ progress: impl FnMut(u64, u64),
+) -> Result {
+ if let Some(executable) = managed_executable(private_data, spec) {
+ return Ok(executable);
+ }
+ let artifact = current_artifact(spec).ok_or_else(|| {
+ "VidXP does not publish a managed Ollama runtime for this operating system and architecture."
+ .to_string()
+ })?;
+ let runtime_root = managed_runtime_root(private_data);
+ fs::create_dir_all(&runtime_root).map_err(|error| {
+ format!(
+ "Could not create the managed query runtime folder at {}: {error}",
+ runtime_root.display()
+ )
+ })?;
+ let target = managed_runtime_directory(private_data, spec)?;
+ let nonce = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .map_err(|error| format!("The system clock is invalid: {error}"))?
+ .as_nanos();
+ let temporary_name = format!("ollama-{}-{}-{nonce}", spec.version, std::process::id());
+ let archive_path = runtime_root.join(format!(".{temporary_name}.download"));
+ let staging = runtime_root.join(format!(".{temporary_name}.partial"));
+ let result = (|| {
+ download_archive(artifact, &archive_path, cancellation, progress)?;
+ if cancellation.is_cancelled() {
+ return Err("the managed Ollama runtime setup was cancelled".into());
+ }
+ extract_archive(&archive_path, &staging, artifact.archive, cancellation)?;
+ let staged_executable = staging.join(&artifact.executable);
+ if !staged_executable.is_file() {
+ return Err(format!(
+ "The managed Ollama archive did not contain {}.",
+ artifact.executable.display()
+ ));
+ }
+ if target.exists() {
+ fs::remove_dir_all(&target).map_err(|error| {
+ format!("Could not replace the incomplete managed Ollama runtime: {error}")
+ })?;
+ }
+ fs::rename(&staging, &target)
+ .map_err(|error| format!("Could not activate the managed Ollama runtime: {error}"))?;
+ let executable = target.join(&artifact.executable);
+ Ok(fs::canonicalize(&executable).unwrap_or(executable))
+ })();
+ let _ = fs::remove_file(&archive_path);
+ if staging.exists() {
+ let _ = fs::remove_dir_all(&staging);
+ }
+ result
+}
+
#[cfg(test)]
mod tests {
use super::*;
- #[test]
- fn supported_install_plans_never_run_an_unattended_shell_script() {
- if let Some(plan) = system_install_plan(|name| Some(PathBuf::from(name))) {
- let command = plan.command.join(" ").to_ascii_lowercase();
- assert!(!command.contains("curl"));
- assert!(!command.contains("powershell"));
- assert!(!command.contains("sh -"));
+ fn temporary_root(label: &str) -> PathBuf {
+ std::env::temp_dir().join(format!(
+ "vidxp-{label}-{}-{}",
+ std::process::id(),
+ SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .expect("clock")
+ .as_nanos()
+ ))
+ }
+
+ fn runtime_spec() -> ManagedRuntimeSpec {
+ ManagedRuntimeSpec {
+ version: "0.32.5".into(),
+ maximum_download_size_bytes: 10,
+ artifacts: BTreeMap::from([(
+ current_platform_key().unwrap_or("unsupported").into(),
+ ManagedRuntimeArtifactSpec {
+ url: "https://example.invalid/ollama.zip".into(),
+ sha256: "00".repeat(32),
+ download_size_bytes: 10,
+ archive: ManagedRuntimeArchive::Zip,
+ executable: PathBuf::from(if cfg!(windows) {
+ "ollama.exe"
+ } else {
+ "ollama"
+ }),
+ },
+ )]),
}
}
@@ -150,21 +443,80 @@ mod tests {
assert_eq!(version_meets_minimum("unknown", (14, 0, 0)), None);
}
- #[cfg(windows)]
#[test]
- fn windows_install_is_explicit_and_non_interactive() {
- let plan =
- system_install_plan(|name| (name == "winget").then(|| PathBuf::from("winget.exe")))
- .expect("install plan");
- assert!(
- plan.command
- .windows(2)
- .any(|pair| pair == ["--id", "Ollama.Ollama"])
+ fn managed_runtime_version_cannot_escape_its_owned_root() {
+ let mut spec = runtime_spec();
+ spec.version = "../escape".into();
+ assert!(managed_runtime_directory(Path::new("runtime-root"), &spec).is_err());
+ }
+
+ #[test]
+ fn managed_executable_requires_the_expected_file() {
+ let root = temporary_root("managed-ollama-path");
+ let spec = runtime_spec();
+ assert_eq!(managed_executable(&root, &spec), None);
+ fs::remove_dir_all(root).ok();
+ }
+
+ #[test]
+ fn zip_runtime_archive_extracts_only_expected_files() {
+ let root = temporary_root("managed-ollama-zip");
+ let archive_path = root.join("runtime.zip");
+ let destination = root.join("extracted");
+ fs::create_dir_all(&root).expect("temporary root");
+ let archive_file = File::create(&archive_path).expect("archive file");
+ let mut archive = zip::ZipWriter::new(archive_file);
+ archive
+ .start_file(
+ "ollama.exe",
+ zip::write::SimpleFileOptions::default()
+ .compression_method(zip::CompressionMethod::Deflated),
+ )
+ .expect("archive entry");
+ archive.write_all(b"headless-runtime").expect("entry data");
+ archive.finish().expect("finished archive");
+
+ extract_zip(&archive_path, &destination, &CancellationToken::default())
+ .expect("extracted archive");
+
+ assert_eq!(
+ fs::read(destination.join("ollama.exe")).expect("extracted executable"),
+ b"headless-runtime"
);
- assert!(
- plan.command
- .iter()
- .any(|value| value == "--disable-interactivity")
+ fs::remove_dir_all(root).expect("temporary cleanup");
+ }
+
+ #[test]
+ fn tar_runtime_archive_extracts_only_expected_files() {
+ let root = temporary_root("managed-ollama-tar");
+ let archive_path = root.join("runtime.tgz");
+ let destination = root.join("extracted");
+ fs::create_dir_all(&root).expect("temporary root");
+ let archive_file = File::create(&archive_path).expect("archive file");
+ let encoder = flate2::write::GzEncoder::new(archive_file, flate2::Compression::default());
+ let mut archive = tar::Builder::new(encoder);
+ let contents = b"headless-runtime";
+ let mut header = tar::Header::new_gnu();
+ header.set_size(contents.len() as u64);
+ header.set_mode(0o755);
+ header.set_cksum();
+ archive
+ .append_data(&mut header, "ollama", &contents[..])
+ .expect("archive entry");
+ archive
+ .into_inner()
+ .expect("archive encoder")
+ .finish()
+ .expect("finished archive");
+ fs::create_dir_all(&destination).expect("extraction destination");
+
+ extract_tar_gz(&archive_path, &destination, &CancellationToken::default())
+ .expect("extracted archive");
+
+ assert_eq!(
+ fs::read(destination.join("ollama")).expect("extracted executable"),
+ contents
);
+ fs::remove_dir_all(root).expect("temporary cleanup");
}
}
diff --git a/desktop/src/App.test.tsx b/desktop/src/App.test.tsx
index d16d8eee..d2ef472d 100644
--- a/desktop/src/App.test.tsx
+++ b/desktop/src/App.test.tsx
@@ -96,7 +96,7 @@ describe('desktop target lifecycle', () => {
browser: { extra: 'frontend', label: 'Browser interface', description: 'Open VidXP in your browser.', default: true },
mcp: { extra: 'mcp', label: 'AI assistant integration', description: 'Connect a compatible AI app.', default: false },
server: { extra: 'server', label: 'App integration service', description: 'Let other local apps connect.', default: false },
- }, local_answers: { engine: 'ollama', model: 'qwen3.5:4b-q4_K_M', download_size_bytes: 3650722202, label: 'Local grounded answers', description: 'Turn search evidence into cited answers locally.' } });
+ }, local_answers: { engine: 'ollama', model: 'qwen3.5:4b-q4_K_M', download_size_bytes: 3650722202, managed_runtime: { version: '0.32.5', maximum_download_size_bytes: 1457824795 }, label: 'Local grounded answers', description: 'Turn search evidence into cited answers locally.' } });
mocks.runtimeStatus.mockResolvedValue({ state: 'never_configured', ready: false, runtime_profile: null, package_version: '0.4.0', capabilities: [], surfaces: [], model_directory: 'C:\\Models', detail: 'No managed runtime yet.' });
mocks.modelDirectoryInventory.mockResolvedValue({ directory: 'C:\\Models', exists: false, readable: true, total_bytes: 0, file_count: 0, recognized_models: [], empty: true, verification_required: false, truncated: false, detail: 'Empty.' });
mocks.installMediaRuntime.mockResolvedValue({ ready: true });
@@ -524,7 +524,7 @@ describe('desktop target lifecycle', () => {
await user.click(screen.getByRole('checkbox', { name: /Local grounded answers/i }));
expect(screen.getByText(/There is no URL to enter/i)).toBeVisible();
- expect(screen.getByText(/grounded-answer model adds 3.40 GiB/i)).toBeVisible();
+ expect(screen.getByText(/Grounded answers add up to 1.36 GiB for the headless runtime and 3.40 GiB for the model/i)).toBeVisible();
await user.click(screen.getByRole('button', { name: 'Install VidXP' }));
expect(mocks.installMediaRuntime).toHaveBeenCalledWith('draft-1', 9);
@@ -651,6 +651,15 @@ describe('desktop target lifecycle', () => {
reportProgress?.({ draft_id: 'draft-1', current: 4, total: 8, stage: 'dependencies', message: 'Installing the selected search features' });
expect(await screen.findByText('Step 4 of 8')).toBeVisible();
expect(screen.getByText('Installing the selected search features')).toBeVisible();
+ reportProgress?.({
+ draft_id: 'draft-1',
+ current: 2,
+ total: 9,
+ stage: 'local-answers',
+ message: 'Preparing the local grounded-answer model',
+ model_message: 'Downloading the headless Ollama 0.32.5 runtime',
+ });
+ expect(await screen.findByText(/No separate app or setup window is involved/i)).toBeVisible();
reportProgress?.({
draft_id: 'draft-1',
current: 7,
diff --git a/desktop/src/components/ManagedSetup.tsx b/desktop/src/components/ManagedSetup.tsx
index 95126656..0795e9b3 100644
--- a/desktop/src/components/ManagedSetup.tsx
+++ b/desktop/src/components/ManagedSetup.tsx
@@ -416,11 +416,16 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
engine: 'ollama',
model: 'qwen3.5:4b-q4_K_M',
download_size_bytes: 3650722202,
+ managed_runtime: {
+ version: '0.32.5',
+ maximum_download_size_bytes: 1457824795,
+ },
label: 'Local grounded answers',
description: 'Turn VidXP search evidence into cited answers on this computer.',
};
const localAnswerModelBytes = localAnswers ? localAnswerSpec.download_size_bytes : 0;
- const plannedSetupBytes = managedRuntimeBytes + selectedModelBytes + localAnswerModelBytes;
+ const localAnswerRuntimeBytes = localAnswers ? localAnswerSpec.managed_runtime.maximum_download_size_bytes : 0;
+ const plannedSetupBytes = managedRuntimeBytes + selectedModelBytes + localAnswerModelBytes + localAnswerRuntimeBytes;
const capabilityModelSummary = capabilities
.map((id) => {
const capability = manifest?.capabilities[id];
@@ -524,7 +529,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
/>
{localAnswers && (
- VidXP checks for Ollama, asks before installing it, starts only a VidXP-owned service when needed, and configures the browser, API, worker, Premiere, and MCP surfaces automatically. There is no URL to enter.
+ VidXP reuses a healthy Ollama service or executable when available. Otherwise it asks before downloading a verified headless runtime into VidXP's private data; it never installs the Ollama desktop app. There is no URL to enter.
)}
@@ -553,7 +558,7 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
} loading={operation === 'folder'} disabled={isBusy} onClick={() => void chooseFolder()}>Change location…
- The managed runtime can use approximately {formatBytes(managedRuntimeBytes)}. Selected model downloads total up to {formatBytes(selectedModelBytes)}.{localAnswers ? ` The grounded-answer model adds ${formatBytes(localAnswerModelBytes)}.` : ''}
+ The managed runtime can use approximately {formatBytes(managedRuntimeBytes)}. Selected model downloads total up to {formatBytes(selectedModelBytes)}.{localAnswers ? ` Grounded answers add up to ${formatBytes(localAnswerRuntimeBytes)} for the headless runtime and ${formatBytes(localAnswerModelBytes)} for the model.` : ''}
{capabilityModelSummary}
Plan for approximately {formatBytes(plannedSetupBytes)} locally, plus temporary installation space, indexes, and videos. Valid cached model files are reused.
@@ -661,7 +666,11 @@ export function ManagedSetup({ draftId, selectedManagedRuntimeProfile, premiereR
)}
{setupProgress?.message ?? 'Starting managed setup'}
- The existing installation remains active until every step has completed and the replacement passes validation.
+
+ {setupProgress?.stage === 'local-answers'
+ ? 'VidXP reuses Ollama or manages a private headless runtime. No separate app or setup window is involved.'
+ : 'The existing installation remains active until every step has completed and the replacement passes validation.'}
+
{cancelFailure && {cancelFailure} }
diff --git a/desktop/src/tauri.ts b/desktop/src/tauri.ts
index 43c98446..cfe6275c 100644
--- a/desktop/src/tauri.ts
+++ b/desktop/src/tauri.ts
@@ -154,6 +154,10 @@ export interface RuntimeManifest {
engine: string;
model: string;
download_size_bytes: number;
+ managed_runtime: {
+ version: string;
+ maximum_download_size_bytes: number;
+ };
label: string;
description: string;
};
diff --git a/docs/architecture/platform.md b/docs/architecture/platform.md
index ca55ee19..a0b03b29 100644
--- a/docs/architecture/platform.md
+++ b/docs/architecture/platform.md
@@ -870,17 +870,22 @@ self-hosted Ollama base URL selects that model unless an operator explicitly
overrides it. VidXP sets temperature zero, disables reasoning output, and
requires the native JSON schemas for both planning and synthesis. Model weights
are never bundled. Desktop setup pulls the approved artifact only after the
-user selects local grounded answers and approves any required Ollama install;
-CLI and server operators pull it explicitly.
-
-Desktop treats the provider as an optional supervised system dependency. It
-first probes the loopback `/api/version` and `/api/tags` contracts, reuses an
-existing healthy service without taking ownership, or starts a child
-`ollama serve` process that its existing process-tree supervisor owns. The
-model pull uses Ollama's streaming `/api/pull` contract. Desktop persists only
-the feature selection, injects the private `/v1` endpoint and approved model
-into managed processes, and includes the same non-secret environment in stdio
-MCP configuration. It never stops an externally owned Ollama service.
+user selects local grounded answers and approves any required headless-runtime
+download; CLI and server operators pull it explicitly.
+
+Desktop treats the provider as an optional supervised runtime. It first probes
+the loopback `/api/version` and `/api/tags` contracts and reuses an existing
+healthy service without taking ownership. It next reuses an existing Ollama
+executable. If neither is available on a supported Desktop target, it downloads
+the pinned official headless archive declared in the embedded runtime manifest,
+verifies its expected byte count and SHA-256 digest, and atomically activates it
+under Desktop's private application data. Desktop never installs the Ollama
+desktop app. It starts a child `ollama serve` process that its existing
+process-tree supervisor owns, and the model pull uses Ollama's streaming
+`/api/pull` contract. Desktop persists only the feature selection, injects the
+private `/v1` endpoint and approved model into managed processes, and includes
+the same non-secret environment in stdio MCP configuration. It never stops an
+externally owned Ollama service.
Published model results select the integration candidate; the repository gate
does not attempt to reproduce general model leaderboards. Promotion still
diff --git a/docs/benchmarking/agent_ablation.md b/docs/benchmarking/agent_ablation.md
index e26e4a03..9ffdcf9c 100644
--- a/docs/benchmarking/agent_ablation.md
+++ b/docs/benchmarking/agent_ablation.md
@@ -108,6 +108,9 @@ uv sync --frozen --extra local-worker --extra mcp --extra benchmarks
npm --prefix benchmarks/codex-mcp ci
```
+The benchmark pins the Codex SDK directly and omits Promptfoo's unrelated
+optional provider packages from this install.
+
Create all mutable state outside the checkout. The paths below are examples;
keep the same values for both conditions:
diff --git a/docs/desktop.md b/docs/desktop.md
index d09af8eb..3e1917ae 100644
--- a/docs/desktop.md
+++ b/docs/desktop.md
@@ -204,6 +204,17 @@ wait for user confirmation before running either one. On Linux, setup shows an
APT, DNF, or manual command and leaves elevation to the user. Existing
installations remain responsible for their own media setup.
+Local grounded answers do not justify installing another desktop application.
+Desktop reuses a healthy Ollama service without taking ownership, then checks
+for an existing executable. When neither is available on Windows x86-64 or
+macOS Apple Silicon, it downloads the pinned headless archive declared in
+`runtime-manifest.json`, verifies its byte count and SHA-256 digest, extracts it
+into Desktop's private application data, and starts `ollama serve` through the
+shared process supervisor. Downloads are cancellable, incomplete archives and
+staging directories are removed, and only a completely extracted version is
+activated. Linux and unsupported architectures require an external Ollama
+installation. Desktop never invokes an Ollama desktop-app installer.
+
Starting Desktop shows the control panel without opening a browser. **Open
VidXP** starts or reuses the browser service and opens one tab. Closing the
configured window hides it in the system tray. **Quit VidXP** stops only the
diff --git a/docs/local-api.md b/docs/local-api.md
index d74d5703..737a3482 100644
--- a/docs/local-api.md
+++ b/docs/local-api.md
@@ -93,12 +93,14 @@ evidence. VidXP falls back to deterministic evidence retrieval when Ollama is
not configured or unavailable.
For VidXP Desktop, open **Setup options** and enable **Local grounded
-answers**. Desktop performs the Ollama health check, asks before installing a
-supported system package, downloads the approved model with visible progress,
-and carries the non-secret local provider settings into every managed surface,
-including copied stdio MCP JSON and **Set up in Codex**. If Desktop starts
-`ollama serve`, it supervises and stops only that owned process. It never stops
-an Ollama app or service that was already running.
+answers**. Desktop first reuses a healthy Ollama service or existing executable.
+On supported Desktop platforms, it otherwise asks before downloading the
+pinned, checksum-verified headless runtime into VidXP's private data. It never
+installs the Ollama desktop app. Desktop downloads the approved model with
+visible progress and carries the non-secret local provider settings into every
+managed surface, including copied stdio MCP JSON and **Set up in Codex**. If
+Desktop starts `ollama serve`, it supervises and stops only that owned process.
+It never stops an Ollama app or service that was already running.
The commands below are only for command-line installations and custom
deployments.
@@ -126,13 +128,15 @@ $env:VIDXP_SLM_BASE_URL = "http://127.0.0.1:11434/v1"
vidxp query "When does the taxi arrive?"
```
-The official Q4_K_M model download is approximately 3.4 GB. It runs locally,
-so there is no model API fee or numbered hosted-model run; it still uses local
-storage, memory, compute time, and electricity. Desktop downloads it only when
-the user selects the feature; CLI users pull it explicitly. VidXP never bundles
-the model with the Python or Desktop packages. A reused external Ollama service
-continues to own its model storage; VidXP does not claim those files are in its
-search-model cache.
+The official Q4_K_M model download is approximately 3.4 GB. A Desktop-managed
+headless runtime can add up to approximately 1.36 GiB; an existing service or
+executable avoids that download. Local inference has no model API fee or
+numbered hosted-model run, but it still uses local storage, memory, compute time,
+and electricity. Desktop downloads the model only when the user selects the
+feature; CLI users pull it explicitly. VidXP never bundles the model with the
+Python or Desktop packages. A reused external Ollama service continues to own
+its model storage; VidXP does not claim those files are in its search-model
+cache.
The current query adapter sends structured search evidence, not video or audio
bytes, to Qwen. Speech transcripts can support generated factual claims.
diff --git a/tests/test_packaging.py b/tests/test_packaging.py
index bbf2df44..ba7bd9d0 100644
--- a/tests/test_packaging.py
+++ b/tests/test_packaging.py
@@ -1,4 +1,5 @@
import json
+import re
import subprocess
import sys
import tarfile
@@ -588,6 +589,30 @@ def test_optional_ollama_profile_never_pulls_a_model_implicitly(self):
)
self.assertNotIn("ollama pull", compose.lower())
+ def test_bundled_chroma_service_is_not_published(self):
+ compose = (ROOT / "compose.coolify.yaml").read_text(
+ encoding="utf-8"
+ )
+ service = re.search(
+ r"(?ms)^ chroma:\n(?P.*?)(?=^ [a-z0-9_-]+:\n|\Z)",
+ compose,
+ )
+
+ self.assertIsNotNone(service)
+ body = service.group("body")
+ self.assertNotRegex(body, r"(?m)^ ports:\s*$")
+ self.assertNotRegex(body, r"(?m)^ network_mode:\s*host\s*$")
+
+ def test_codex_benchmark_omits_unused_optional_providers(self):
+ benchmark = ROOT / "benchmarks" / "codex-mcp"
+ npm_config = (benchmark / ".npmrc").read_text(encoding="utf-8")
+ package = json.loads(
+ (benchmark / "package.json").read_text(encoding="utf-8")
+ )
+
+ self.assertIn("omit=optional", npm_config.splitlines())
+ self.assertIn("@openai/codex-sdk", package["devDependencies"])
+
def test_desktop_manifest_matches_published_package_contract(self):
project = tomllib.loads(
(ROOT / "pyproject.toml").read_text(encoding="utf-8")
From 1debd6140c1417acde30c51241b9fb5815bfcbe0 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Tue, 1 Sep 2026 16:24:46 +0500
Subject: [PATCH 7/9] fix(release): update uv lock version structurally (#138)
---
release-please-config.json | 3 ++-
release-please-config.stable.json | 3 ++-
tests/test_packaging.py | 20 ++++++++++++--------
uv.lock | 2 +-
4 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/release-please-config.json b/release-please-config.json
index 43e6a57b..12817680 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -13,8 +13,9 @@
".": {
"extra-files": [
{
+ "jsonpath": "$.package[?(@.name.value==\"vidxp\")].version",
"path": "uv.lock",
- "type": "generic"
+ "type": "toml"
},
{
"jsonpath": "$.version",
diff --git a/release-please-config.stable.json b/release-please-config.stable.json
index dbff67cc..258f3500 100644
--- a/release-please-config.stable.json
+++ b/release-please-config.stable.json
@@ -16,8 +16,9 @@
"type": "json"
},
{
+ "jsonpath": "$.package[?(@.name.value==\"vidxp\")].version",
"path": "uv.lock",
- "type": "generic"
+ "type": "toml"
},
{
"jsonpath": "$.version",
diff --git a/tests/test_packaging.py b/tests/test_packaging.py
index ba7bd9d0..fabc303c 100644
--- a/tests/test_packaging.py
+++ b/tests/test_packaging.py
@@ -751,10 +751,7 @@ def test_combined_release_version_contract(self):
}
self.assertEqual(
generic_files,
- {
- "uv.lock",
- "desktop/src-tauri/Cargo.toml",
- },
+ {"desktop/src-tauri/Cargo.toml"},
filename,
)
toml_files = {
@@ -765,6 +762,9 @@ def test_combined_release_version_contract(self):
self.assertEqual(
toml_files,
{
+ "uv.lock": (
+ '$.package[?(@.name.value=="vidxp")].version'
+ ),
"desktop/src-tauri/Cargo.lock": (
'$.package[?(@.name.value=="vidxp-desktop")].version'
)
@@ -840,11 +840,15 @@ def test_combined_release_version_contract(self):
if package["name"] == "vidxp-desktop"
)
self.assertEqual(desktop_lock["version"], version)
- self.assertIn(
- version_marker,
- (ROOT / "uv.lock").read_text(encoding="utf-8"),
- "uv.lock",
+ uv_lock = tomllib.loads(
+ (ROOT / "uv.lock").read_text(encoding="utf-8")
+ )
+ project_lock = next(
+ package
+ for package in uv_lock["package"]
+ if package["name"] == "vidxp"
)
+ self.assertEqual(project_lock["version"], version)
self.assertNotIn(
f"vidxp=={version}",
(
diff --git a/uv.lock b/uv.lock
index 7e6a9c9e..159418d3 100644
--- a/uv.lock
+++ b/uv.lock
@@ -4561,7 +4561,7 @@ wheels = [
[[package]]
name = "vidxp"
-version = "0.4.0" # x-release-please-version
+version = "0.4.0"
source = { editable = "." }
dependencies = [
{ name = "dbos" },
From 446e97c0f5981e15688a1154ae18148c298b3b47 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Tue, 1 Sep 2026 18:18:02 +0500
Subject: [PATCH 8/9] test(benchmarks): automate Promptfoo setup (#139)
---
benchmarks/codex-mcp/package.json | 13 +-
benchmarks/codex-mcp/scripts/require-node.mjs | 8 +
benchmarks/codex-mcp/scripts/setup-lib.mjs | 87 ++++++
benchmarks/codex-mcp/scripts/setup.mjs | 265 ++++++++++++++++++
benchmarks/codex-mcp/scripts/setup.test.mjs | 75 +++++
docs/benchmarking/agent_ablation.md | 128 ++-------
6 files changed, 467 insertions(+), 109 deletions(-)
create mode 100644 benchmarks/codex-mcp/scripts/require-node.mjs
create mode 100644 benchmarks/codex-mcp/scripts/setup-lib.mjs
create mode 100644 benchmarks/codex-mcp/scripts/setup.mjs
create mode 100644 benchmarks/codex-mcp/scripts/setup.test.mjs
diff --git a/benchmarks/codex-mcp/package.json b/benchmarks/codex-mcp/package.json
index f0643007..2621e83a 100644
--- a/benchmarks/codex-mcp/package.json
+++ b/benchmarks/codex-mcp/package.json
@@ -7,11 +7,14 @@
"node": ">=22.22.0"
},
"scripts": {
- "check": "promptfoo validate -c promptfooconfig.yaml",
- "preflight": "node scripts/preflight.mjs",
- "eval:smoke": "npm run preflight && promptfoo eval -c promptfooconfig.yaml --filter-first-n 2 --repeat 1 --no-cache --no-share",
- "eval:pilot": "npm run preflight && promptfoo eval -c promptfooconfig.yaml --repeat 3 --no-cache --no-share",
- "view": "promptfoo view"
+ "setup": "node scripts/setup.mjs",
+ "test:setup": "node --test scripts/setup.test.mjs",
+ "promptfoo": "node --env-file=.env node_modules/promptfoo/dist/src/entrypoint.js",
+ "check": "node scripts/require-node.mjs && node --env-file-if-exists=.env node_modules/promptfoo/dist/src/entrypoint.js validate -c promptfooconfig.yaml",
+ "preflight": "node --env-file=.env scripts/preflight.mjs",
+ "eval:smoke": "npm run preflight && npm run promptfoo -- eval -c promptfooconfig.yaml --filter-first-n 2 --repeat 1 --no-cache --no-share",
+ "eval:pilot": "npm run preflight && npm run promptfoo -- eval -c promptfooconfig.yaml --repeat 3 --no-cache --no-share",
+ "view": "npm run promptfoo -- view"
},
"devDependencies": {
"@openai/codex-sdk": "0.151.0",
diff --git a/benchmarks/codex-mcp/scripts/require-node.mjs b/benchmarks/codex-mcp/scripts/require-node.mjs
new file mode 100644
index 00000000..fa7a74de
--- /dev/null
+++ b/benchmarks/codex-mcp/scripts/require-node.mjs
@@ -0,0 +1,8 @@
+import { REQUIRED_NODE_VERSION, versionAtLeast } from './setup-lib.mjs';
+
+if (!versionAtLeast(process.versions.node)) {
+ process.stderr.write(
+ `Node.js ${REQUIRED_NODE_VERSION.join('.')} or newer is required; found ${process.versions.node}.\n`,
+ );
+ process.exitCode = 1;
+}
diff --git a/benchmarks/codex-mcp/scripts/setup-lib.mjs b/benchmarks/codex-mcp/scripts/setup-lib.mjs
new file mode 100644
index 00000000..d4316561
--- /dev/null
+++ b/benchmarks/codex-mcp/scripts/setup-lib.mjs
@@ -0,0 +1,87 @@
+import { homedir } from 'node:os';
+import { posix, win32 } from 'node:path';
+
+export const REQUIRED_NODE_VERSION = [22, 22, 0];
+
+export function versionAtLeast(actual, required = REQUIRED_NODE_VERSION) {
+ const parts = actual.split('.').map(Number);
+ return required.every((requiredPart, index) => {
+ const actualPart = parts[index] ?? 0;
+ const prefixMatches = required
+ .slice(0, index)
+ .every((part, prefixIndex) => (parts[prefixIndex] ?? 0) === part);
+ return !prefixMatches || actualPart >= requiredPart;
+ });
+}
+
+export function defaultEvaluationRoot(environment, platform = process.platform) {
+ const paths = platform === 'win32' ? win32 : posix;
+ if (environment.VIDXP_EVAL_ROOT) {
+ return paths.resolve(environment.VIDXP_EVAL_ROOT);
+ }
+ if (platform === 'win32') {
+ if (!environment.LOCALAPPDATA) {
+ throw new Error('LOCALAPPDATA is required when VIDXP_EVAL_ROOT is unset.');
+ }
+ return paths.join(environment.LOCALAPPDATA, 'VidXP', 'benchmarks', 'codex-mcp');
+ }
+ const dataHome = environment.XDG_DATA_HOME || paths.join(homedir(), '.local', 'share');
+ return paths.join(dataHome, 'vidxp', 'benchmarks', 'codex-mcp');
+}
+
+export function evaluationEnvironment({
+ benchmarkRoot,
+ repositoryRoot,
+ evaluationRoot,
+ environment = process.env,
+ platform = process.platform,
+}) {
+ const paths = platform === 'win32' ? win32 : posix;
+ const executable = platform === 'win32' ? 'vidxp-mcp.exe' : 'vidxp-mcp';
+ const scriptsDirectory = platform === 'win32' ? 'Scripts' : 'bin';
+ return {
+ VIDXP_EVAL_CODEX_HOME: paths.join(evaluationRoot, 'codex-home'),
+ VIDXP_EVAL_WORKSPACE: paths.join(evaluationRoot, 'workspace'),
+ VIDXP_EVAL_DATA_DIR: paths.join(evaluationRoot, 'vidxp-data'),
+ VIDXP_EVAL_INDEX_DIR: paths.join(evaluationRoot, 'vidxp-index'),
+ VIDXP_MCP_COMMAND: paths.join(repositoryRoot, '.venv', scriptsDirectory, executable),
+ VIDXP_EVAL_REPOSITORY: environment.VIDXP_EVAL_REPOSITORY || 'default',
+ VIDXP_EVAL_DEVICE: environment.VIDXP_EVAL_DEVICE || 'cpu',
+ VIDXP_EVAL_MODEL: environment.VIDXP_EVAL_MODEL || 'gpt-5.6-sol',
+ VIDXP_EVAL_REASONING: environment.VIDXP_EVAL_REASONING || 'medium',
+ VIDXP_EVAL_ARTIFACT_DIR: paths.join(evaluationRoot, 'longvale-artifacts'),
+ VIDXP_EVAL_ENV_FILE: paths.join(benchmarkRoot, '.env'),
+ };
+}
+
+export function serializeEnvironment(environment) {
+ return Object.entries(environment)
+ .filter(([name]) => name !== 'VIDXP_EVAL_ARTIFACT_DIR' && name !== 'VIDXP_EVAL_ENV_FILE')
+ .map(([name, value]) => `${name}=${JSON.stringify(value.replaceAll('\\', '/'))}`)
+ .join('\n') + '\n';
+}
+
+export function indexContainsPilot(index, videoIds, modalities) {
+ if (!index) {
+ return false;
+ }
+ const filenames = new Set((index.items || []).map((item) => item.original_filename));
+ const indexedModalities = new Set(index.modalities || []);
+ return videoIds.every((id) => filenames.has(`${id}.mp4`))
+ && modalities.every((modality) => indexedModalities.has(modality));
+}
+
+export function libsqlBindingName(platform, architecture, glibcVersion = undefined) {
+ if (platform === 'win32' && architecture === 'x64') {
+ return '@libsql/win32-x64-msvc';
+ }
+ if (platform === 'darwin' && ['arm64', 'x64'].includes(architecture)) {
+ return `@libsql/darwin-${architecture}`;
+ }
+ if (platform === 'linux' && ['arm', 'arm64', 'x64'].includes(architecture)) {
+ const libc = glibcVersion ? (architecture === 'arm' ? 'gnueabihf' : 'gnu')
+ : (architecture === 'arm' ? 'musleabihf' : 'musl');
+ return `@libsql/linux-${architecture}-${libc}`;
+ }
+ throw new Error(`Promptfoo has no pinned libsql binding for ${platform}-${architecture}.`);
+}
diff --git a/benchmarks/codex-mcp/scripts/setup.mjs b/benchmarks/codex-mcp/scripts/setup.mjs
new file mode 100644
index 00000000..416d5fd8
--- /dev/null
+++ b/benchmarks/codex-mcp/scripts/setup.mjs
@@ -0,0 +1,265 @@
+import { createHash } from 'node:crypto';
+import { spawnSync } from 'node:child_process';
+import {
+ copyFileSync,
+ createReadStream,
+ existsSync,
+ mkdirSync,
+ readFileSync,
+ writeFileSync,
+} from 'node:fs';
+import { dirname, join, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import {
+ REQUIRED_NODE_VERSION,
+ defaultEvaluationRoot,
+ evaluationEnvironment,
+ indexContainsPilot,
+ libsqlBindingName,
+ serializeEnvironment,
+ versionAtLeast,
+} from './setup-lib.mjs';
+
+const benchmarkRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
+const repositoryRoot = resolve(benchmarkRoot, '..', '..');
+const manifestPath = join(benchmarkRoot, 'tasks', 'longvale-part9-pilot.json');
+const datasetRevision = '18889b01886e30c36b0d1c650ac4439ad460ee73';
+const archiveHash = 'c83d62557f102c6d41ea95c2c3b3581657481c8646cc70b1e12a85ead27a7ae3';
+const archiveRelativePath = join('raw_videos_test', 'LongVALE_test_1171_part_9.zip');
+const annotationFilename = 'longvale-annotations-eval.json';
+const modalities = ['scene', 'action', 'sound', 'speech'];
+
+function executableName(command) {
+ return process.platform === 'win32' && command === 'npm' ? 'npm.cmd' : command;
+}
+
+function formatCommand(command, args) {
+ return [command, ...args]
+ .map((part) => (/\s/.test(part) ? JSON.stringify(part) : part))
+ .join(' ');
+}
+
+function run(command, args, { cwd = repositoryRoot, env = process.env, capture = false } = {}) {
+ process.stdout.write(`\n> ${formatCommand(command, args)}\n`);
+ const result = spawnSync(executableName(command), args, {
+ cwd,
+ env,
+ encoding: capture ? 'utf8' : undefined,
+ stdio: capture ? 'pipe' : 'inherit',
+ });
+ if (result.error) {
+ throw new Error(`Could not run ${command}: ${result.error.message}`);
+ }
+ if (result.status !== 0) {
+ const detail = capture ? `\n${result.stderr || result.stdout}` : '';
+ throw new Error(`${command} exited with status ${result.status}.${detail}`);
+ }
+ return capture ? result.stdout : '';
+}
+
+async function sha256(path) {
+ const hash = createHash('sha256');
+ for await (const chunk of createReadStream(path)) {
+ hash.update(chunk);
+ }
+ return hash.digest('hex');
+}
+
+function readIndex(environment) {
+ try {
+ const output = run(
+ 'uv',
+ [
+ 'run', '--no-sync', 'vidxp',
+ '--data-dir', environment.VIDXP_EVAL_DATA_DIR,
+ '--index-dir', environment.VIDXP_EVAL_INDEX_DIR,
+ 'index', 'list', '--json',
+ ],
+ { env: { ...process.env, ...environment }, capture: true },
+ );
+ return JSON.parse(output);
+ } catch {
+ return null;
+ }
+}
+
+async function main() {
+ if (!versionAtLeast(process.versions.node)) {
+ throw new Error(
+ `Node.js ${REQUIRED_NODE_VERSION.join('.')} or newer is required; found ${process.versions.node}.`,
+ );
+ }
+
+ run('uv', ['--version'], { capture: true });
+ run('codex', ['--version'], { capture: true });
+
+ const evaluationRoot = defaultEvaluationRoot(process.env);
+ const setupEnvironment = evaluationEnvironment({
+ benchmarkRoot,
+ repositoryRoot,
+ evaluationRoot,
+ });
+ const commandEnvironment = { ...process.env, ...setupEnvironment };
+ const tasks = JSON.parse(readFileSync(manifestPath, 'utf8'));
+ const videoIds = [...new Set(tasks.map((task) => task.video_id))];
+
+ run(
+ 'uv',
+ ['sync', '--frozen', '--extra', 'local-worker', '--extra', 'mcp', '--extra', 'benchmarks'],
+ );
+ run('npm', ['ci'], { cwd: benchmarkRoot });
+
+ const glibcVersion = process.report?.getReport().header.glibcVersionRuntime;
+ const bindingName = libsqlBindingName(process.platform, process.arch, glibcVersion);
+ const libsqlManifest = JSON.parse(readFileSync(
+ join(benchmarkRoot, 'node_modules', 'libsql', 'package.json'),
+ 'utf8',
+ ));
+ const bindingVersion = libsqlManifest.optionalDependencies?.[bindingName];
+ if (!bindingVersion) {
+ throw new Error(`The Promptfoo lock does not declare ${bindingName}.`);
+ }
+ run(
+ 'npm',
+ [
+ 'install', '--no-save', '--package-lock=false', '--omit=optional',
+ `${bindingName}@${bindingVersion}`,
+ ],
+ { cwd: benchmarkRoot },
+ );
+ run(
+ process.execPath,
+ [
+ join(benchmarkRoot, 'node_modules', 'promptfoo', 'dist', 'src', 'entrypoint.js'),
+ 'validate', '-c', join(benchmarkRoot, 'promptfooconfig.yaml'),
+ ],
+ { cwd: benchmarkRoot, env: commandEnvironment },
+ );
+
+ for (const directory of [
+ setupEnvironment.VIDXP_EVAL_CODEX_HOME,
+ setupEnvironment.VIDXP_EVAL_WORKSPACE,
+ join(setupEnvironment.VIDXP_EVAL_WORKSPACE, 'media'),
+ setupEnvironment.VIDXP_EVAL_DATA_DIR,
+ setupEnvironment.VIDXP_EVAL_INDEX_DIR,
+ setupEnvironment.VIDXP_EVAL_ARTIFACT_DIR,
+ ]) {
+ mkdirSync(directory, { recursive: true });
+ }
+ if (!existsSync(setupEnvironment.VIDXP_MCP_COMMAND)) {
+ throw new Error(`VidXP MCP executable was not created at ${setupEnvironment.VIDXP_MCP_COMMAND}.`);
+ }
+ writeFileSync(
+ setupEnvironment.VIDXP_EVAL_ENV_FILE,
+ serializeEnvironment(setupEnvironment),
+ 'utf8',
+ );
+
+ const authPath = join(setupEnvironment.VIDXP_EVAL_CODEX_HOME, 'auth.json');
+ if (!existsSync(authPath)) {
+ process.stdout.write('\nSign in to the isolated Codex profile when prompted.\n');
+ run('codex', ['login'], {
+ env: { ...commandEnvironment, CODEX_HOME: setupEnvironment.VIDXP_EVAL_CODEX_HOME },
+ });
+ }
+ if (!existsSync(authPath)) {
+ throw new Error('Codex login completed without creating auth.json in the isolated profile.');
+ }
+
+ process.stdout.write(
+ '\nDownloading the pinned LongVALE pilot files. Use of the dataset is subject to its published terms.\n',
+ );
+ run(
+ 'uvx',
+ [
+ 'hf', 'download', 'ttgeng233/LongVALE',
+ annotationFilename,
+ archiveRelativePath.replaceAll('\\', '/'),
+ '--repo-type', 'dataset',
+ '--revision', datasetRevision,
+ '--local-dir', setupEnvironment.VIDXP_EVAL_ARTIFACT_DIR,
+ ],
+ { env: commandEnvironment },
+ );
+
+ const archivePath = join(setupEnvironment.VIDXP_EVAL_ARTIFACT_DIR, archiveRelativePath);
+ const actualHash = await sha256(archivePath);
+ if (actualHash !== archiveHash) {
+ throw new Error(`LongVALE archive hash mismatch: expected ${archiveHash}, found ${actualHash}.`);
+ }
+
+ const sourceMedia = join(setupEnvironment.VIDXP_EVAL_ARTIFACT_DIR, 'video_test_1171');
+ if (videoIds.some((videoId) => !existsSync(join(sourceMedia, `${videoId}.mp4`)))) {
+ run(
+ 'uv',
+ ['run', '--no-sync', 'python', '-m', 'zipfile', '-e', archivePath, setupEnvironment.VIDXP_EVAL_ARTIFACT_DIR],
+ { env: commandEnvironment },
+ );
+ }
+ for (const videoId of videoIds) {
+ const source = join(sourceMedia, `${videoId}.mp4`);
+ if (!existsSync(source)) {
+ throw new Error(`The LongVALE archive did not contain ${source}.`);
+ }
+ copyFileSync(source, join(setupEnvironment.VIDXP_EVAL_WORKSPACE, 'media', `${videoId}.mp4`));
+ }
+
+ run(
+ 'uv',
+ [
+ 'run', '--no-sync', 'vidxp',
+ '--data-dir', setupEnvironment.VIDXP_EVAL_DATA_DIR,
+ '--index-dir', setupEnvironment.VIDXP_EVAL_INDEX_DIR,
+ 'prepare', '--modalities', modalities.join(','), '--yes',
+ ],
+ { env: commandEnvironment },
+ );
+
+ if (!indexContainsPilot(readIndex(setupEnvironment), videoIds, modalities)) {
+ for (const videoId of videoIds) {
+ process.stdout.write(`\nIndexing ${videoId}.mp4\n`);
+ const mediaPath = join(setupEnvironment.VIDXP_EVAL_WORKSPACE, 'media', `${videoId}.mp4`);
+ const imported = JSON.parse(run(
+ 'uv',
+ [
+ 'run', '--no-sync', 'vidxp',
+ '--data-dir', setupEnvironment.VIDXP_EVAL_DATA_DIR,
+ '--index-dir', setupEnvironment.VIDXP_EVAL_INDEX_DIR,
+ 'media', 'import', mediaPath, '--json',
+ ],
+ { env: commandEnvironment, capture: true },
+ ));
+ run(
+ 'uv',
+ [
+ 'run', '--no-sync', 'vidxp',
+ '--data-dir', setupEnvironment.VIDXP_EVAL_DATA_DIR,
+ '--index-dir', setupEnvironment.VIDXP_EVAL_INDEX_DIR,
+ 'index', 'create', imported.media_id,
+ ...modalities.flatMap((modality) => ['--modality', modality]),
+ ],
+ { env: commandEnvironment },
+ );
+ }
+ } else {
+ process.stdout.write('\nThe five pilot videos are already indexed; skipping indexing.\n');
+ }
+
+ run(
+ process.execPath,
+ [join(benchmarkRoot, 'scripts', 'preflight.mjs')],
+ { cwd: benchmarkRoot, env: commandEnvironment },
+ );
+
+ process.stdout.write(
+ '\nSetup complete. Run:\n'
+ + ' npm --prefix benchmarks/codex-mcp run eval:smoke\n'
+ + ' npm --prefix benchmarks/codex-mcp run eval:pilot\n',
+ );
+}
+
+main().catch((error) => {
+ process.stderr.write(`\nSetup failed: ${error.message}\n`);
+ process.exitCode = 1;
+});
diff --git a/benchmarks/codex-mcp/scripts/setup.test.mjs b/benchmarks/codex-mcp/scripts/setup.test.mjs
new file mode 100644
index 00000000..0d0cf18e
--- /dev/null
+++ b/benchmarks/codex-mcp/scripts/setup.test.mjs
@@ -0,0 +1,75 @@
+import assert from 'node:assert/strict';
+import { test } from 'node:test';
+
+import {
+ defaultEvaluationRoot,
+ evaluationEnvironment,
+ indexContainsPilot,
+ libsqlBindingName,
+ serializeEnvironment,
+ versionAtLeast,
+} from './setup-lib.mjs';
+
+test('checks the required Node version numerically', () => {
+ assert.equal(versionAtLeast('22.21.9'), false);
+ assert.equal(versionAtLeast('22.22.0'), true);
+ assert.equal(versionAtLeast('23.0.0'), true);
+});
+
+test('recognizes a complete pilot index regardless of extra media', () => {
+ const index = {
+ items: [
+ { original_filename: 'alpha.mp4' },
+ { original_filename: 'beta.mp4' },
+ { original_filename: 'unrelated.mp4' },
+ ],
+ modalities: ['scene', 'action', 'sound', 'speech'],
+ };
+
+ assert.equal(
+ indexContainsPilot(index, ['alpha', 'beta'], ['scene', 'action', 'sound', 'speech']),
+ true,
+ );
+ assert.equal(indexContainsPilot(index, ['alpha', 'missing'], ['scene']), false);
+ assert.equal(indexContainsPilot(index, ['alpha'], ['scene', 'ocr']), false);
+});
+
+test('uses one optional root override for mutable setup state', () => {
+ assert.equal(
+ defaultEvaluationRoot({ VIDXP_EVAL_ROOT: 'C:/custom/eval' }, 'win32'),
+ 'C:\\custom\\eval',
+ );
+ assert.equal(
+ defaultEvaluationRoot({ LOCALAPPDATA: 'C:/Users/test/AppData/Local' }, 'win32'),
+ 'C:\\Users\\test\\AppData\\Local\\VidXP\\benchmarks\\codex-mcp',
+ );
+ assert.equal(
+ defaultEvaluationRoot({ XDG_DATA_HOME: '/tmp/data' }, 'linux'),
+ '/tmp/data/vidxp/benchmarks/codex-mcp',
+ );
+});
+
+test('selects the required Promptfoo SQLite binding for the host', () => {
+ assert.equal(libsqlBindingName('win32', 'x64'), '@libsql/win32-x64-msvc');
+ assert.equal(libsqlBindingName('darwin', 'arm64'), '@libsql/darwin-arm64');
+ assert.equal(libsqlBindingName('linux', 'x64', '2.39'), '@libsql/linux-x64-gnu');
+ assert.equal(libsqlBindingName('linux', 'x64'), '@libsql/linux-x64-musl');
+ assert.throws(() => libsqlBindingName('win32', 'arm64'), /no pinned libsql binding/);
+});
+
+test('builds and serializes the environment consumed by Promptfoo', () => {
+ const environment = evaluationEnvironment({
+ benchmarkRoot: 'C:/repo/benchmarks/codex-mcp',
+ repositoryRoot: 'C:/repo',
+ evaluationRoot: 'C:/eval',
+ environment: {},
+ platform: 'win32',
+ });
+ const serialized = serializeEnvironment(environment);
+
+ assert.match(serialized, /VIDXP_EVAL_WORKSPACE="C:\/eval\/workspace"/);
+ assert.match(serialized, /VIDXP_MCP_COMMAND="C:\/repo\/\.venv\/Scripts\/vidxp-mcp\.exe"/);
+ assert.match(serialized, /VIDXP_EVAL_MODEL="gpt-5\.6-sol"/);
+ assert.doesNotMatch(serialized, /VIDXP_EVAL_ENV_FILE/);
+ assert.doesNotMatch(serialized, /VIDXP_EVAL_ARTIFACT_DIR/);
+});
diff --git a/docs/benchmarking/agent_ablation.md b/docs/benchmarking/agent_ablation.md
index 9ffdcf9c..d528de23 100644
--- a/docs/benchmarking/agent_ablation.md
+++ b/docs/benchmarking/agent_ablation.md
@@ -4,7 +4,7 @@ Collection index: [Benchmarking research](README.md)
Status: Runnable scaffold; no agent results recorded
-Last verified: 2026-08-30
+Last verified: 2026-09-01
This experiment measures whether access to VidXP through its local stdio MCP
server improves a Codex agent's ability to find timestamped evidence in long
@@ -100,128 +100,48 @@ therefore does not replace LongVALE in this ablation.
Promptfoo 0.122.2 requires Node.js 22.22.0 or newer. The benchmark-local
`.npmrc` enforces that requirement so an unsupported runtime fails during
-installation instead of failing after Codex runs have begun. Install VidXP and
-the local evaluation dependencies:
+installation instead of failing after Codex runs have begun. You also need
+`uv` and the Codex CLI on `PATH`.
-```powershell
-uv sync --frozen --extra local-worker --extra mcp --extra benchmarks
-npm --prefix benchmarks/codex-mcp ci
-```
-
-The benchmark pins the Codex SDK directly and omits Promptfoo's unrelated
-optional provider packages from this install.
-
-Create all mutable state outside the checkout. The paths below are examples;
-keep the same values for both conditions:
+From the repository root, run the automated setup:
```powershell
-$evalRoot = Join-Path $env:LOCALAPPDATA 'VidXP\benchmarks\codex-mcp'
-$env:VIDXP_EVAL_CODEX_HOME = Join-Path $evalRoot 'codex-home'
-$env:VIDXP_EVAL_WORKSPACE = Join-Path $evalRoot 'workspace'
-$env:VIDXP_EVAL_DATA_DIR = Join-Path $evalRoot 'vidxp-data'
-$env:VIDXP_EVAL_INDEX_DIR = Join-Path $evalRoot 'vidxp-index'
-$env:VIDXP_MCP_COMMAND = (Resolve-Path '.venv\Scripts\vidxp-mcp.exe').Path
-$env:VIDXP_EVAL_REPOSITORY = 'default'
-$env:VIDXP_EVAL_DEVICE = 'cpu'
-$env:VIDXP_EVAL_MODEL = 'gpt-5.6-sol'
-$env:VIDXP_EVAL_REASONING = 'medium'
-
-New-Item -ItemType Directory -Force `
- $env:VIDXP_EVAL_CODEX_HOME, `
- $env:VIDXP_EVAL_WORKSPACE, `
- (Join-Path $env:VIDXP_EVAL_WORKSPACE 'media'), `
- $env:VIDXP_EVAL_DATA_DIR, `
- $env:VIDXP_EVAL_INDEX_DIR | Out-Null
-
-$env:CODEX_HOME = $env:VIDXP_EVAL_CODEX_HOME
-codex login
-Remove-Item Env:CODEX_HOME
+npm --prefix benchmarks/codex-mcp run setup
```
-Do not copy or commit `auth.json`. The preflight rejects an isolated Codex
-configuration that declares any ambient `[mcp_servers]` section.
-
-## Fetch and index the pilot media
-
-Accept the LongVALE dataset terms before downloading. Fetch only the pinned
-annotation and part-nine evaluation archive:
-
-The commands below require the Hugging Face `hf` CLI. Install it separately if
-it is not already available; it is a dataset-transfer tool and is not part of
-VidXP's runtime dependency set.
+The command installs the pinned Python and Node dependencies, creates isolated
+state outside the checkout, opens Codex login when authentication is absent,
+downloads and verifies the pinned LongVALE archive, copies the five pilot
+videos, prepares the four required capabilities, indexes the media, saves the
+evaluation environment in the ignored `benchmarks/codex-mcp/.env` file, and
+runs preflight. Accept the LongVALE dataset terms before running it. Do not copy
+or commit the generated `auth.json`.
-```powershell
-$artifactRoot = Join-Path $evalRoot 'longvale-artifacts'
-hf download ttgeng233/LongVALE `
- longvale-annotations-eval.json `
- raw_videos_test/LongVALE_test_1171_part_9.zip `
- --repo-type dataset `
- --revision 18889b01886e30c36b0d1c650ac4439ad460ee73 `
- --local-dir $artifactRoot
-
-$archive = Join-Path $artifactRoot `
- 'raw_videos_test\LongVALE_test_1171_part_9.zip'
-(Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
-Expand-Archive -LiteralPath $archive -DestinationPath $artifactRoot
-```
-
-The printed hash must equal the pinned SHA-256 above. Copy the five selected
-MP4s into `$env:VIDXP_EVAL_WORKSPACE\media`, preserving their filenames:
+By default, mutable state goes under the operating system's user data
+directory. Set only `VIDXP_EVAL_ROOT` when it needs to live elsewhere:
```powershell
-$sourceMedia = Join-Path $artifactRoot 'video_test_1171'
-$videoIds = @(
- 'ZYTmgi1pAIE',
- 'ZIdFAGJrlCw',
- 'ZGXCr5n8Frg',
- '_py1WXVX4oc',
- 'ZVUAC3m48G0'
-)
-foreach ($videoId in $videoIds) {
- Copy-Item -LiteralPath (Join-Path $sourceMedia "$videoId.mp4") `
- -Destination (Join-Path $env:VIDXP_EVAL_WORKSPACE 'media')
-}
+$env:VIDXP_EVAL_ROOT = 'D:\vidxp-eval'
+npm --prefix benchmarks/codex-mcp run setup
```
-Prepare and index all four evidence paths:
-
-```powershell
-uv run --no-sync vidxp `
- --data-dir $env:VIDXP_EVAL_DATA_DIR `
- --index-dir $env:VIDXP_EVAL_INDEX_DIR `
- prepare --modalities scene,action,sound,speech --yes
-
-foreach ($videoId in $videoIds) {
- $mediaPath = Join-Path $env:VIDXP_EVAL_WORKSPACE "media\$videoId.mp4"
- $asset = uv run --no-sync vidxp `
- --data-dir $env:VIDXP_EVAL_DATA_DIR `
- --index-dir $env:VIDXP_EVAL_INDEX_DIR `
- media import $mediaPath --json | ConvertFrom-Json
-
- uv run --no-sync vidxp `
- --data-dir $env:VIDXP_EVAL_DATA_DIR `
- --index-dir $env:VIDXP_EVAL_INDEX_DIR `
- index create $asset.media_id `
- --modality scene `
- --modality action `
- --modality sound `
- --modality speech
-}
-```
+The setup is safe to rerun. Cached downloads and prepared models are reused,
+and indexing is skipped when all five videos and four modalities are already
+present. The benchmark pins the Codex SDK directly and omits Promptfoo's
+unrelated optional provider packages from the install.
## Validate before spending runs
-The following commands perform no Codex inference:
+Setup finishes by running preflight, which verifies the dedicated Codex
+authentication, absence of ambient MCP configuration, all five media files,
+the index paths, and a real VidXP MCP handshake. To repeat the configuration and
+preflight checks without setup or Codex inference, run:
```powershell
npm --prefix benchmarks/codex-mcp run check
npm --prefix benchmarks/codex-mcp run preflight
```
-Preflight verifies the dedicated Codex authentication, absence of ambient MCP
-configuration, all five media files, the index paths, and a real VidXP MCP
-handshake. Do not run the matrix if it fails.
-
The first paid/allowance-consuming smoke is one task in both conditions: two
Codex runs total.
From 24b916affcb67f16c7add911c94ad6eaf6d45a73 Mon Sep 17 00:00:00 2001
From: Talha Amjad
Date: Tue, 1 Sep 2026 19:27:53 +0500
Subject: [PATCH 9/9] fix(benchmarks): complete local setup and CI scope (#140)
* fix(benchmarks): initialize media runtime in setup
* fix(ci): skip artifact builds for benchmark changes
---
benchmarks/codex-mcp/scripts/setup.mjs | 5 +++++
docs/benchmarking/agent_ablation.md | 18 +++++++++++-------
tests/test_ci_scope.py | 6 ++++++
utils/ci_scope.py | 5 ++++-
4 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/benchmarks/codex-mcp/scripts/setup.mjs b/benchmarks/codex-mcp/scripts/setup.mjs
index 416d5fd8..43eab894 100644
--- a/benchmarks/codex-mcp/scripts/setup.mjs
+++ b/benchmarks/codex-mcp/scripts/setup.mjs
@@ -108,6 +108,11 @@ async function main() {
'uv',
['sync', '--frozen', '--extra', 'local-worker', '--extra', 'mcp', '--extra', 'benchmarks'],
);
+ run(
+ 'uv',
+ ['run', '--no-sync', 'vidxp', 'init', '--yes'],
+ { env: commandEnvironment },
+ );
run('npm', ['ci'], { cwd: benchmarkRoot });
const glibcVersion = process.report?.getReport().header.glibcVersionRuntime;
diff --git a/docs/benchmarking/agent_ablation.md b/docs/benchmarking/agent_ablation.md
index d528de23..86287040 100644
--- a/docs/benchmarking/agent_ablation.md
+++ b/docs/benchmarking/agent_ablation.md
@@ -101,7 +101,10 @@ therefore does not replace LongVALE in this ablation.
Promptfoo 0.122.2 requires Node.js 22.22.0 or newer. The benchmark-local
`.npmrc` enforces that requirement so an unsupported runtime fails during
installation instead of failing after Codex runs have begun. You also need
-`uv` and the Codex CLI on `PATH`.
+`uv` and the Codex CLI on `PATH`. The setup verifies FFmpeg and ffprobe and,
+when they are absent, installs them through a supported package manager. On a
+fresh macOS machine, install Homebrew before running setup so VidXP can install
+FFmpeg automatically.
From the repository root, run the automated setup:
@@ -110,12 +113,13 @@ npm --prefix benchmarks/codex-mcp run setup
```
The command installs the pinned Python and Node dependencies, creates isolated
-state outside the checkout, opens Codex login when authentication is absent,
-downloads and verifies the pinned LongVALE archive, copies the five pilot
-videos, prepares the four required capabilities, indexes the media, saves the
-evaluation environment in the ignored `benchmarks/codex-mcp/.env` file, and
-runs preflight. Accept the LongVALE dataset terms before running it. Do not copy
-or commit the generated `auth.json`.
+state outside the checkout, initializes the system media runtime, opens Codex
+login when authentication is absent, downloads and verifies the pinned
+LongVALE archive, copies the five pilot videos, prepares the four required
+capabilities, indexes the media, saves the evaluation environment in the
+ignored `benchmarks/codex-mcp/.env` file, and runs preflight. Accept the
+LongVALE dataset terms before running it. Do not copy or commit the generated
+`auth.json`.
By default, mutable state goes under the operating system's user data
directory. Set only `VIDXP_EVAL_ROOT` when it needs to live elsewhere:
diff --git a/tests/test_ci_scope.py b/tests/test_ci_scope.py
index edae7c95..7b80a1c7 100644
--- a/tests/test_ci_scope.py
+++ b/tests/test_ci_scope.py
@@ -20,6 +20,12 @@ def test_tests_and_desktop_changes_skip_container_builds(self):
Scope(run_suite=True, run_container=False, run_desktop=True),
)
+ def test_benchmark_changes_skip_product_artifact_builds(self):
+ self.assertEqual(
+ classify(["benchmarks/codex-mcp/scripts/setup.mjs"]),
+ Scope(run_suite=True, run_container=False, run_desktop=False),
+ )
+
def test_product_and_workflow_changes_validate_containers(self):
for path in (
"src/vidxp/new_feature.py",
diff --git a/utils/ci_scope.py b/utils/ci_scope.py
index ba590cb2..f774404d 100644
--- a/utils/ci_scope.py
+++ b/utils/ci_scope.py
@@ -27,7 +27,9 @@ def _is_documentation(path: str) -> bool:
def _is_container_neutral(path: str) -> bool:
- return path.startswith((".agents/", "desktop/", "plugins/", "tests/"))
+ return path.startswith(
+ (".agents/", "benchmarks/", "desktop/", "plugins/", "tests/")
+ )
def _affects_desktop(path: str) -> bool:
@@ -47,6 +49,7 @@ def _is_unknown_product_path(path: str) -> bool:
(
".agents/",
".github/",
+ "benchmarks/",
"desktop/",
"plugins/",
"src/",