mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-11 03:13:51 +00:00
1 parent
f0693a10c1
commit
c4dc91a7da
7 files changed
+91
-68
No files matched your search
+2
-2
@@ -19,7 +19,7 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
- Add `auth_secret` to `serena_config.yml` for authenticating communication between Serena components
|
||||
and services. When missing, null, or empty, a random UUID is generated and persisted; existing values
|
||||
are preserved
|
||||
- Fix: MCP `initialize` now reports Serena's version instead of the installed mcp SDK version (#1889)
|
||||
- Fix: MCP server now reports Serena's version instead of the installed MCP SDK version (#1889)
|
||||
- Fix: importing Serena no longer loads the `anthropic` package unless the Anthropic token counter is
|
||||
actually used; the unconditional import added seconds to CLI/MCP startup on some machines (#2012)
|
||||
- Fix: Parallel agents auto-registering projects could overwrite each other's changes to the global
|
||||
@@ -154,6 +154,7 @@ CLI:
|
||||
|
||||
* Dependencies:
|
||||
- Remove the redundant `dotenv` dependency; the `dotenv` module is provided by `python-dotenv`
|
||||
- Upgrade the `mcp` SDK from 1.28.1 to 2.2.0
|
||||
|
||||
# v1.7.0 (2026-08-09)
|
||||
|
||||
@@ -306,7 +307,6 @@ CLI:
|
||||
`target_file`/`targetFile` file-path keys (shared payload parsing, applies to all hook clients).
|
||||
- Fix hook input parsing for clients that emit raw control characters in JSON string values #1743.
|
||||
|
||||
|
||||
# v1.6.1 (2026-07-21)
|
||||
|
||||
* General:
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ dependencies = [
|
||||
"requests==2.33.0",
|
||||
"overrides==7.7.0",
|
||||
"python-dotenv==1.2.2",
|
||||
"mcp==1.28.1",
|
||||
"mcp==2.2.0",
|
||||
"flask==3.1.3", # bumped from 3.1.1 for CVE fix (also fixes werkzeug alert)
|
||||
"sensai-utils==1.5.0",
|
||||
"pydantic==2.12.5",
|
||||
|
||||
+5
-3
@@ -390,8 +390,6 @@ class TopLevelCommands(AutoRegisteringGroup):
|
||||
|
||||
factory = SerenaMCPFactory(transport=transport, context=context, project=project_file, memory_log_handler=memory_log_handler)
|
||||
server = factory.create_mcp_server(
|
||||
host=host,
|
||||
port=port,
|
||||
mode_selection_def=mode_selection_def,
|
||||
language_backend=LanguageBackendRegistry.get_instance().resolve(language_backend) if language_backend else None,
|
||||
agent_interface=AgentInterface.from_str(agent_interface) if agent_interface else None,
|
||||
@@ -409,7 +407,11 @@ class TopLevelCommands(AutoRegisteringGroup):
|
||||
project_file,
|
||||
)
|
||||
log.info("Starting MCP server …")
|
||||
server.run(transport=transport)
|
||||
kwargs = {}
|
||||
if transport != "stdio":
|
||||
kwargs["host"] = host
|
||||
kwargs["port"] = port
|
||||
server.run(transport=transport, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
@click.command(
|
||||
|
||||
+11
-24
@@ -11,17 +11,16 @@ from dataclasses import dataclass
|
||||
from typing import Any, Literal, cast
|
||||
|
||||
import docstring_parser
|
||||
from mcp.server.fastmcp import server
|
||||
from mcp.server.fastmcp.exceptions import ToolError
|
||||
from mcp.server.fastmcp.server import Context, FastMCP, Settings
|
||||
from mcp.server.fastmcp.tools.base import Tool as FastMCPTool
|
||||
from mcp.server.session import ServerSessionT
|
||||
from mcp.shared.context import LifespanContextT, RequestT
|
||||
from mcp.server.mcpserver import server
|
||||
from mcp.server.mcpserver.context import LifespanContextT, RequestT
|
||||
from mcp.server.mcpserver.exceptions import ToolError
|
||||
from mcp.server.mcpserver.server import Context
|
||||
from mcp.server.mcpserver.server import MCPServer as FastMCP
|
||||
from mcp.server.mcpserver.tools.base import Tool as FastMCPTool
|
||||
from mcp.types import ToolAnnotations
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
from sensai.util import logging
|
||||
|
||||
from serena import __version__
|
||||
from serena import __version__ as serena_version_str
|
||||
from serena.agent import (
|
||||
SerenaAgent,
|
||||
)
|
||||
@@ -110,8 +109,8 @@ class SerenaFastMCPTool(FastMCPTool):
|
||||
can_edit = tool.can_edit()
|
||||
annotations = ToolAnnotations(
|
||||
title=tool_title,
|
||||
readOnlyHint=not can_edit,
|
||||
destructiveHint=can_edit,
|
||||
read_only_hint=not can_edit,
|
||||
destructive_hint=can_edit,
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
@@ -133,7 +132,7 @@ class SerenaFastMCPTool(FastMCPTool):
|
||||
async def run(
|
||||
self,
|
||||
arguments: dict[str, Any],
|
||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
||||
context: Context[LifespanContextT, RequestT],
|
||||
convert_result: bool = False,
|
||||
) -> Any:
|
||||
# apply parameter aliases
|
||||
@@ -323,8 +322,6 @@ class SerenaMCPFactory:
|
||||
|
||||
def create_mcp_server(
|
||||
self,
|
||||
host: str = "127.0.0.1",
|
||||
port: int = 8000,
|
||||
mode_selection_def: ModeSelectionDefinition | None = None,
|
||||
language_backend: LanguageBackend | None = None,
|
||||
agent_interface: AgentInterface | None = None,
|
||||
@@ -339,8 +336,6 @@ class SerenaMCPFactory:
|
||||
"""
|
||||
Create an MCP server with process-isolated SerenaAgent to prevent asyncio contamination.
|
||||
|
||||
:param host: The host to bind to
|
||||
:param port: The port to bind to
|
||||
:param mode_selection_def: the mode selection definition to apply
|
||||
:param language_backend: the language backend to use, overriding the configuration setting.
|
||||
:param agent_interface: the agent interface to use, overriding the configuration setting.
|
||||
@@ -383,23 +378,15 @@ class SerenaMCPFactory:
|
||||
show_fatal_exception_safe(e)
|
||||
raise
|
||||
|
||||
# Override model_config to disable the use of `.env` files for reading settings, because user projects are likely to contain
|
||||
# `.env` files (e.g. containing LOG_LEVEL) that are not supposed to override the MCP settings;
|
||||
# retain only FASTMCP_ prefix for already set environment variables.
|
||||
Settings.model_config = SettingsConfigDict(env_prefix="FASTMCP_")
|
||||
instructions = self._get_initial_instructions()
|
||||
log.info("MCP server initial instructions:\n%s", instructions)
|
||||
mcp = FastMCP(
|
||||
name="Serena",
|
||||
version=serena_version_str,
|
||||
lifespan=self.server_lifespan,
|
||||
website_url="https://oraios.github.io/serena",
|
||||
host=host,
|
||||
port=port,
|
||||
instructions=instructions,
|
||||
)
|
||||
# FastMCP currently falls back to the installed mcp SDK version when no version is set.
|
||||
# Set the low-level server value explicitly so MCP clients identify Serena correctly.
|
||||
mcp._mcp_server.version = __version__
|
||||
return mcp
|
||||
|
||||
@asynccontextmanager
|
||||
|
||||
@@ -8,8 +8,8 @@ from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
|
||||
|
||||
from mcp import Implementation
|
||||
from mcp.server.fastmcp import Context
|
||||
from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
|
||||
from mcp.server.mcpserver import Context
|
||||
from mcp.server.mcpserver.utilities.func_metadata import FuncMetadata, func_metadata
|
||||
from sensai.util import logging
|
||||
from sensai.util.helper import mark_used
|
||||
from sensai.util.string import dict_string
|
||||
|
||||
+2
-22
@@ -1,13 +1,11 @@
|
||||
"""Tests for the mcp.py module in serena."""
|
||||
|
||||
import pytest
|
||||
from mcp.server.fastmcp import Context
|
||||
from mcp.server.fastmcp.tools.base import Tool as MCPTool
|
||||
from mcp.server.mcpserver import Context
|
||||
from mcp.server.mcpserver.tools.base import Tool as MCPTool
|
||||
|
||||
from serena import __version__
|
||||
from serena.agent import Tool, ToolRegistry
|
||||
from serena.config.context_mode import SerenaAgentContext
|
||||
from serena.config.serena_config import SerenaConfig
|
||||
from serena.mcp import SerenaMCPFactory
|
||||
from serena.repl.facade import ApiScope
|
||||
from serena.repl.repl import SerenaRepl
|
||||
@@ -64,24 +62,6 @@ class BasicTool(BaseMockTool):
|
||||
return self.apply(**kwargs)
|
||||
|
||||
|
||||
def test_create_mcp_server_reports_serena_version(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""MCP initialize must report Serena's version, not the installed mcp SDK version."""
|
||||
|
||||
class MinimalAgent:
|
||||
def create_connection_prompt(self) -> str:
|
||||
return ""
|
||||
|
||||
monkeypatch.setattr(SerenaConfig, "from_config_file", classmethod(lambda cls: SerenaConfig()))
|
||||
factory = SerenaMCPFactory(transport="stdio")
|
||||
monkeypatch.setattr(factory, "_create_serena_agent", lambda *args, **kwargs: MinimalAgent())
|
||||
|
||||
mcp_server = factory.create_mcp_server()
|
||||
initialization_options = mcp_server._mcp_server.create_initialization_options()
|
||||
|
||||
assert initialization_options.server_name == "Serena"
|
||||
assert initialization_options.server_version == __version__
|
||||
|
||||
|
||||
def test_make_tool_basic() -> None:
|
||||
"""Test that make_tool correctly creates an MCP tool from a Tool object."""
|
||||
mock_tool = BasicTool()
|
||||
|
||||
@@ -821,6 +821,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore2"
|
||||
version = "2.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "h11" },
|
||||
{ name = "truststore" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089, upload-time = "2026-07-24T09:21:03.867Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/fb/46c52b781975c335a2bcf1072c7bbc007cbdc8d674217f5ee1daba2c848b/httpcore2-2.9.1-py3-none-any.whl", hash = "sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26", size = 82809, upload-time = "2026-07-24T09:21:01.178Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
@@ -842,12 +855,19 @@ http2 = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx-sse"
|
||||
version = "0.4.3"
|
||||
name = "httpx2"
|
||||
version = "2.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "httpcore2" },
|
||||
{ name = "idna" },
|
||||
{ name = "truststore" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458, upload-time = "2026-07-24T09:21:04.972Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/b8/cfd91c4ab9134d386d48f0b6ac662ff3d4be6efdee59ee1c67ebc3c0487c/httpx2-2.9.1-py3-none-any.whl", hash = "sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a", size = 91191, upload-time = "2026-07-24T09:21:02.6Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -861,11 +881,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.15"
|
||||
version = "3.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1353,15 +1373,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mcp"
|
||||
version = "1.28.1"
|
||||
version = "2.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx-sse" },
|
||||
{ name = "httpx2" },
|
||||
{ name = "jsonschema" },
|
||||
{ name = "mcp-types" },
|
||||
{ name = "opentelemetry-api" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "pyjwt", extra = ["crypto"] },
|
||||
{ name = "python-multipart" },
|
||||
{ name = "pywin32", marker = "sys_platform == 'win32'" },
|
||||
@@ -1371,9 +1391,22 @@ dependencies = [
|
||||
{ name = "typing-inspection" },
|
||||
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/31/ac54fb0fdd5b37de704486e288bba4fbbb463f24cfcfedbede407b854513/mcp-2.2.0.tar.gz", hash = "sha256:2dc37ecb1974becdcebdbf7561e7c15a07dbbf20ba21ba16c3593b3038b3afbd", size = 4084129, upload-time = "2026-09-07T16:06:23.439Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/ff/8e7eade68b8a28f7da0ed1085544341b51f9c935dbf6b95c76b7edfea6a0/mcp-2.2.0-py3-none-any.whl", hash = "sha256:bde982589473a060ae145e3406e9a5333fe538c97229ba841f5a7f92be004f81", size = 365656, upload-time = "2026-09-07T16:06:19.711Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcp-types"
|
||||
version = "2.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ae/91/762d7755d971aff8a28d75f7961656148edf27875c8026e6385aaab08ae7/mcp_types-2.2.0.tar.gz", hash = "sha256:d3ed53703ddd10d9c6399f29d322bb66f3f67ab41348ac8556ba23e07fedefad", size = 65892, upload-time = "2026-09-07T16:06:25.187Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/d7/6ffba5d8cd5dd9b8a19478875c50e04945314ba5074e84d749283f27f62d/mcp_types-2.2.0-py3-none-any.whl", hash = "sha256:ea476b73ee86709ab5abc9452385ed36cc05907e582355622e294595c9a04f13", size = 69106, upload-time = "2026-09-07T16:06:21.461Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1565,6 +1598,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
version = "1.44.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oslex"
|
||||
version = "2.0.0"
|
||||
@@ -2857,7 +2902,7 @@ requires-dist = [
|
||||
{ name = "joblib", specifier = "==1.5.1" },
|
||||
{ name = "jupyter-book", marker = "extra == 'dev'", specifier = "==1.0.4.post1" },
|
||||
{ name = "lsprotocol", specifier = "==2025.0.0" },
|
||||
{ name = "mcp", specifier = "==1.28.1" },
|
||||
{ name = "mcp", specifier = "==2.2.0" },
|
||||
{ name = "oslex", specifier = "==2.0.0" },
|
||||
{ name = "overrides", specifier = "==7.7.0" },
|
||||
{ name = "pathspec", specifier = "==0.12.1" },
|
||||
@@ -3520,6 +3565,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truststore"
|
||||
version = "0.10.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ty"
|
||||
version = "0.0.24"
|
||||
|
||||
Reference in new issue
Block a user