fix: reduce end-user installation footprint
This commit is contained in:
+6
-4
@@ -49,13 +49,13 @@ git -C <install-dir> describe --tags --exact-match HEAD
|
|||||||
|
|
||||||
### 2. 环境检查与确定性准备
|
### 2. 环境检查与确定性准备
|
||||||
|
|
||||||
需要:Git、`uv`、Python 3.12+、NocoBase `nb` CLI 2.1+。先运行只读检查:
|
需要:Git、`uv`、Python 3.12+、NocoBase `nb` CLI 2.1+。Git 只用于取得和校验发布版本;`uv` 用于创建锁定的项目环境以及后续诊断/更新。若机器尚未安装 `nb`,安装官方 CLI 还会条件性需要 Node.js 与 npm。先运行只读检查:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> inspect --json
|
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> inspect --json
|
||||||
```
|
```
|
||||||
|
|
||||||
若 `uv` 或 `nb` 不存在,先取得用户同意再安装。NocoBase CLI 当前官方包入口是 `npm install -g @nocobase/cli`;安装后必须重新运行检查,不要通过修改 PATH 输出伪造成功。
|
若 `uv`、`nb` 或安装 `nb` 所需的 Node.js/npm 不存在,先向用户说明来源、安装位置和影响范围并取得同意。NocoBase CLI 当前官方包入口是 `npm install -g @nocobase/cli`;安装后必须重新运行检查,不要通过修改 PATH 输出伪造成功。
|
||||||
|
|
||||||
然后运行幂等准备步骤:
|
然后运行幂等准备步骤:
|
||||||
|
|
||||||
@@ -63,7 +63,9 @@ uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py
|
|||||||
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> prepare --json
|
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> prepare --json
|
||||||
```
|
```
|
||||||
|
|
||||||
该脚本只会校验项目文件、在缺失时从示例创建 `config.json`,并执行锁定依赖同步。已有 `config.json` 会被保留,依赖必须来自 `uv.lock`。
|
该脚本只会校验项目文件、在缺失时从示例创建 `config.json`,并把 `uv.lock` 中的**运行时依赖**同步到项目自己的 `.venv`。它使用 `--no-dev`,不会给终端用户安装 `mypy`、`ruff`、`pip-audit` 或其传递依赖。已有 `config.json` 会被保留。
|
||||||
|
|
||||||
|
安装会在用户电脑留下以下内容:固定 Release 的仓库副本及浅层 `.git`、项目 `.venv`、不含 Key 的 `config.json`、由 `nb` CLI 管理的个人登录环境,以及合并后的 MCP 客户端配置和原配置备份。`uv` 可能保留下载缓存,缓存不是 MCP 运行所必需;安装器不会自动删除用户的共享缓存。默认 stdio 流程不会创建 systemd 服务、监听端口或 `.runtime` Token。
|
||||||
|
|
||||||
### 3. 连接用户自己的 NocoBase 账号
|
### 3. 连接用户自己的 NocoBase 账号
|
||||||
|
|
||||||
@@ -104,7 +106,7 @@ Key 过期或撤销后,Agent 重新启动 `connect-key`,用户在新的隐
|
|||||||
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> client-config
|
uv run --no-project --python 3.12 python <install-dir>/scripts/agent_install.py --project-root <install-dir> client-config
|
||||||
```
|
```
|
||||||
|
|
||||||
输出采用常见的 `mcpServers` JSON 形状,包含绝对的项目、`uv` 和 `nb` 路径。Agent 应查明自己客户端当前版本支持的 MCP 配置格式,将其中的 `command`、`args`、`env` 等价映射过去。
|
输出采用常见的 `mcpServers` JSON 形状,包含项目 `.venv` 中 Python、项目配置和 `nb` 的绝对路径。MCP 客户端直接启动已准备好的虚拟环境,不在每次调用时经过 `uv` 或同步依赖。Agent 应查明自己客户端当前版本支持的 MCP 配置格式,将其中的 `command`、`args`、`env` 等价映射过去。
|
||||||
|
|
||||||
写入前:
|
写入前:
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ Each user runs this MCP on their own computer and signs in to the remote NocoBas
|
|||||||
|
|
||||||
For an agent-guided installation that works across MCP clients, give the installing Agent the immutable Release version of [`INSTALL.md`](INSTALL.md). The guide combines adaptive prompts with an idempotent helper script; it defaults to local stdio and read-only access. Do not distribute a default-branch installation link as a release.
|
For an agent-guided installation that works across MCP clients, give the installing Agent the immutable Release version of [`INSTALL.md`](INSTALL.md). The guide combines adaptive prompts with an idempotent helper script; it defaults to local stdio and read-only access. Do not distribute a default-branch installation link as a release.
|
||||||
|
|
||||||
|
The end-user installer creates a project-local `.venv` from the locked runtime dependency set only; it does not install the development tools from the `dev` group. Generated MCP client configuration launches that environment's Python directly, so normal MCP startup does not resync packages or require `uv` to remain in the client command.
|
||||||
|
|
||||||
The default stdio server is read-only. Add `--enable-writes` for fixed-schema project create/update tools. Optional local HTTP mode also provides authenticated attachment uploads. Neither mode exposes arbitrary collections, fields, commands, or delete operations.
|
The default stdio server is read-only. Add `--enable-writes` for fixed-schema project create/update tools. Optional local HTTP mode also provides authenticated attachment uploads. Neither mode exposes arbitrary collections, fields, commands, or delete operations.
|
||||||
|
|
||||||
## Configure
|
## Configure
|
||||||
|
|||||||
+16
-12
@@ -13,8 +13,10 @@ MCP 不保存公共业务账号,也不需要部署一个多人共用的远程
|
|||||||
## 环境要求
|
## 环境要求
|
||||||
|
|
||||||
- Python 3.12 或更高版本
|
- Python 3.12 或更高版本
|
||||||
|
- Git(仅安装、版本校验和更新)
|
||||||
- `uv`
|
- `uv`
|
||||||
- NocoBase `nb` CLI 2.1 或更高版本
|
- NocoBase `nb` CLI 2.1 或更高版本
|
||||||
|
- Node.js/npm(仅在尚未安装 `nb` 时用于安装官方 CLI)
|
||||||
- 可通过浏览器访问目标 NocoBase
|
- 可通过浏览器访问目标 NocoBase
|
||||||
- 管理员已为用户配置所需的 NocoBase 角色与数据权限
|
- 管理员已为用户配置所需的 NocoBase 角色与数据权限
|
||||||
|
|
||||||
@@ -31,7 +33,7 @@ MCP 不保存公共业务账号,也不需要部署一个多人共用的远程
|
|||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Donaldttt/nocobase-mcp.git
|
git clone https://github.com/Donaldttt/nocobase-mcp.git
|
||||||
cd nocobase-mcp
|
cd nocobase-mcp
|
||||||
uv sync --locked
|
uv sync --locked --no-dev
|
||||||
cp config.example.json config.json
|
cp config.example.json config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ Agent 先引导用户在 NocoBase 个人设置中创建可撤销、有合理有
|
|||||||
先进行健康检查:
|
先进行健康检查:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run nocobase-crm-mcp --config config.json
|
.venv/bin/python -m nocobase_crm_mcp.server --config config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
然后让 MCP 客户端调用 `crm_health`。如果返回 `nb_auth_failed`,由 Agent 重新启动 `connect-key`,用户在新的本机隐藏提示中输入新 Key;不要让用户手动尝试 `nb` 命令。
|
然后让 MCP 客户端调用 `crm_health`。如果返回 `nb_auth_failed`,由 Agent 重新启动 `connect-key`,用户在新的本机隐藏提示中输入新 Key;不要让用户手动尝试 `nb` 命令。
|
||||||
@@ -60,21 +62,23 @@ uv run nocobase-crm-mcp --config config.json
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"nocobase-crm": {
|
"nocobase-crm": {
|
||||||
"command": "uv",
|
"command": "/absolute/path/to/nocobase-mcp/.venv/bin/python",
|
||||||
"args": [
|
"args": [
|
||||||
"--directory",
|
"-m",
|
||||||
"/absolute/path/to/nocobase-mcp",
|
"nocobase_crm_mcp.server",
|
||||||
"run",
|
|
||||||
"--no-sync",
|
|
||||||
"nocobase-crm-mcp",
|
|
||||||
"--config",
|
"--config",
|
||||||
"/absolute/path/to/nocobase-mcp/config.json"
|
"/absolute/path/to/nocobase-mcp/config.json"
|
||||||
]
|
],
|
||||||
|
"env": {
|
||||||
|
"NOCOBASE_MCP_NB_BIN": "/absolute/path/to/nb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
不要手写猜测虚拟环境路径;优先使用 `scripts/agent_install.py client-config` 的输出。上例是 POSIX 路径,Windows 输出会自动使用 `.venv\\Scripts\\python.exe`。
|
||||||
|
|
||||||
默认只读。如需项目创建和更新,在参数末尾加入 `--enable-writes`。这些写工具只允许预先定义的项目字段,不提供删除或任意资源访问能力。
|
默认只读。如需项目创建和更新,在参数末尾加入 `--enable-writes`。这些写工具只允许预先定义的项目字段,不提供删除或任意资源访问能力。
|
||||||
|
|
||||||
附件上传需要可选的本机 HTTP 模式。HTTP 服务默认只监听 `127.0.0.1:10087`;上传路由本身也默认关闭,不得暴露到局域网或公网:
|
附件上传需要可选的本机 HTTP 模式。HTTP 服务默认只监听 `127.0.0.1:10087`;上传路由本身也默认关闭,不得暴露到局域网或公网:
|
||||||
@@ -120,8 +124,8 @@ Linux 用户也可以执行 `bash scripts/mcp_service.sh start` 安装用户级
|
|||||||
```bash
|
```bash
|
||||||
git rev-parse HEAD
|
git rev-parse HEAD
|
||||||
git pull --ff-only
|
git pull --ff-only
|
||||||
uv sync --locked
|
uv sync --locked --no-dev
|
||||||
uv run python -m unittest discover -s tests
|
.venv/bin/python -m unittest discover -s tests
|
||||||
```
|
```
|
||||||
|
|
||||||
使用 systemd 本机 HTTP 服务时,再执行:
|
使用 systemd 本机 HTTP 服务时,再执行:
|
||||||
@@ -136,7 +140,7 @@ journalctl --user -u nocobase-crm-mcp.service -n 200 --no-pager
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git switch --detach <previous-release-tag-or-commit>
|
git switch --detach <previous-release-tag-or-commit>
|
||||||
uv sync --locked
|
uv sync --locked --no-dev
|
||||||
bash scripts/mcp_service.sh start
|
bash scripts/mcp_service.sh start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,25 @@
|
|||||||
"defaultTransport": "stdio",
|
"defaultTransport": "stdio",
|
||||||
"python": ">=3.12",
|
"python": ">=3.12",
|
||||||
"packageManager": "uv",
|
"packageManager": "uv",
|
||||||
|
"environment": ".venv",
|
||||||
|
"launcher": "project-venv-python",
|
||||||
|
"dependencyProfile": "runtime-only-no-dev",
|
||||||
"nocobaseCli": ">=2.1"
|
"nocobaseCli": ">=2.1"
|
||||||
},
|
},
|
||||||
|
"installation": {
|
||||||
|
"requiredTools": ["git", "uv", "python", "nb"],
|
||||||
|
"conditionalTools": {
|
||||||
|
"nodeAndNpm": "Required only when the NocoBase nb CLI is not already installed."
|
||||||
|
},
|
||||||
|
"persistentArtifacts": [
|
||||||
|
"release checkout",
|
||||||
|
"project .venv",
|
||||||
|
"config.json",
|
||||||
|
"nb CLI user environment",
|
||||||
|
"MCP client configuration and backup"
|
||||||
|
],
|
||||||
|
"defaultBackgroundService": false
|
||||||
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"type": "personal-api-key",
|
"type": "personal-api-key",
|
||||||
"cliAuthType": "token",
|
"cliAuthType": "token",
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ def create_server(
|
|||||||
allowed_origins: list[str] | None = None,
|
allowed_origins: list[str] | None = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if FastMCP is None:
|
if FastMCP is None:
|
||||||
raise RuntimeError("Missing MCP SDK. Install dependencies with: uv sync")
|
raise RuntimeError("Missing MCP SDK. Install runtime dependencies with: uv sync --locked --no-dev")
|
||||||
service = crm_service or _make_service(config_path)
|
service = crm_service or _make_service(config_path)
|
||||||
if not readonly and project_service is None:
|
if not readonly and project_service is None:
|
||||||
raise ValueError("project_service is required when readonly is false")
|
raise ValueError("project_service is required when readonly is false")
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ requires-python = ">=3.12"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cryptography>=50,<51",
|
"cryptography>=50,<51",
|
||||||
"httpx>=0.27,<1",
|
"httpx>=0.27,<1",
|
||||||
"mcp[cli]>=1.27,<2",
|
"mcp>=1.27,<2",
|
||||||
"python-multipart>=0.0.9,<1",
|
"python-multipart>=0.0.9,<1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .common import (
|
|||||||
shutil,
|
shutil,
|
||||||
subprocess,
|
subprocess,
|
||||||
)
|
)
|
||||||
from .project import inspect_project
|
from .project import _venv_python_path, inspect_project
|
||||||
|
|
||||||
|
|
||||||
def doctor_project(
|
def doctor_project(
|
||||||
@@ -47,10 +47,7 @@ def doctor_project(
|
|||||||
commands = (
|
commands = (
|
||||||
[uv_bin, "lock", "--check"],
|
[uv_bin, "lock", "--check"],
|
||||||
[
|
[
|
||||||
uv_bin,
|
str(_venv_python_path(root)),
|
||||||
"run",
|
|
||||||
"--no-sync",
|
|
||||||
"python",
|
|
||||||
"-c",
|
"-c",
|
||||||
"import mcp; import nocobase_crm_mcp",
|
"import mcp; import nocobase_crm_mcp",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -101,9 +101,19 @@ def prepare_project(
|
|||||||
shutil.copy2(example_path, config_path)
|
shutil.copy2(example_path, config_path)
|
||||||
created_config = True
|
created_config = True
|
||||||
|
|
||||||
command = [uv_bin, "sync", "--locked", "--python", f"{MIN_PYTHON[0]}.{MIN_PYTHON[1]}"]
|
venv_path = root / ".venv"
|
||||||
|
command = [uv_bin, "sync", "--locked", "--no-dev", "--python", f"{MIN_PYTHON[0]}.{MIN_PYTHON[1]}"]
|
||||||
|
command_environment = os.environ.copy()
|
||||||
|
command_environment["UV_PROJECT_ENVIRONMENT"] = str(venv_path)
|
||||||
try:
|
try:
|
||||||
result = runner(command, cwd=str(root), capture_output=True, text=True, check=False)
|
result = runner(
|
||||||
|
command,
|
||||||
|
cwd=str(root),
|
||||||
|
env=command_environment,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
raise InstallerError(f"Could not execute uv sync: {error}") from error
|
raise InstallerError(f"Could not execute uv sync: {error}") from error
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
@@ -116,7 +126,8 @@ def prepare_project(
|
|||||||
"projectRoot": str(root),
|
"projectRoot": str(root),
|
||||||
"configPath": str(config_path),
|
"configPath": str(config_path),
|
||||||
"configCreated": created_config,
|
"configCreated": created_config,
|
||||||
"dependencies": "locked",
|
"dependencies": "locked-runtime-only",
|
||||||
|
"environmentPath": str(venv_path),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -169,20 +180,16 @@ def render_client_config(
|
|||||||
*,
|
*,
|
||||||
enable_writes: bool = False,
|
enable_writes: bool = False,
|
||||||
which: Which = shutil.which,
|
which: Which = shutil.which,
|
||||||
|
os_name: str | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
root = _project_root(project_root)
|
root = _project_root(project_root)
|
||||||
uv_bin = which("uv") or "uv"
|
|
||||||
nb_bin = which("nb") or "nb"
|
nb_bin = which("nb") or "nb"
|
||||||
if os.path.isabs(uv_bin):
|
|
||||||
uv_bin = os.path.abspath(uv_bin)
|
|
||||||
if os.path.isabs(nb_bin):
|
if os.path.isabs(nb_bin):
|
||||||
nb_bin = os.path.abspath(nb_bin)
|
nb_bin = os.path.abspath(nb_bin)
|
||||||
|
python_bin = _venv_python_path(root, os_name=os_name)
|
||||||
args = [
|
args = [
|
||||||
"--directory",
|
"-m",
|
||||||
str(root),
|
"nocobase_crm_mcp.server",
|
||||||
"run",
|
|
||||||
"--no-sync",
|
|
||||||
"nocobase-crm-mcp",
|
|
||||||
"--config",
|
"--config",
|
||||||
str(root / "config.json"),
|
str(root / "config.json"),
|
||||||
]
|
]
|
||||||
@@ -191,9 +198,15 @@ def render_client_config(
|
|||||||
return {
|
return {
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
SERVER_NAME: {
|
SERVER_NAME: {
|
||||||
"command": uv_bin,
|
"command": str(python_bin),
|
||||||
"args": args,
|
"args": args,
|
||||||
"env": {"NOCOBASE_MCP_NB_BIN": nb_bin},
|
"env": {"NOCOBASE_MCP_NB_BIN": nb_bin},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _venv_python_path(root: Path, *, os_name: str | None = None) -> Path:
|
||||||
|
if (os_name or os.name) == "nt":
|
||||||
|
return root / ".venv" / "Scripts" / "python.exe"
|
||||||
|
return root / ".venv" / "bin" / "python"
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ class AgentInstallTests(unittest.TestCase):
|
|||||||
self.assertTrue(first["configCreated"])
|
self.assertTrue(first["configCreated"])
|
||||||
self.assertFalse(second["configCreated"])
|
self.assertFalse(second["configCreated"])
|
||||||
self.assertEqual(preserved["env"], "custom-env")
|
self.assertEqual(preserved["env"], "custom-env")
|
||||||
self.assertEqual(calls[0][0], ["/opt/bin/uv", "sync", "--locked", "--python", "3.12"])
|
self.assertEqual(calls[0][0], ["/opt/bin/uv", "sync", "--locked", "--no-dev", "--python", "3.12"])
|
||||||
|
self.assertEqual(calls[0][1]["env"]["UV_PROJECT_ENVIRONMENT"], str(root / ".venv"))
|
||||||
|
self.assertEqual(first["dependencies"], "locked-runtime-only")
|
||||||
|
self.assertEqual(first["environmentPath"], str(root / ".venv"))
|
||||||
self.assertEqual(len(calls), 2)
|
self.assertEqual(len(calls), 2)
|
||||||
|
|
||||||
def test_set_env_backs_up_config_before_changing_it(self) -> None:
|
def test_set_env_backs_up_config_before_changing_it(self) -> None:
|
||||||
@@ -227,12 +230,21 @@ class AgentInstallTests(unittest.TestCase):
|
|||||||
writable = agent_install.render_client_config(root, enable_writes=True, which=fake_which)
|
writable = agent_install.render_client_config(root, enable_writes=True, which=fake_which)
|
||||||
|
|
||||||
server = config["mcpServers"]["nocobase-crm"]
|
server = config["mcpServers"]["nocobase-crm"]
|
||||||
self.assertEqual(server["command"], "/opt/bin/uv")
|
self.assertEqual(server["command"], str(root.resolve() / ".venv" / "bin" / "python"))
|
||||||
self.assertEqual(server["env"]["NOCOBASE_MCP_NB_BIN"], "/opt/bin/nb")
|
self.assertEqual(server["env"]["NOCOBASE_MCP_NB_BIN"], "/opt/bin/nb")
|
||||||
self.assertIn(str(root.resolve()), server["args"])
|
self.assertIn(str(root.resolve() / "config.json"), server["args"])
|
||||||
|
self.assertEqual(server["args"][:2], ["-m", "nocobase_crm_mcp.server"])
|
||||||
self.assertNotIn("--enable-writes", server["args"])
|
self.assertNotIn("--enable-writes", server["args"])
|
||||||
self.assertIn("--enable-writes", writable["mcpServers"]["nocobase-crm"]["args"])
|
self.assertIn("--enable-writes", writable["mcpServers"]["nocobase-crm"]["args"])
|
||||||
|
|
||||||
|
def test_client_config_uses_windows_project_environment_python(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
config = agent_install.render_client_config(root, which=fake_which, os_name="nt")
|
||||||
|
|
||||||
|
server = config["mcpServers"]["nocobase-crm"]
|
||||||
|
self.assertEqual(server["command"], str(root.resolve() / ".venv" / "Scripts" / "python.exe"))
|
||||||
|
|
||||||
def test_doctor_checks_local_install_without_reading_nb_secrets(self) -> None:
|
def test_doctor_checks_local_install_without_reading_nb_secrets(self) -> None:
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
@@ -254,6 +266,14 @@ class AgentInstallTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(result["ok"])
|
self.assertTrue(result["ok"])
|
||||||
self.assertEqual(commands[0], ["/opt/bin/uv", "lock", "--check"])
|
self.assertEqual(commands[0], ["/opt/bin/uv", "lock", "--check"])
|
||||||
|
self.assertEqual(
|
||||||
|
commands[1],
|
||||||
|
[
|
||||||
|
str(root.resolve() / ".venv" / "bin" / "python"),
|
||||||
|
"-c",
|
||||||
|
"import mcp; import nocobase_crm_mcp",
|
||||||
|
],
|
||||||
|
)
|
||||||
self.assertNotIn("--show-secrets", json.dumps(result))
|
self.assertNotIn("--show-secrets", json.dumps(result))
|
||||||
self.assertTrue(any("crm_health" in step for step in result["remainingVerification"]))
|
self.assertTrue(any("crm_health" in step for step in result["remainingVerification"]))
|
||||||
|
|
||||||
@@ -269,6 +289,9 @@ class AgentInstallTests(unittest.TestCase):
|
|||||||
self.assertEqual(manifest["authentication"]["secretEntry"], "user-controlled-private-tty")
|
self.assertEqual(manifest["authentication"]["secretEntry"], "user-controlled-private-tty")
|
||||||
self.assertFalse(manifest["authentication"]["allowPassword"])
|
self.assertFalse(manifest["authentication"]["allowPassword"])
|
||||||
self.assertFalse(manifest["authentication"]["allowCommandLineSecret"])
|
self.assertFalse(manifest["authentication"]["allowCommandLineSecret"])
|
||||||
|
self.assertEqual(manifest["runtime"]["launcher"], "project-venv-python")
|
||||||
|
self.assertEqual(manifest["runtime"]["dependencyProfile"], "runtime-only-no-dev")
|
||||||
|
self.assertFalse(manifest["installation"]["defaultBackgroundService"])
|
||||||
self.assertTrue(manifest["securityDefaults"]["readOnly"])
|
self.assertTrue(manifest["securityDefaults"]["readOnly"])
|
||||||
self.assertFalse(manifest["securityDefaults"]["uploads"])
|
self.assertFalse(manifest["securityDefaults"]["uploads"])
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,6 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.14' and sys_platform != 'win32'",
|
"python_full_version < '3.14' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "annotated-doc"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "annotated-types"
|
name = "annotated-types"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -639,12 +630,6 @@ 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/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
|
||||||
cli = [
|
|
||||||
{ name = "python-dotenv" },
|
|
||||||
{ name = "typer" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdurl"
|
name = "mdurl"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -765,7 +750,7 @@ source = { editable = "." }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cryptography" },
|
{ name = "cryptography" },
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
{ name = "mcp", extra = ["cli"] },
|
{ name = "mcp" },
|
||||||
{ name = "python-multipart" },
|
{ name = "python-multipart" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -780,7 +765,7 @@ dev = [
|
|||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "cryptography", specifier = ">=50,<51" },
|
{ name = "cryptography", specifier = ">=50,<51" },
|
||||||
{ name = "httpx", specifier = ">=0.27,<1" },
|
{ name = "httpx", specifier = ">=0.27,<1" },
|
||||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.27,<2" },
|
{ name = "mcp", specifier = ">=1.27,<2" },
|
||||||
{ name = "python-multipart", specifier = ">=0.0.9,<1" },
|
{ name = "python-multipart", specifier = ">=0.0.9,<1" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1239,15 +1224,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/69/3e/4132e539aed78c148854d4997a2685b0ed4dc4e87110b59ce528564e184e/ruff-0.16.3-py3-none-win_arm64.whl", hash = "sha256:b8ca152da82c1acc1fa8d5874b15951935f0eef46f10e6954c83859011b6178a", size = 11399302, upload-time = "2026-08-13T15:17:10.908Z" },
|
{ url = "https://files.pythonhosted.org/packages/69/3e/4132e539aed78c148854d4997a2685b0ed4dc4e87110b59ce528564e184e/ruff-0.16.3-py3-none-win_arm64.whl", hash = "sha256:b8ca152da82c1acc1fa8d5874b15951935f0eef46f10e6954c83859011b6178a", size = 11399302, upload-time = "2026-08-13T15:17:10.908Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shellingham"
|
|
||||||
version = "1.5.4"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sortedcontainers"
|
name = "sortedcontainers"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@@ -1337,21 +1313,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typer"
|
|
||||||
version = "0.26.8"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "annotated-doc" },
|
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
||||||
{ name = "rich" },
|
|
||||||
{ name = "shellingham" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/f7/68adc395201b20b872d68e975386832e8005ffeacedd43a1d837a32815be/typer-0.26.8.tar.gz", hash = "sha256:c244a6bd558886fe3f8780efb6bdd28bb9aff005a94eedebaa5cb32926fe2f7e", size = 202097, upload-time = "2026-06-26T09:22:45.705Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/80/87/b9fd69c92c6102a066e1b86a35243f53e70bd4c709f2a26d9f4fee4f4dc0/typer-0.26.8-py3-none-any.whl", hash = "sha256:3512ca79ac5c11113414b36e80281b872884477722440691c89d1112e321a49c", size = 122564, upload-time = "2026-06-26T09:22:44.72Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.16.0"
|
version = "4.16.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user