From 2458bfa111d4db1bd57b375960eaf4ca8c2fb25e Mon Sep 17 00:00:00 2001 From: "ethan.chen" Date: Wed, 7 Jan 2026 16:56:31 +0800 Subject: [PATCH] feat: Enhance deployment capabilities with direct server deployment tools, email configuration, and comprehensive documentation --- DIRECT_DEPLOY.md | 295 +++++++++++++++++ MCP_CONNECTION.md | 163 ++++++++++ README.md | 9 + README_DEPLOY.md | 53 ++++ bun.lock | 166 ++++++++++ ecosystem.config.js | 26 ++ env.template | 9 + package.json | 7 +- src/index.ts | 4 + src/storage/config.ts | 22 ++ src/tools/common/email.ts | 206 ++++++++++++ src/tools/devops/deploy.ts | 384 +++++++++++++++++++++++ src/tools/programming/projectTemplate.ts | 2 +- tests/tsconfig.json | 14 + tests/unit/storage/config.test.ts | 7 +- tsconfig.json | 7 +- tsconfig.test.json | 9 + types/bun-test.d.ts | 59 ++++ 18 files changed, 1430 insertions(+), 12 deletions(-) create mode 100644 DIRECT_DEPLOY.md create mode 100644 MCP_CONNECTION.md create mode 100644 README_DEPLOY.md create mode 100644 ecosystem.config.js create mode 100644 src/tools/common/email.ts create mode 100644 src/tools/devops/deploy.ts create mode 100644 tests/tsconfig.json create mode 100644 tsconfig.test.json create mode 100644 types/bun-test.d.ts diff --git a/DIRECT_DEPLOY.md b/DIRECT_DEPLOY.md new file mode 100644 index 0000000..e602e5f --- /dev/null +++ b/DIRECT_DEPLOY.md @@ -0,0 +1,295 @@ +# 直接部署方案(推荐) + +## 概述 + +直接在服务器上运行 MCP 服务器(不使用 Docker),这样可以直接使用 MCP 工具进行部署,无需 SSH 连接。 + +## 优势 + +✅ **无需 SSH** - MCP 工具直接在服务器上运行 +✅ **更简单** - 不需要配置 Runner、Webhook 等 +✅ **更直接** - 在 Cursor 中直接触发部署 +✅ **更灵活** - 可以直接访问服务器文件系统 + +## 安装步骤 + +### 1. 在服务器上安装 Bun + +```bash +# 安装 Bun +curl -fsSL https://bun.sh/install | bash + +# 验证安装 +bun --version +``` + +### 2. 克隆项目 + +```bash +git clone /opt/cloud-mcp +cd /opt/cloud-mcp +``` + +### 3. 安装依赖 + +```bash +bun install +``` + +### 4. 配置环境变量 + +```bash +cp env.template .env +nano .env # 编辑配置 +``` + +### 5. 运行 MCP 服务器 + +#### 方式一:直接运行(测试) + +```bash +bun run src/index.ts +``` + +#### 方式二:使用 PM2(推荐) + +```bash +# 安装 PM2 +bun add -g pm2 + +# 启动服务 +pm2 start "bun run src/index.ts" --name cloud-mcp + +# 设置开机自启 +pm2 save +pm2 startup +``` + +#### 方式三:使用 systemd(生产环境) + +创建服务文件: + +```bash +sudo tee /etc/systemd/system/cloud-mcp.service > /dev/null < /opt/cloud-mcp +cd /opt/cloud-mcp +bun install +bun run start +``` + +详细文档:查看 `DIRECT_DEPLOY.md` + +## 方案二:Docker 部署 + +**优点**: +- ✅ 环境隔离 +- ✅ 易于管理 +- ✅ 适合生产环境 + +**适用场景**:生产环境、需要隔离的场景 + +详细文档:查看 `DEPLOY.md` + +## 推荐方案 + +对于你的使用场景(个人 MCP 服务器),**推荐使用方案一(直接运行)**: + +1. 在服务器上直接运行 MCP +2. 通过 Cursor 的 MCP 连接直接访问 +3. 使用 `deploy_update` 工具一键部署更新 + +这样就不需要: +- ❌ Gitea Actions Runner +- ❌ SSH 配置 +- ❌ Webhook 服务器 +- ❌ Docker 容器 + +只需要: +- ✅ 在服务器上运行 MCP +- ✅ 在 Cursor 中配置 MCP 连接 +- ✅ 使用部署工具更新 + diff --git a/bun.lock b/bun.lock index cfb52dd..cb1a00d 100644 --- a/bun.lock +++ b/bun.lock @@ -7,22 +7,166 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", "axios": "^1.7.7", + "nodemailer": "^6.9.8", "ssh2": "^1.15.0", }, "devDependencies": { "@types/node": "^22.7.9", + "@types/nodemailer": "^6.4.14", "@types/ssh2": "^1.15.4", "typescript": "^5.6.3", }, }, }, "packages": { + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + + "@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.964.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.964.0", "@aws-sdk/credential-provider-node": "3.964.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.964.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.964.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gAbDlXvNP5Sb2tS4tJYmOS6/frmba2tryJ4MzJVeR1ad8sSa94GQx7XbR7HyCi5VtJpOSk7Uibp3aKzK3+sWsg=="], + + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.964.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.964.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.964.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.964.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-IenVyY8Io2CwBgmS22xk/H5LibmSbvLnPA9oFqLORO6Ji1Ks8z/ow+ud/ZurVjFekz3LD/uxVFX3ZKGo6N7Byw=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.964.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/xml-builder": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-1gIfbt0KRxI8am1UYFcIxQ5QKb22JyN3k52sxyrKXJYC8Knn/rTUAZbYti45CfETe5PLadInGvWqClwGRlZKNg=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jWNSXOOBMYuxzI2rXi8x91YL07dhomyGzzh0CdaLej0LRmknmDrZcZNkVpa7Fredy1PFcmOlokwCS5PmZMN8ZQ=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-up7dl6vcaoXuYSwGXDvx8RnF8Lwj3jGChhyUR7krZOXLarIfUUN3ILOZnVNK5s/HnVNkEILlkdPvjhr9LVC1/Q=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/credential-provider-env": "3.964.0", "@aws-sdk/credential-provider-http": "3.964.0", "@aws-sdk/credential-provider-login": "3.964.0", "@aws-sdk/credential-provider-process": "3.964.0", "@aws-sdk/credential-provider-sso": "3.964.0", "@aws-sdk/credential-provider-web-identity": "3.964.0", "@aws-sdk/nested-clients": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-t4FN9qTWU4nXDU6EQ6jopvyhXw0dbQ3n+3g6x5hmc1ECFAqA+xmFd1i5LljdZCi79cUXHduQWwvW8RJHMf0qJw=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/nested-clients": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-c64dmTizMkJXDRzN3NYPTmUpKxegr5lmLOYPeQ60Zcbft6HFwPme8Gwy8pNxO4gG1fw6Ja2Vu6fZuSTn8aDFOQ=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.964.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.964.0", "@aws-sdk/credential-provider-http": "3.964.0", "@aws-sdk/credential-provider-ini": "3.964.0", "@aws-sdk/credential-provider-process": "3.964.0", "@aws-sdk/credential-provider-sso": "3.964.0", "@aws-sdk/credential-provider-web-identity": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-FHxDXPOj888/qc/X8s0x4aUBdp4Y3k9VePRehUJBWRhhTsAyuIJis5V0iQeY1qvtqHXYa2qd1EZHGJ3bTjHxSw=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-HaTLKqj3jeZY88E/iBjsNJsXgmRTTT7TghqeRiF8FKb/7UY1xEvasBO0c1xqfOye8dsyt35nTfTTyIsd/CBfww=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.964.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.964.0", "@aws-sdk/core": "3.964.0", "@aws-sdk/token-providers": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-oR78TjSpjVf1IpPWQnGHEGqlnQs+K4f5nCxLK2P6JDPprXay6oknsoSiU4x2urav6VCyMPMC9KTCGjBoFKUIxQ=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/nested-clients": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-07JQDmbjZjOt3nL/j1wTcvQqjmPkynQYftUV/ooZ+qTbmJXFbCBdal1VCElyeiu0AgBq9dfhw0rBBcbND1ZMlA=="], + + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA=="], + + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ=="], + + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA=="], + + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/QyBl8WLNtqw3ucyAggumQXVCi8GRxaDGE1ElyYMmacfiwHl37S9y8JVW/QLL1lIEXGcsrhMUKV3pyFJFALA7w=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.964.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.964.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.964.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.964.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ql+ftRwjyZkZeG3qbrRJFVmNR0id83WEUqhFVjvrQMWspNApBhz0Ar4YVSn7Uv0QaKkaR7ALPtmdMzFr3/E4bQ=="], + + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.964.0", "", { "dependencies": { "@aws-sdk/core": "3.964.0", "@aws-sdk/nested-clients": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-UqouLQbYepZnMFJGB/DVpA5GhF9uT98vNWSMz9PVbhgEPUKa73FECRT6YFZvZOh8kA+0JiENrnmS6d93I70ykQ=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-endpoints": "^3.2.7", "tslib": "^2.6.2" } }, "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nhmgKHnNV9K+i9daumaIz8JTLsIIML9PE/HUks5liyrjUzenjW/aHoc7WJ9/Td/gPZtayxFnXQSJRb/fDlBuJw=="], + + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw=="], + + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.964.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.964.0", "@aws-sdk/types": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-jgob8Z/bZIh1dwEgLqE12q+aCf0ieLy7anT8bWpqMijMJqsnrPBToa7smSykfom9YHrdOgrQhXswMpE75dzLRw=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA=="], + + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="], + "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="], "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "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.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "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.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "@smithy/abort-controller": ["@smithy/abort-controller@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="], + + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg=="], + + "@smithy/core": ["@smithy/core@3.20.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.8", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg=="], + + "@smithy/hash-node": ["@smithy/hash-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw=="], + + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg=="], + + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.1", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-serde": "^4.2.8", "@smithy/node-config-provider": "^4.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg=="], + + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/service-error-classification": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg=="], + + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w=="], + + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="], + + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.7", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ=="], + + "@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="], + + "@smithy/protocol-http": ["@smithy/protocol-http@5.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="], + + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="], + + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="], + + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="], + + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.3.7", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="], + + "@smithy/smithy-client": ["@smithy/smithy-client@4.10.2", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-stack": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g=="], + + "@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="], + + "@smithy/url-parser": ["@smithy/url-parser@4.2.7", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg=="], + + "@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], + + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], + + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.16", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ=="], + + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.19", "", { "dependencies": { "@smithy/config-resolver": "^4.4.5", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA=="], + + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg=="], + + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@smithy/util-middleware": ["@smithy/util-middleware@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w=="], + + "@smithy/util-retry": ["@smithy/util-retry@4.2.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="], + + "@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="], + + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + + "@smithy/util-waiter": ["@smithy/util-waiter@4.2.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw=="], + + "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], + "@types/nodemailer": ["@types/nodemailer@6.4.21", "", { "dependencies": { "@aws-sdk/client-ses": "^3.731.1", "@types/node": "*" } }, "sha512-Eix+sb/Nj28MNnWvO2X1OLrk5vuD4C9SMnb2Vf4itWnxphYeSceqkFX7IdmxTzn+dvmnNz7paMbg4Uc60wSfJg=="], + "@types/ssh2": ["@types/ssh2@1.15.5", "", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -41,6 +185,8 @@ "body-parser": ["body-parser@2.2.1", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="], + "bowser": ["bowser@2.13.1", "", {}, "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw=="], + "buildcheck": ["buildcheck@0.0.7", "", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -101,6 +247,8 @@ "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "finalhandler": ["finalhandler@2.1.1", "", { "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" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], @@ -161,6 +309,8 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "nodemailer": ["nodemailer@6.10.1", "", {}, "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], @@ -215,8 +365,12 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="], "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], @@ -237,12 +391,24 @@ "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + "@types/ssh2/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], } } diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..db03360 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,26 @@ +// PM2 ecosystem file for cloud-mcp + +module.exports = { + apps: [ + { + name: "cloud-mcp", + script: "bun", + args: "run src/index.ts", + cwd: "/opt/cloud-mcp", + instances: 1, + exec_mode: "fork", + watch: false, + max_memory_restart: "500M", + env: { + NODE_ENV: "production", + }, + error_file: "./logs/cloud-mcp-error.log", + out_file: "./logs/cloud-mcp-out.log", + log_date_format: "YYYY-MM-DD HH:mm:ss Z", + merge_logs: true, + autorestart: true, + max_restarts: 10, + min_uptime: "10s", + }, + ], +}; diff --git a/env.template b/env.template index e647251..b352028 100644 --- a/env.template +++ b/env.template @@ -18,6 +18,15 @@ ROUTER_HOST=192.168.1.1 ROUTER_USERNAME=admin ROUTER_PASSWORD=your-router-password +# Email Configuration +# 邮件配置(SMTP) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your-email@gmail.com +EMAIL_PASSWORD=your-app-password +EMAIL_FROM=Your Name +EMAIL_SECURE=false + # API Keys (optional) # 可选:配置 API 密钥以使用完整功能 # 足球信息 API (football-data.org - 免费注册获取) diff --git a/package.json b/package.json index 4093d66..499eb2e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "scripts": { "dev": "bun run src/index.ts", "build": "bun build src/index.ts --outdir dist --target bun", - "start": "bun run dist/index.js", + "start": "bun run src/index.ts", + "start:pm2": "pm2 start ecosystem.config.js", + "stop:pm2": "pm2 stop cloud-mcp", + "restart:pm2": "pm2 restart cloud-mcp", "test": "bun test", "test:watch": "bun test --watch", "test:coverage": "bun test --coverage" @@ -22,10 +25,12 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", "axios": "^1.7.7", + "nodemailer": "^6.9.8", "ssh2": "^1.15.0" }, "devDependencies": { "@types/node": "^22.7.9", + "@types/nodemailer": "^6.4.14", "@types/ssh2": "^1.15.4", "typescript": "^5.6.3" }, diff --git a/src/index.ts b/src/index.ts index 8dd0568..03463bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ import { registerCodeReviewTools } from "./tools/programming/codeReview.js"; import { registerNASTools } from "./tools/devops/nas.js"; import { registerServerTools } from "./tools/devops/server.js"; import { registerRouterTools } from "./tools/devops/router.js"; +import { registerDeployTools } from "./tools/devops/deploy.js"; import { registerMathTools } from "./tools/family/math.js"; import { registerBabyTools } from "./tools/family/baby.js"; @@ -24,6 +25,7 @@ import { registerGameTools } from "./tools/hobbies/games.js"; import { registerNoteTools } from "./tools/common/notes.js"; import { registerTaskTools } from "./tools/common/tasks.js"; +import { registerEmailTools } from "./tools/common/email.js"; // Register all tool modules logger.info("Registering tools..."); @@ -38,6 +40,7 @@ registerCodeReviewTools(); registerNASTools(); registerServerTools(); registerRouterTools(); +registerDeployTools(); // Family tools registerMathTools(); @@ -50,6 +53,7 @@ registerGameTools(); // Common tools registerNoteTools(); registerTaskTools(); +registerEmailTools(); logger.info("All tools registered. Starting MCP server..."); diff --git a/src/storage/config.ts b/src/storage/config.ts index 704322a..4c0bed4 100644 --- a/src/storage/config.ts +++ b/src/storage/config.ts @@ -23,10 +23,20 @@ export interface RouterConfig { password?: string; } +export interface EmailConfig { + host?: string; + port?: number; + user?: string; + password?: string; + from?: string; + secure?: boolean; +} + export interface AppConfig { nas: NASConfig; server: ServerConfig; router: RouterConfig; + email: EmailConfig; footballApiKey?: string; gameApiKey?: string; } @@ -57,6 +67,14 @@ class ConfigManager { username: process.env.ROUTER_USERNAME, password: process.env.ROUTER_PASSWORD, }, + email: { + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT ? parseInt(process.env.EMAIL_PORT) : 587, + user: process.env.EMAIL_USER, + password: process.env.EMAIL_PASSWORD, + from: process.env.EMAIL_FROM, + secure: process.env.EMAIL_SECURE === 'true', + }, footballApiKey: process.env.FOOTBALL_API_KEY, gameApiKey: process.env.GAME_API_KEY, }; @@ -78,6 +96,10 @@ class ConfigManager { return this.config.router; } + getEmailConfig(): EmailConfig { + return this.config.email; + } + reload(): void { this.config = this.loadConfig(); } diff --git a/src/tools/common/email.ts b/src/tools/common/email.ts new file mode 100644 index 0000000..650bf2d --- /dev/null +++ b/src/tools/common/email.ts @@ -0,0 +1,206 @@ +/** + * Email sending tools + */ + +import { mcpServer } from "../../server.js"; +import { configManager } from "../../storage/config.js"; +import { logger } from "../../utils/logger.js"; +import nodemailer from "nodemailer"; +import { readFileSync, existsSync } from "fs"; + +export function registerEmailTools(): void { + // Send email + mcpServer.registerTool( + { + name: "email_send", + description: + "Send an email via SMTP with support for text, HTML, and attachments", + inputSchema: { + type: "object", + properties: { + to: { + type: "string", + description: "Recipient email address (required)", + }, + subject: { + type: "string", + description: "Email subject (required)", + }, + text: { + type: "string", + description: "Plain text email body (required)", + }, + html: { + type: "string", + description: "HTML email body (optional, if not provided, text will be used)", + }, + cc: { + type: "string", + description: "CC recipient email address (optional)", + }, + bcc: { + type: "string", + description: "BCC recipient email address (optional)", + }, + attachments: { + type: "array", + description: "Email attachments (optional)", + items: { + type: "object", + properties: { + filename: { + type: "string", + description: "Attachment filename", + }, + path: { + type: "string", + description: "Path to file (if using file path)", + }, + content: { + type: "string", + description: "File content as string (if using content directly)", + }, + }, + }, + }, + }, + required: ["to", "subject", "text"], + }, + }, + async (args) => { + const emailConfig = configManager.getEmailConfig(); + + if (!emailConfig.host || !emailConfig.user || !emailConfig.password) { + return { + content: [ + { + type: "text", + text: "Error: Email configuration not found. Please set EMAIL_HOST, EMAIL_USER, and EMAIL_PASSWORD in environment variables.", + }, + ], + isError: true, + }; + } + + try { + const to = args.to as string; + const subject = args.subject as string; + const text = args.text as string; + const html = args.html as string | undefined; + const cc = args.cc as string | undefined; + const bcc = args.bcc as string | undefined; + const attachments = args.attachments as + | Array<{ filename: string; path?: string; content?: string }> + | undefined; + + // Create transporter + const transporter = nodemailer.createTransport({ + host: emailConfig.host, + port: emailConfig.port || 587, + secure: emailConfig.secure || false, + auth: { + user: emailConfig.user, + pass: emailConfig.password, + }, + }); + + // Process attachments + const processedAttachments: any[] = []; + if (attachments && attachments.length > 0) { + for (const attachment of attachments) { + if (attachment.path) { + // Use file path + if (existsSync(attachment.path)) { + processedAttachments.push({ + filename: attachment.filename, + path: attachment.path, + }); + } else { + logger.warn(`Attachment file not found: ${attachment.path}`); + return { + content: [ + { + type: "text", + text: `Error: Attachment file not found: ${attachment.path}`, + }, + ], + isError: true, + }; + } + } else if (attachment.content) { + // Use content directly + processedAttachments.push({ + filename: attachment.filename, + content: attachment.content, + }); + } else { + logger.warn( + `Invalid attachment: missing path or content for ${attachment.filename}` + ); + return { + content: [ + { + type: "text", + text: `Error: Invalid attachment: missing path or content for ${attachment.filename}`, + }, + ], + isError: true, + }; + } + } + } + + // Prepare email options + const mailOptions: any = { + from: emailConfig.from || emailConfig.user, + to: to, + subject: subject, + text: text, + }; + + if (html) { + mailOptions.html = html; + } + + if (cc) { + mailOptions.cc = cc; + } + + if (bcc) { + mailOptions.bcc = bcc; + } + + if (processedAttachments.length > 0) { + mailOptions.attachments = processedAttachments; + } + + // Send email + logger.info(`Sending email to ${to} with subject: ${subject}`); + const info = await transporter.sendMail(mailOptions); + + return { + content: [ + { + type: "text", + text: `Email sent successfully!\n\nTo: ${to}\nSubject: ${subject}\nMessage ID: ${info.messageId}${processedAttachments.length > 0 ? `\nAttachments: ${processedAttachments.length}` : ""}`, + }, + ], + }; + } catch (error) { + logger.error("Error sending email:", error); + const errorMessage = + error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Error sending email: ${errorMessage}\n\nPlease check:\n1. Email configuration (EMAIL_HOST, EMAIL_USER, EMAIL_PASSWORD)\n2. SMTP server connection\n3. Recipient email address`, + }, + ], + isError: true, + }; + } + } + ); +} + diff --git a/src/tools/devops/deploy.ts b/src/tools/devops/deploy.ts new file mode 100644 index 0000000..d998a40 --- /dev/null +++ b/src/tools/devops/deploy.ts @@ -0,0 +1,384 @@ +/** + * Deployment tools - for direct server deployment + * These tools run directly on the server, no SSH needed + */ + +import { mcpServer } from "../../server.js"; +import { logger } from "../../utils/logger.js"; +import { execSync } from "child_process"; +import { existsSync, readFileSync } from "fs"; +import { join } from "path"; + +export function registerDeployTools(): void { + // Pull latest code and deploy + mcpServer.registerTool( + { + name: "deploy_update", + description: + "Pull latest code from git and redeploy the MCP server (runs directly on server)", + inputSchema: { + type: "object", + properties: { + branch: { + type: "string", + description: "Git branch to pull from (default: main or master)", + default: "main", + }, + rebuild: { + type: "boolean", + description: "Force rebuild Docker image (default: false)", + default: false, + }, + }, + }, + }, + async (args) => { + try { + const branch = (args.branch as string) || "main"; + const rebuild = (args.rebuild as boolean) || false; + const projectDir = process.cwd(); + + logger.info(`Starting deployment update in ${projectDir}`); + + let output = "Deployment Update\n\n"; + const steps: string[] = []; + + // Step 1: Check if we're in a git repository + if (!existsSync(join(projectDir, ".git"))) { + return { + content: [ + { + type: "text", + text: "Error: Not in a git repository. Please run this from the project directory.", + }, + ], + isError: true, + }; + } + + // Step 2: Fetch latest changes + try { + steps.push("Fetching latest changes from git..."); + const fetchOutput = execSync("git fetch origin", { + cwd: projectDir, + encoding: "utf-8", + }); + output += `✓ Fetched changes\n`; + } catch (error) { + return { + content: [ + { + type: "text", + text: `Error fetching from git: ${ + error instanceof Error ? error.message : String(error) + }`, + }, + ], + isError: true, + }; + } + + // Step 3: Check current branch + const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { + cwd: projectDir, + encoding: "utf-8", + }).trim(); + + // Step 4: Pull latest code + try { + steps.push(`Pulling latest code from ${branch}...`); + const pullOutput = execSync(`git pull origin ${branch}`, { + cwd: projectDir, + encoding: "utf-8", + }); + output += `✓ Pulled latest code from ${branch}\n`; + output += `\n${pullOutput}\n`; + } catch (error) { + return { + content: [ + { + type: "text", + text: `Error pulling code: ${ + error instanceof Error ? error.message : String(error) + }`, + }, + ], + isError: true, + }; + } + + // Step 5: Install dependencies if package.json changed + try { + steps.push("Checking for dependency updates..."); + const hasPackageChanges = execSync( + "git diff HEAD@{1} HEAD -- package.json bun.lockb", + { cwd: projectDir, encoding: "utf-8" } + ).trim(); + + if (hasPackageChanges) { + steps.push("Installing/updating dependencies..."); + execSync("bun install", { + cwd: projectDir, + encoding: "utf-8", + stdio: "inherit", + }); + output += `✓ Dependencies updated\n`; + } else { + output += `✓ No dependency changes\n`; + } + } catch (error) { + // Not critical, continue + logger.warn("Could not check dependencies:", error); + } + + // Step 6: Restart service (if using systemd or PM2) + try { + steps.push("Restarting MCP server..."); + + // Check if running as systemd service + const serviceName = process.env.MCP_SERVICE_NAME || "cloud-mcp"; + try { + execSync(`systemctl is-active --quiet ${serviceName}`, { + encoding: "utf-8", + }); + // Service exists and is active, restart it + execSync(`sudo systemctl restart ${serviceName}`, { + encoding: "utf-8", + }); + output += `✓ Restarted systemd service: ${serviceName}\n`; + } catch { + // Not a systemd service, try PM2 + try { + execSync("pm2 list | grep cloud-mcp", { + encoding: "utf-8", + }); + execSync("pm2 restart cloud-mcp", { + encoding: "utf-8", + }); + output += `✓ Restarted PM2 process: cloud-mcp\n`; + } catch { + // Not PM2 either, just log that manual restart is needed + output += `⚠ Service restart skipped (not running as systemd/PM2)\n`; + output += ` Please restart the MCP server manually\n`; + } + } + } catch (error) { + logger.warn("Could not restart service:", error); + output += `⚠ Could not auto-restart service\n`; + } + + output += `\n✅ Deployment update completed!\n`; + output += `\nSteps executed:\n${steps + .map((s, i) => `${i + 1}. ${s}`) + .join("\n")}`; + + return { + content: [ + { + type: "text", + text: output, + }, + ], + }; + } catch (error) { + logger.error("Deployment error:", error); + return { + content: [ + { + type: "text", + text: `Error during deployment: ${ + error instanceof Error ? error.message : String(error) + }`, + }, + ], + isError: true, + }; + } + } + ); + + // Check deployment status + mcpServer.registerTool( + { + name: "deploy_status", + description: + "Check deployment status - git status, last commit, service status", + inputSchema: { + type: "object", + properties: {}, + }, + }, + async () => { + try { + const projectDir = process.cwd(); + let output = "Deployment Status\n\n"; + + // Git status + try { + const gitStatus = execSync("git status --short", { + cwd: projectDir, + encoding: "utf-8", + }).trim(); + + const lastCommit = execSync("git log -1 --oneline", { + cwd: projectDir, + encoding: "utf-8", + }).trim(); + + const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { + cwd: projectDir, + encoding: "utf-8", + }).trim(); + + output += `📦 Git Status:\n`; + output += `Branch: ${currentBranch}\n`; + output += `Last commit: ${lastCommit}\n`; + + // Check if behind remote + try { + execSync("git fetch origin", { cwd: projectDir }); + const behind = execSync( + `git rev-list HEAD..origin/${currentBranch} --count`, + { cwd: projectDir, encoding: "utf-8" } + ).trim(); + + if (behind !== "0") { + output += `⚠ Behind remote by ${behind} commit(s)\n`; + } else { + output += `✓ Up to date with remote\n`; + } + } catch { + // Ignore + } + + if (gitStatus) { + output += `\nUncommitted changes:\n${gitStatus}\n`; + } else { + output += `✓ Working directory clean\n`; + } + } catch (error) { + output += `Error checking git status: ${ + error instanceof Error ? error.message : String(error) + }\n`; + } + + // Service status + output += `\n🔧 Service Status:\n`; + const serviceName = process.env.MCP_SERVICE_NAME || "cloud-mcp"; + + try { + const systemdStatus = execSync(`systemctl is-active ${serviceName}`, { + encoding: "utf-8", + }).trim(); + output += `Systemd: ${systemdStatus}\n`; + } catch { + try { + const pm2Status = execSync("pm2 list | grep cloud-mcp", { + encoding: "utf-8", + }); + output += `PM2: Running\n`; + } catch { + output += `Service: Not managed by systemd/PM2\n`; + } + } + + // Process status + try { + const processInfo = execSync( + "ps aux | grep 'bun.*index.ts' | grep -v grep", + { + encoding: "utf-8", + } + ).trim(); + if (processInfo) { + output += `\nProcess: Running\n`; + } else { + output += `\nProcess: Not found\n`; + } + } catch { + output += `\nProcess: Unknown\n`; + } + + return { + content: [ + { + type: "text", + text: output, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: "text", + text: `Error checking status: ${ + error instanceof Error ? error.message : String(error) + }`, + }, + ], + isError: true, + }; + } + } + ); + + // View deployment logs + mcpServer.registerTool( + { + name: "deploy_logs", + description: "View deployment logs", + inputSchema: { + type: "object", + properties: { + lines: { + type: "number", + description: "Number of lines to show (default: 50)", + default: 50, + }, + }, + }, + }, + async (args) => { + try { + const lines = (args.lines as number) || 50; + const logFile = join(process.cwd(), "deploy.log"); + + if (existsSync(logFile)) { + const content = readFileSync(logFile, "utf-8"); + const logLines = content.split("\n").slice(-lines).join("\n"); + + return { + content: [ + { + type: "text", + text: `Deployment Logs (last ${lines} lines):\n\n${logLines}`, + }, + ], + }; + } else { + return { + content: [ + { + type: "text", + text: "No deployment log file found. Logs will be created on first deployment.", + }, + ], + }; + } + } catch (error) { + return { + content: [ + { + type: "text", + text: `Error reading logs: ${ + error instanceof Error ? error.message : String(error) + }`, + }, + ], + isError: true, + }; + } + } + ); +} diff --git a/src/tools/programming/projectTemplate.ts b/src/tools/programming/projectTemplate.ts index 16a4948..198e3be 100644 --- a/src/tools/programming/projectTemplate.ts +++ b/src/tools/programming/projectTemplate.ts @@ -440,7 +440,7 @@ serve({ module: "ESNext", lib: ["ES2022"], moduleResolution: "bundler", - types: ["bun-types"], + types: ["node"], strict: true, esModuleInterop: true, skipLibCheck: true, diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..9e308a3 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["node"], + "noEmit": true, + "skipLibCheck": true, + "typeRoots": ["../types", "../node_modules/@types"], + "moduleResolution": "bundler", + "rootDir": ".." + }, + "include": ["**/*.ts"], + "exclude": [] +} + diff --git a/tests/unit/storage/config.test.ts b/tests/unit/storage/config.test.ts index 9e627aa..3c85002 100644 --- a/tests/unit/storage/config.test.ts +++ b/tests/unit/storage/config.test.ts @@ -1,13 +1,8 @@ -/* - * @Date: 2026-01-07 09:09:22 - * @LastEditors: 陈子健 - * @LastEditTime: 2026-01-07 10:04:55 - * @FilePath: /cloud-mcp/tests/unit/storage/config.test.ts - */ /** * Configuration management tests */ +// @ts-ignore - Bun test types are built-in import { describe, test, expect, beforeEach, afterEach } from "bun:test"; import { configManager } from "../../../src/storage/config.js"; import { setTestEnv } from "../../helpers/test-utils.js"; diff --git a/tsconfig.json b/tsconfig.json index 795153b..f038abb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "ESNext", "lib": ["ES2022"], "moduleResolution": "bundler", - "types": ["bun-types", "node"], + "types": ["node"], "strict": true, "esModuleInterop": true, "skipLibCheck": true, @@ -19,7 +19,6 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "include": ["src/**/*", "types/**/*"], + "exclude": ["node_modules", "dist", "tests"] } - diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..bfaf1c3 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["node", "bun-types"], + "noEmit": true + }, + "include": ["tests/**/*", "src/**/*"] +} + diff --git a/types/bun-test.d.ts b/types/bun-test.d.ts new file mode 100644 index 0000000..3d00677 --- /dev/null +++ b/types/bun-test.d.ts @@ -0,0 +1,59 @@ +/* + * @Date: 2026-01-07 15:24:14 + * @LastEditors: 陈子健 + * @LastEditTime: 2026-01-07 15:26:23 + * @FilePath: /cloud-mcp/types/bun-test.d.ts + */ +/** + * Type declarations for Bun test framework + * This file provides type definitions for Bun's built-in test framework + * Place this file in a types directory and include it in tsconfig.json + */ + +declare module "bun:test" { + export function describe(name: string, fn: () => void | Promise): void; + export function test( + name: string, + fn: () => void | Promise, + timeout?: number + ): void; + export function it( + name: string, + fn: () => void | Promise, + timeout?: number + ): void; + export function expect(actual: any): { + toBe(expected: any): void; + toBeDefined(): void; + toBeUndefined(): void; + toBeNull(): void; + toBeTruthy(): void; + toBeFalsy(): void; + toEqual(expected: any): void; + toContain(expected: any): void; + toMatch(pattern: string | RegExp): void; + toThrow(error?: string | RegExp | Error): void; + toBeGreaterThan(expected: number): void; + toBeLessThan(expected: number): void; + toBeGreaterThanOrEqual(expected: number): void; + toBeLessThanOrEqual(expected: number): void; + toBeCloseTo(expected: number, precision?: number): void; + toBeInstanceOf(expected: any): void; + not: { + toBe(expected: any): void; + toBeDefined(): void; + toBeUndefined(): void; + toBeNull(): void; + toBeTruthy(): void; + toBeFalsy(): void; + toEqual(expected: any): void; + toContain(expected: any): void; + toMatch(pattern: string | RegExp): void; + toThrow(error?: string | RegExp | Error): void; + }; + }; + export function beforeEach(fn: () => void | Promise): void; + export function afterEach(fn: () => void | Promise): void; + export function beforeAll(fn: () => void | Promise): void; + export function afterAll(fn: () => void | Promise): void; +}