@cloudbase/cloudbase-mcp 2.26.0 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/README.zh-CN.md +1 -1
- package/dist/341.index.js +6 -1
- package/dist/602.index.cjs +6 -1
- package/dist/cli.cjs +4 -4
- package/dist/index.cjs +791 -530
- package/dist/index.d.ts +8 -1
- package/dist/index.js +794 -517
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import * as __WEBPACK_EXTERNAL_MODULE_https__ from "https";
|
|
|
13
13
|
import * as __WEBPACK_EXTERNAL_MODULE_net__ from "net";
|
|
14
14
|
import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs";
|
|
15
15
|
import * as __WEBPACK_EXTERNAL_MODULE__modelcontextprotocol_sdk_server_mcp_js_45c326f0__ from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_path_02319fef__ from "node:path";
|
|
16
17
|
import * as __WEBPACK_EXTERNAL_MODULE_child_process__ from "child_process";
|
|
17
18
|
import * as __WEBPACK_EXTERNAL_MODULE_ws__ from "ws";
|
|
18
19
|
import * as __WEBPACK_EXTERNAL_MODULE__cloudbase_manager_node_6a52ea40__ from "@cloudbase/manager-node";
|
|
@@ -23,6 +24,7 @@ import * as __WEBPACK_EXTERNAL_MODULE_crypto__ from "crypto";
|
|
|
23
24
|
import * as __WEBPACK_EXTERNAL_MODULE_fs_promises_f8dae9d1__ from "fs/promises";
|
|
24
25
|
import * as __WEBPACK_EXTERNAL_MODULE_os__ from "os";
|
|
25
26
|
import * as __WEBPACK_EXTERNAL_MODULE_winston__ from "winston";
|
|
27
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__ from "node:fs";
|
|
26
28
|
/******/ var __webpack_modules__ = ({
|
|
27
29
|
|
|
28
30
|
/***/ 622:
|
|
@@ -2550,6 +2552,7 @@ function registerEnvTools(server) {
|
|
|
2550
2552
|
|
|
2551
2553
|
|
|
2552
2554
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2555
|
+
exports.assertTcbCloudRunActionAllowed = assertTcbCloudRunActionAllowed;
|
|
2553
2556
|
exports.removeEmptyStringParams = removeEmptyStringParams;
|
|
2554
2557
|
exports.buildCapiErrorMessage = buildCapiErrorMessage;
|
|
2555
2558
|
exports.registerCapiTools = registerCapiTools;
|
|
@@ -2561,6 +2564,7 @@ const CLOUDBASE_CONTROL_PLANE_DOC_URL = "https://cloud.tencent.com/document/prod
|
|
|
2561
2564
|
const CLOUDBASE_DEPENDENCY_API_DOC_URL = "https://cloud.tencent.com/document/product/876/34808";
|
|
2562
2565
|
const ALLOWED_SERVICES = [
|
|
2563
2566
|
"tcb",
|
|
2567
|
+
"tcbr",
|
|
2564
2568
|
"scf",
|
|
2565
2569
|
"sts",
|
|
2566
2570
|
"cam",
|
|
@@ -2568,6 +2572,31 @@ const ALLOWED_SERVICES = [
|
|
|
2568
2572
|
"cdn",
|
|
2569
2573
|
"vpc",
|
|
2570
2574
|
];
|
|
2575
|
+
/**
|
|
2576
|
+
* tcb 旧版小租户云托管(CloudBase Run)API 族,统一禁用。
|
|
2577
|
+
*
|
|
2578
|
+
* 云托管一律走 tcbr 新逻辑(CreateCloudRunEnv / CreateCloudRunServer)。tcb 的
|
|
2579
|
+
* CreateCloudBaseRunResource 是 2018 旧小租户开通接口:在无大租户记录的环境上调用
|
|
2580
|
+
* 会把服务创建到小租户,产生错误的服务与版本(2026-08-13 用户实测,ATO 调研
|
|
2581
|
+
* efec7cc5 结论)。同族 Describe/Delete 一并拦截,避免 AI 误以为与 tcbr 环境相关。
|
|
2582
|
+
*/
|
|
2583
|
+
const TCB_CLOUDRUN_FORBIDDEN_ACTIONS = [
|
|
2584
|
+
"CreateCloudBaseRunResource",
|
|
2585
|
+
"DescribeCloudBaseRunResource",
|
|
2586
|
+
"DeleteCloudBaseRunResource",
|
|
2587
|
+
];
|
|
2588
|
+
const TCB_CLOUDRUN_FORBIDDEN_HINT = `云托管(CloudBase Run)统一走 tcbr 新逻辑(CreateCloudRunEnv / CreateCloudRunServer),` +
|
|
2589
|
+
`不要使用 tcb 旧小租户接口(${TCB_CLOUDRUN_FORBIDDEN_ACTIONS.join(" / ")})。` +
|
|
2590
|
+
`新环境请先初始化云托管:callCloudApi(service="tcbr", version="2022-02-17", action="CreateCloudRunEnv", params={EnvId:"..."}),` +
|
|
2591
|
+
`再通过 manageCloudRun(action="deploy") 创建服务;查询单个环境基础信息/是否已开通云托管用 callCloudApi(service="tcbr", version="2022-02-17", action="DescribeEnvBaseInfo", params={EnvId:"..."}),查询环境列表/资源信息用 DescribeCloudRunEnvs。`;
|
|
2592
|
+
/**
|
|
2593
|
+
* 拒绝 tcb 旧版小租户云托管 API。命中黑名单时抛出带 tcbr 引导的错误。
|
|
2594
|
+
*/
|
|
2595
|
+
function assertTcbCloudRunActionAllowed(service, action) {
|
|
2596
|
+
if (service === "tcb" && TCB_CLOUDRUN_FORBIDDEN_ACTIONS.includes(action)) {
|
|
2597
|
+
throw new Error(`[${service}/${action}] 已禁用:${TCB_CLOUDRUN_FORBIDDEN_HINT}`);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2571
2600
|
function levenshteinDistance(left, right) {
|
|
2572
2601
|
const rows = left.length + 1;
|
|
2573
2602
|
const cols = right.length + 1;
|
|
@@ -2643,7 +2672,7 @@ function formatTcbParamsTypeHint(action) {
|
|
|
2643
2672
|
return `参数类型参考:\n\`\`\`ts\n${entry.paramsType}\n\`\`\``;
|
|
2644
2673
|
}
|
|
2645
2674
|
function buildCapiDocGuidance(service) {
|
|
2646
|
-
if (service === "tcb" || service === "lowcode" || service === "scf") {
|
|
2675
|
+
if (service === "tcb" || service === "tcbr" || service === "lowcode" || service === "scf") {
|
|
2647
2676
|
return `优先查阅 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。`;
|
|
2648
2677
|
}
|
|
2649
2678
|
return `请优先核对对应官方云 API 文档;若你的场景其实是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode="openapi"),不要继续猜测管控面 Action。`;
|
|
@@ -2733,15 +2762,21 @@ function registerCapiTools(server) {
|
|
|
2733
2762
|
**云函数**: \`DescribeFunctions\`、\`CreateFunction\`、\`UpdateFunctionCode\`、\`DeleteFunction\`
|
|
2734
2763
|
**数据库**: \`CreateMySQLInstance\`、\`DescribeMySQLInstances\`、\`DestroyMySQLInstance\`
|
|
2735
2764
|
|
|
2765
|
+
⚠️ 云托管(CloudBase Run)统一走 tcbr service(CreateCloudRunEnv / CreateCloudRunServer / DescribeEnvBaseInfo / DescribeCloudRunEnvs,version="2022-02-17"),tcb 旧小租户接口 CreateCloudBaseRunResource 等已被禁用;部署请用 manageCloudRun。查询单个环境基础信息/是否已开通云托管用 DescribeEnvBaseInfo(EnvId 必填),查询环境列表及资源信息用 DescribeCloudRunEnvs(EnvId 可选过滤)。
|
|
2766
|
+
|
|
2736
2767
|
销毁环境时,常见做法是至少带上 \`EnvId\` 和 \`BypassCheck: true\`,如果环境已经处于隔离期再按文档补 \`IsForce: true\`。`,
|
|
2737
2768
|
inputSchema: {
|
|
2738
2769
|
service: zod_1.z
|
|
2739
2770
|
.enum(ALLOWED_SERVICES)
|
|
2740
|
-
.describe("选择要访问的服务。可选:tcb、scf、sts、cam、lowcode、cdn、vpc。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action
|
|
2771
|
+
.describe("选择要访问的服务。可选:tcb、tcbr、scf、sts、cam、lowcode、cdn、vpc。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action。云托管统一走 tcbr(version 需传 2022-02-17)。"),
|
|
2741
2772
|
action: zod_1.z
|
|
2742
2773
|
.string()
|
|
2743
2774
|
.min(1)
|
|
2744
|
-
.describe("具体 Action 名称,需符合对应服务的官方 API 定义。若不确定正确 Action,请先查官方文档;不要用近义词或历史命名进行猜测。tcb 常用 Action:环境管理 CreateEnv/ModifyEnv/DescribeEnvs/DestroyEnv、用户管理 CreateUser/ModifyUser/DescribeUserList/DeleteUsers、认证配置 EditAuthConfig、云函数 DescribeFunctions/CreateFunction、数据库 CreateMySQLInstance 等。"),
|
|
2775
|
+
.describe("具体 Action 名称,需符合对应服务的官方 API 定义。若不确定正确 Action,请先查官方文档;不要用近义词或历史命名进行猜测。tcb 常用 Action:环境管理 CreateEnv/ModifyEnv/DescribeEnvs/DestroyEnv、用户管理 CreateUser/ModifyUser/DescribeUserList/DeleteUsers、认证配置 EditAuthConfig、云函数 DescribeFunctions/CreateFunction、数据库 CreateMySQLInstance 等。tcbr 常用 Action:CreateCloudRunEnv(初始化云托管)、DescribeEnvBaseInfo(查询单个环境基础信息,EnvId 必填)、DescribeCloudRunEnvs(查询环境列表/资源信息,EnvId 可选过滤)、CreateCloudRunServer/DescribeCloudRunServers。"),
|
|
2776
|
+
version: zod_1.z
|
|
2777
|
+
.string()
|
|
2778
|
+
.optional()
|
|
2779
|
+
.describe("API 版本(可选)。缺省时按 service 使用 SDK 内置默认版本;tcbr 必须传 \"2022-02-17\"(否则请求缺少 X-TC-Version 会失败)。示例:service=\"tcbr\", version=\"2022-02-17\", action=\"CreateCloudRunEnv\", params={EnvId:\"env-xxx\",PackageType:\"Standard\"}。"),
|
|
2745
2780
|
params: zod_1.z
|
|
2746
2781
|
.record(zod_1.z.any())
|
|
2747
2782
|
.optional()
|
|
@@ -2754,10 +2789,11 @@ function registerCapiTools(server) {
|
|
|
2754
2789
|
openWorldHint: true,
|
|
2755
2790
|
category: CATEGORY,
|
|
2756
2791
|
},
|
|
2757
|
-
}, async ({ service, action, params, }) => {
|
|
2792
|
+
}, async ({ service, action, params, version, }) => {
|
|
2758
2793
|
if (!ALLOWED_SERVICES.includes(service)) {
|
|
2759
2794
|
throw new Error(`Service ${service} is not allowed. Allowed services: ${ALLOWED_SERVICES.join(", ")}`);
|
|
2760
2795
|
}
|
|
2796
|
+
assertTcbCloudRunActionAllowed(service, action);
|
|
2761
2797
|
const cloudbase = await getManager();
|
|
2762
2798
|
if (['1', 'true'].includes(process.env.CLOUDBASE_EVALUATE_MODE ?? '')) {
|
|
2763
2799
|
if (service === 'lowcode') {
|
|
@@ -2786,7 +2822,7 @@ function registerCapiTools(server) {
|
|
|
2786
2822
|
let result;
|
|
2787
2823
|
try {
|
|
2788
2824
|
const cleanedParams = params ? removeEmptyStringParams(params) : {};
|
|
2789
|
-
result = await cloudbase.commonService(service).call({
|
|
2825
|
+
result = await cloudbase.commonService(service, version).call({
|
|
2790
2826
|
Action: action,
|
|
2791
2827
|
Param: cleanedParams,
|
|
2792
2828
|
});
|
|
@@ -2903,7 +2939,7 @@ ${envIdSection}
|
|
|
2903
2939
|
## 环境信息
|
|
2904
2940
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
2905
2941
|
- Node.js版本: ${process.version}
|
|
2906
|
-
- MCP 版本:${process.env.npm_package_version || ( true ? "2.
|
|
2942
|
+
- MCP 版本:${process.env.npm_package_version || ( true ? "2.27.0" : 0)}
|
|
2907
2943
|
- 系统架构: ${os_1.default.arch()}
|
|
2908
2944
|
- 时间: ${new Date().toISOString()}
|
|
2909
2945
|
- 请求ID: ${requestId}
|
|
@@ -2984,7 +3020,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
2984
3020
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
2985
3021
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
2986
3022
|
});
|
|
2987
|
-
const mcpVersion = true ? "2.
|
|
3023
|
+
const mcpVersion = true ? "2.27.0" : 0;
|
|
2988
3024
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
2989
3025
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
2990
3026
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -3139,7 +3175,6 @@ const mcp_js_1 = __webpack_require__(4619);
|
|
|
3139
3175
|
const databaseNoSQL_js_1 = __webpack_require__(3903);
|
|
3140
3176
|
const databasePG_js_1 = __webpack_require__(6473);
|
|
3141
3177
|
const databaseSQL_js_1 = __webpack_require__(9782);
|
|
3142
|
-
const download_js_1 = __webpack_require__(9097);
|
|
3143
3178
|
const env_js_1 = __webpack_require__(622);
|
|
3144
3179
|
const functions_js_1 = __webpack_require__(5936);
|
|
3145
3180
|
const hosting_js_1 = __webpack_require__(7279);
|
|
@@ -3160,7 +3195,7 @@ const logs_js_1 = __webpack_require__(2322);
|
|
|
3160
3195
|
const permissions_js_1 = __webpack_require__(6485);
|
|
3161
3196
|
const cloud_mode_js_1 = __webpack_require__(9684);
|
|
3162
3197
|
const logger_js_1 = __webpack_require__(3039);
|
|
3163
|
-
const
|
|
3198
|
+
const site_map_js_1 = __webpack_require__(8107);
|
|
3164
3199
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
3165
3200
|
const tool_wrapper_js_1 = __webpack_require__(1363);
|
|
3166
3201
|
// 默认插件列表
|
|
@@ -3182,13 +3217,15 @@ const DEFAULT_PLUGINS = [
|
|
|
3182
3217
|
"permissions",
|
|
3183
3218
|
"logs",
|
|
3184
3219
|
"agents",
|
|
3185
|
-
"download",
|
|
3186
3220
|
"capi",
|
|
3187
3221
|
];
|
|
3188
3222
|
function registerDatabase(server) {
|
|
3189
|
-
//
|
|
3190
|
-
const
|
|
3191
|
-
|
|
3223
|
+
// NoSQL 数据库按站点能力集合注册(国际站默认不支持 NoSQL)
|
|
3224
|
+
const { site } = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
3225
|
+
site: server.cloudBaseOptions?.site,
|
|
3226
|
+
region: server.cloudBaseOptions?.region,
|
|
3227
|
+
});
|
|
3228
|
+
if (site_map_js_1.SITE_REGION_MAP[site].capabilities.noSql) {
|
|
3192
3229
|
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
3193
3230
|
}
|
|
3194
3231
|
(0, dataModel_js_1.registerDataModelTools)(server);
|
|
@@ -3197,8 +3234,11 @@ function registerMysqlDatabase(server) {
|
|
|
3197
3234
|
(0, databaseSQL_js_1.registerSQLDatabaseTools)(server);
|
|
3198
3235
|
}
|
|
3199
3236
|
function registerNoSQLDatabase(server) {
|
|
3200
|
-
const
|
|
3201
|
-
|
|
3237
|
+
const { site } = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
3238
|
+
site: server.cloudBaseOptions?.site,
|
|
3239
|
+
region: server.cloudBaseOptions?.region,
|
|
3240
|
+
});
|
|
3241
|
+
if (site_map_js_1.SITE_REGION_MAP[site].capabilities.noSql) {
|
|
3202
3242
|
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
3203
3243
|
}
|
|
3204
3244
|
}
|
|
@@ -3217,7 +3257,6 @@ const AVAILABLE_PLUGINS = {
|
|
|
3217
3257
|
storage: { name: "storage", register: storage_js_1.registerStorageTools },
|
|
3218
3258
|
setup: { name: "setup", register: setup_js_1.registerSetupTools },
|
|
3219
3259
|
rag: { name: "rag", register: rag_js_1.registerRagTools },
|
|
3220
|
-
download: { name: "download", register: download_js_1.registerDownloadTools },
|
|
3221
3260
|
gateway: { name: "gateway", register: gateway_js_1.registerGatewayTools },
|
|
3222
3261
|
"app-auth": { name: "app-auth", register: app_auth_js_1.registerAppAuthTools },
|
|
3223
3262
|
permissions: { name: "permissions", register: permissions_js_1.registerPermissionTools },
|
|
@@ -3404,26 +3443,6 @@ Object.defineProperty(exports, "reportToolkitLifecycle", ({ enumerable: true, ge
|
|
|
3404
3443
|
Object.defineProperty(exports, "telemetryReporter", ({ enumerable: true, get: function () { return telemetry_js_1.telemetryReporter; } }));
|
|
3405
3444
|
|
|
3406
3445
|
|
|
3407
|
-
/***/ }),
|
|
3408
|
-
|
|
3409
|
-
/***/ 1454:
|
|
3410
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
3414
|
-
exports.isInternationalRegion = void 0;
|
|
3415
|
-
exports.isValidRegion = isValidRegion;
|
|
3416
|
-
const REGION = {
|
|
3417
|
-
SHANGHAI: 'ap-shanghai',
|
|
3418
|
-
SINGAPORE: 'ap-singapore',
|
|
3419
|
-
};
|
|
3420
|
-
const isInternationalRegion = (region) => region === REGION.SINGAPORE;
|
|
3421
|
-
exports.isInternationalRegion = isInternationalRegion;
|
|
3422
|
-
function isValidRegion(region) {
|
|
3423
|
-
return Object.values(REGION).includes(region);
|
|
3424
|
-
}
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
3446
|
/***/ }),
|
|
3428
3447
|
|
|
3429
3448
|
/***/ 1622:
|
|
@@ -4874,6 +4893,7 @@ const manager_node_1 = __importDefault(__webpack_require__(6665));
|
|
|
4874
4893
|
const auth_js_1 = __webpack_require__(7291);
|
|
4875
4894
|
const logger_js_1 = __webpack_require__(3039);
|
|
4876
4895
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
4896
|
+
const site_map_js_1 = __webpack_require__(8107);
|
|
4877
4897
|
// Timeout for envId auto-resolution flow.
|
|
4878
4898
|
// 10 minutes (600 seconds) - matches InteractiveServer timeout
|
|
4879
4899
|
const ENV_ID_TIMEOUT = 600000;
|
|
@@ -4898,7 +4918,7 @@ function createManagerFromLoginState(loginState, region) {
|
|
|
4898
4918
|
envId: loginState.envId,
|
|
4899
4919
|
token: loginState.token,
|
|
4900
4920
|
proxy: process.env.http_proxy,
|
|
4901
|
-
region: region ??
|
|
4921
|
+
region: region ?? (0, site_map_js_1.resolveSiteAndRegion)().region,
|
|
4902
4922
|
});
|
|
4903
4923
|
}
|
|
4904
4924
|
async function listAvailableEnvCandidates(options) {
|
|
@@ -4926,7 +4946,7 @@ async function listAvailableEnvCandidates(options) {
|
|
|
4926
4946
|
if (!loginState?.secretId || !loginState?.secretKey) {
|
|
4927
4947
|
return [];
|
|
4928
4948
|
}
|
|
4929
|
-
const region = cloudBaseOptions
|
|
4949
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions ?? {}).region;
|
|
4930
4950
|
cloudbase = createManagerFromLoginState(loginState, region);
|
|
4931
4951
|
}
|
|
4932
4952
|
try {
|
|
@@ -5127,7 +5147,10 @@ function getCachedEnvId() {
|
|
|
5127
5147
|
const databaseInstanceIdCache = new Map();
|
|
5128
5148
|
function buildDatabaseInstanceIdCacheKey(options) {
|
|
5129
5149
|
const envId = options?.envId ?? options?.cloudBaseOptions?.envId ?? process.env.CLOUDBASE_ENV_ID ?? 'unknown';
|
|
5130
|
-
const region =
|
|
5150
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
5151
|
+
site: options?.cloudBaseOptions?.site,
|
|
5152
|
+
region: options?.region ?? options?.cloudBaseOptions?.region,
|
|
5153
|
+
}).region;
|
|
5131
5154
|
return `${region}:${envId}`;
|
|
5132
5155
|
}
|
|
5133
5156
|
function resetDatabaseInstanceIdCache() {
|
|
@@ -5233,11 +5256,11 @@ async function getCloudBaseManager(options = {}) {
|
|
|
5233
5256
|
});
|
|
5234
5257
|
}
|
|
5235
5258
|
try {
|
|
5236
|
-
// Region priority: explicit option > env
|
|
5237
|
-
const fallbackRegion =
|
|
5259
|
+
// Region priority: explicit option > TCB_REGION/TCB_SITE env > .cloudbase/project.json > ap-shanghai default
|
|
5260
|
+
const { region: fallbackRegion, site } = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions ?? {});
|
|
5238
5261
|
const loginState = authStrategy === 'ensure'
|
|
5239
|
-
? await (0, auth_js_1.getLoginState)({ region: fallbackRegion })
|
|
5240
|
-
: await (0, auth_js_1.peekLoginState)();
|
|
5262
|
+
? await (0, auth_js_1.getLoginState)({ region: fallbackRegion, site })
|
|
5263
|
+
: await (0, auth_js_1.peekLoginState)({ region: fallbackRegion, site });
|
|
5241
5264
|
if (!loginState) {
|
|
5242
5265
|
const authState = await (0, auth_js_1.getAuthProgressState)();
|
|
5243
5266
|
if (authState.status === 'PENDING') {
|
|
@@ -5285,7 +5308,7 @@ async function getCloudBaseManager(options = {}) {
|
|
|
5285
5308
|
}
|
|
5286
5309
|
// envId priority: explicit option > envManager cache > loginState.envId
|
|
5287
5310
|
const resolvedEnvId = finalEnvId || loginEnvId;
|
|
5288
|
-
// region 直接使用 fallbackRegion
|
|
5311
|
+
// region 直接使用 fallbackRegion(resolveSiteAndRegion 统一解析)
|
|
5289
5312
|
// 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
|
|
5290
5313
|
const region = fallbackRegion;
|
|
5291
5314
|
const manager = new manager_node_1.default({
|
|
@@ -5317,8 +5340,8 @@ function createCloudBaseManagerWithOptions(cloudBaseOptions) {
|
|
|
5317
5340
|
hasSecretKey: !!cloudBaseOptions.secretKey,
|
|
5318
5341
|
hasToken: !!cloudBaseOptions.token,
|
|
5319
5342
|
});
|
|
5320
|
-
// Region priority: explicit option > env
|
|
5321
|
-
const region = cloudBaseOptions.region
|
|
5343
|
+
// Region priority: explicit option > TCB_REGION/TCB_SITE env > .cloudbase/project.json > ap-shanghai default
|
|
5344
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions).region;
|
|
5322
5345
|
const manager = new manager_node_1.default({
|
|
5323
5346
|
...cloudBaseOptions,
|
|
5324
5347
|
proxy: cloudBaseOptions.proxy || process.env.http_proxy,
|
|
@@ -9607,8 +9630,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9607
9630
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9608
9631
|
};
|
|
9609
9632
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
9610
|
-
exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
9633
|
+
exports.CLOUDRUN_PACKAGE_TYPES = exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
9634
|
+
exports.queryCloudRunEnvStatus = queryCloudRunEnvStatus;
|
|
9635
|
+
exports.describeCloudRunEnvStatus = describeCloudRunEnvStatus;
|
|
9636
|
+
exports.ensureCloudRunEnvInitialized = ensureCloudRunEnvInitialized;
|
|
9611
9637
|
exports.detectCloudRunDbNetworkRisk = detectCloudRunDbNetworkRisk;
|
|
9638
|
+
exports.extractCloudRunImageInfo = extractCloudRunImageInfo;
|
|
9612
9639
|
exports.registerCloudRunTools = registerCloudRunTools;
|
|
9613
9640
|
const child_process_1 = __webpack_require__(5814);
|
|
9614
9641
|
const fs_1 = __importDefault(__webpack_require__(4421));
|
|
@@ -9623,24 +9650,31 @@ const cloudrun_config_js_1 = __webpack_require__(8266);
|
|
|
9623
9650
|
exports.CLOUDRUN_SERVICE_TYPES = ['function', 'container'];
|
|
9624
9651
|
// CloudRun access types
|
|
9625
9652
|
exports.CLOUDRUN_ACCESS_TYPES = ['OA', 'PUBLIC', 'MINIAPP', 'VPC'];
|
|
9653
|
+
// CloudRun env package types (CreateCloudRunEnv.PackageType)
|
|
9654
|
+
exports.CLOUDRUN_PACKAGE_TYPES = ['Trial', 'Standard', 'Professional', 'Enterprise'];
|
|
9626
9655
|
// Input schema for queryCloudRun tool
|
|
9627
9656
|
const queryCloudRunInputSchema = {
|
|
9628
|
-
action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog
|
|
9657
|
+
action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog', 'envStatus']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog=获取指定服务最近一次或指定构建的部署日志,envStatus=查询当前环境云托管是否已开通及开通状态(Status=creating开通中/normal已开通),用于initEnv之后轮询进度或deploy之前确认环境是否就绪'),
|
|
9629
9658
|
// List operation parameters
|
|
9630
9659
|
pageSize: zod_1.z.number().min(1).max(100).optional().default(10).describe('分页大小,控制每页返回的服务数量。取值范围:1-100,默认值:10。建议根据网络性能和显示需求调整'),
|
|
9631
9660
|
pageNum: zod_1.z.number().min(1).optional().default(1).describe('页码,用于分页查询。从1开始,默认值:1。配合pageSize使用可实现分页浏览'),
|
|
9632
9661
|
serverName: zod_1.z.string().optional().describe('服务名称筛选条件,支持模糊匹配。例如:输入"test"可匹配"test-service"、"my-test-app"等服务名称。留空则查询所有服务'),
|
|
9633
9662
|
serverType: zod_1.z.enum(exports.CLOUDRUN_SERVICE_TYPES).optional().describe('服务类型筛选条件:function=函数型云托管(仅支持Node.js,有特殊的开发要求和限制,适合简单的API服务),container=容器型服务(推荐使用,支持任意语言和框架如Java/Go/Python/PHP/.NET等,适合大多数应用场景)'),
|
|
9663
|
+
envId: zod_1.z.string().optional().describe('环境 ID(action=envStatus 时使用;不传则使用当前配置的环境)。格式如 env-xxxxxx'),
|
|
9634
9664
|
// Detail and log operation parameters
|
|
9635
9665
|
detailServerName: zod_1.z.string().optional().describe('要查询详细信息或部署日志的服务名称。当action为detail或getDeployLog时建议提供,必须是已存在的服务名称。可通过list操作获取可用的服务名称列表'),
|
|
9636
9666
|
buildId: zod_1.z.number().optional().describe('构建ID,仅在action=getDeployLog时使用。不传时默认返回最近一次部署的构建日志'),
|
|
9637
9667
|
};
|
|
9638
9668
|
// Input schema for manageCloudRun tool
|
|
9639
9669
|
const ManageCloudRunInputSchema = {
|
|
9640
|
-
action: zod_1.z.enum(['init', 'download', 'run', 'deploy', 'delete', 'createAgent', 'updateConfig']).describe('云托管服务管理操作类型:init=从模板初始化新的云托管项目代码(在targetPath目录下创建以serverName命名的子目录,支持多种语言和框架模板),download=从云端下载现有服务的代码到本地进行开发,run=在本地运行函数型云托管服务(用于开发和调试,仅支持函数型服务),deploy
|
|
9641
|
-
serverName: zod_1.z.string().describe('云托管服务名称,用于标识和管理服务。命名规则:支持大小写字母、数字、连字符和下划线,必须以字母开头,长度3-45个字符。在init操作中会作为在targetPath
|
|
9670
|
+
action: zod_1.z.enum(['init', 'download', 'run', 'deploy', 'delete', 'createAgent', 'updateConfig', 'initEnv']).describe('云托管服务管理操作类型:init=从模板初始化新的云托管项目代码(在targetPath目录下创建以serverName命名的子目录,支持多种语言和框架模板),download=从云端下载现有服务的代码到本地进行开发,run=在本地运行函数型云托管服务(用于开发和调试,仅支持函数型服务),deploy=将本地代码部署到云端云托管服务(支持函数型和容器型;传 imageUrl 时改为已有镜像部署,走 DeployType=image 容器型,targetPath 可省略;已存在服务会 Read-Merge-Write 保留远程 VpcConf/EnvParams/OpenAccessTypes),updateConfig=仅更新服务配置不重新上传代码(对齐控制台服务设置,走 SubmitServerConfigChangeDiff;不需要 targetPath),delete=删除指定的云托管服务(不可恢复,需要确认),createAgent=创建函数型Agent(基于函数型云托管开发AI智能体),initEnv=开通当前环境的云托管(异步创建云托管环境,幂等:已开通直接返回;适合新环境首次部署前使用)'),
|
|
9671
|
+
serverName: zod_1.z.string().describe('云托管服务名称,用于标识和管理服务。命名规则:支持大小写字母、数字、连字符和下划线,必须以字母开头,长度3-45个字符。在init操作中会作为在targetPath下创建的子目录名,在其他操作中作为目标服务名。initEnv 操作不需要此参数'),
|
|
9672
|
+
// InitEnv operation parameters
|
|
9673
|
+
envId: zod_1.z.string().optional().describe('环境 ID(action=initEnv 时使用;不传则使用当前配置的环境)。格式如 env-xxxxxx'),
|
|
9674
|
+
packageType: zod_1.z.enum(exports.CLOUDRUN_PACKAGE_TYPES).optional().default('Trial').describe('云托管环境套餐类型(action=initEnv 时使用):Trial=试用,Standard=标准,Professional=专业,Enterprise=企业。默认 Trial'),
|
|
9642
9675
|
// Deploy operation parameters
|
|
9643
|
-
targetPath: zod_1.z.string().optional().describe('本地代码路径,必须是绝对路径。在deploy操作中指定要部署的代码目录,在download操作中指定下载目标目录,在init操作中指定云托管服务的上级目录(会在该目录下创建以serverName命名的子目录)。updateConfig 不需要此参数。建议约定:项目根目录下的cloudrun/目录,例如:/Users/username/projects/my-project/cloudrun'),
|
|
9676
|
+
targetPath: zod_1.z.string().optional().describe('本地代码路径,必须是绝对路径。在deploy操作中指定要部署的代码目录,在download操作中指定下载目标目录,在init操作中指定云托管服务的上级目录(会在该目录下创建以serverName命名的子目录)。updateConfig 不需要此参数。建议约定:项目根目录下的cloudrun/目录,例如:/Users/username/projects/my-project/cloudrun。使用 imageUrl 部署已有镜像时此参数可省略'),
|
|
9677
|
+
imageUrl: zod_1.z.string().optional().describe('已有镜像部署(action=deploy 时使用):直接指定容器镜像地址,如 ccr.ccs.tencentyun.com/ns/img:v1 或公网 registry 地址。传入后走 DeployType="image"(容器型)部署,无需本地源码目录(targetPath 可省略)。支持:1) 公网匿名可拉取的镜像直填地址;2) 私有/需登录的镜像(如 ghcr.io)需先在本地 docker pull → docker tag/push 到腾讯云 CCR → 填入 CCR 地址。不传则维持源码构建(本地代码打包上传)。注意:无论哪种部署方式,环境都需先开通云托管(未开通时先调用 initEnv,Status=normal 后再部署)'),
|
|
9644
9678
|
envParamsReplaceAll: zod_1.z.boolean().optional().default(false).describe('EnvParams 合并策略(deploy / updateConfig):false(默认)= 与远程按 key 合并(输入覆盖同名 key,远程其余 key 保留);true= 用输入 EnvParams 整包替换远程。仅当显式传入 EnvParams 时生效'),
|
|
9645
9679
|
serverConfig: zod_1.z.object({
|
|
9646
9680
|
OpenAccessTypes: zod_1.z.array(zod_1.z.enum(exports.CLOUDRUN_ACCESS_TYPES)).optional().describe('公网访问类型配置,控制服务的访问权限:OA=办公网访问,PUBLIC=公网访问(默认,可通过HTTPS域名访问),MINIAPP=小程序访问,VPC=VPC访问(仅同VPC内可访问)。可配置多个类型'),
|
|
@@ -9786,6 +9820,88 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
|
|
|
9786
9820
|
}
|
|
9787
9821
|
const CLOUDRUN_DB_ENV_KEY_PATTERN = /^(DATABASE_URL|DB_HOST|DB_PORT|DB_USER|DB_PASSWORD|DB_NAME|MYSQL_|POSTGRES_|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PG_|REDIS_|MONGO_|MONGODB_|SQLALCHEMY_DATABASE_URI|SPRING_DATASOURCE_)/i;
|
|
9788
9822
|
const CLOUDRUN_DB_URL_PATTERN = /(mysql|mariadb|postgres|postgresql|mongodb(\+srv)?|redis|rediss):\/\//i;
|
|
9823
|
+
async function queryCloudRunEnvStatus(options) {
|
|
9824
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions: options.cloudBaseOptions });
|
|
9825
|
+
if (!manager?.commonService) {
|
|
9826
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot query CloudRun env status.");
|
|
9827
|
+
}
|
|
9828
|
+
return describeCloudRunEnvStatus(manager, options.envId);
|
|
9829
|
+
}
|
|
9830
|
+
/**
|
|
9831
|
+
* 核心实现:用已获取的 manager 查询云托管开通状态(供 initEnv/envStatus 及
|
|
9832
|
+
* ensureCloudRunEnvInitialized 复用,避免重复 getCloudBaseManager)。
|
|
9833
|
+
*/
|
|
9834
|
+
async function describeCloudRunEnvStatus(manager, envId) {
|
|
9835
|
+
const result = await manager
|
|
9836
|
+
.commonService("tcbr", "2022-02-17")
|
|
9837
|
+
.call({
|
|
9838
|
+
Action: "DescribeEnvBaseInfo",
|
|
9839
|
+
Param: { EnvId: envId },
|
|
9840
|
+
});
|
|
9841
|
+
const data = (result ?? {});
|
|
9842
|
+
const baseInfo = (data.EnvBaseInfo ?? {}) ?? {};
|
|
9843
|
+
if (data.IsExist !== true) {
|
|
9844
|
+
return { isExist: false, status: "unopened", baseInfo };
|
|
9845
|
+
}
|
|
9846
|
+
const rawStatus = typeof baseInfo.Status === "string" ? baseInfo.Status : "";
|
|
9847
|
+
const status = rawStatus === "creating" || rawStatus === "normal"
|
|
9848
|
+
? rawStatus
|
|
9849
|
+
: "unknown";
|
|
9850
|
+
return { isExist: true, status, baseInfo };
|
|
9851
|
+
}
|
|
9852
|
+
/**
|
|
9853
|
+
* 探测当前环境云托管(大租户)是否已初始化。
|
|
9854
|
+
*
|
|
9855
|
+
* 背景(2026-08-13 用户实测):新环境未调 CreateCloudRunEnv 初始化云托管时,
|
|
9856
|
+
* 直接 CreateCloudRunServer 会因"无大租户记录"被默认转入小租户,创建出小租户的
|
|
9857
|
+
* 服务和版本——这是错误路径。本函数在 deploy 创建新服务前用 tcbr DescribeEnvBaseInfo
|
|
9858
|
+
* 探测环境是否已开通云托管;未开通则抛错引导先初始化,而不是默默走到小租户路径。
|
|
9859
|
+
*
|
|
9860
|
+
* 实测(2026-08-13 真实凭据):tcbr 不存在 DescribeCloudRunEnv(单数)Action,调用
|
|
9861
|
+
* 恒返回 InvalidAction;正确的探测接口是 DescribeEnvBaseInfo。未开通云托管的环境返回
|
|
9862
|
+
* IsExist=false 且 EnvBaseInfo 为空结构(不抛错);已开通的环境(实测 ai-share-
|
|
9863
|
+
* d2guukyxybb63b206)返回 IsExist=true 且 EnvBaseInfo 含完整字段(Status="normal"、
|
|
9864
|
+
* PackageType/Region/EnvType/CreateTime 等已填充),两分支可明确区分。
|
|
9865
|
+
*
|
|
9866
|
+
* 双验证:DescribeCloudRunServers 未初始化与已初始化但无服务均返回 ServerList=[],
|
|
9867
|
+
* 无法区分,故不作为初始化判定依据。
|
|
9868
|
+
*
|
|
9869
|
+
* @returns 已初始化返回 true;探测到未初始化抛出带引导信息的 Error。
|
|
9870
|
+
*/
|
|
9871
|
+
async function ensureCloudRunEnvInitialized(options) {
|
|
9872
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions: options.cloudBaseOptions });
|
|
9873
|
+
if (!manager?.commonService) {
|
|
9874
|
+
// 老 SDK 无 commonService 时退化为不拦截(保持既有行为,避免误伤)。
|
|
9875
|
+
return true;
|
|
9876
|
+
}
|
|
9877
|
+
try {
|
|
9878
|
+
const status = await describeCloudRunEnvStatus(manager, options.envId);
|
|
9879
|
+
if (!status.isExist) {
|
|
9880
|
+
throwCloudRunEnvNotInitialized(options.envId);
|
|
9881
|
+
}
|
|
9882
|
+
return true;
|
|
9883
|
+
}
|
|
9884
|
+
catch (error) {
|
|
9885
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9886
|
+
// 错误兜底:未初始化/未开通类错误码。
|
|
9887
|
+
// 注意:裸 InvalidParameter 可能是普通参数错误,不拦截;仅当其带 Env/CloudRun
|
|
9888
|
+
// 上下文(疑似 "EnvironmentId not found / CloudRun Env 未开通")时才按未初始化处理。
|
|
9889
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(msg) ||
|
|
9890
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(msg)) {
|
|
9891
|
+
throwCloudRunEnvNotInitialized(options.envId);
|
|
9892
|
+
}
|
|
9893
|
+
// 其他错误(网络/权限等)不拦截,让上层按原逻辑处理。
|
|
9894
|
+
return true;
|
|
9895
|
+
}
|
|
9896
|
+
}
|
|
9897
|
+
function throwCloudRunEnvNotInitialized(envId) {
|
|
9898
|
+
throw new Error(`当前环境(${envId})尚未初始化云托管(CloudRun Env)。` +
|
|
9899
|
+
`不能直接创建服务(CreateCloudRunServer 在无大租户记录时会默认创建到小租户,产生错误的小租户服务与版本)。\n` +
|
|
9900
|
+
`请先开通云托管环境,再重试部署:\n` +
|
|
9901
|
+
`- MCP:manageCloudRun(action="initEnv", envId="${envId}")(异步开通,幂等;开通完成后可用 queryCloudRun(action="envStatus", envId="${envId}") 查询 Status=normal)\n` +
|
|
9902
|
+
`- 或控制台:环境 → 云托管 → 开通(https://tcb.cloud.tencent.com/dev?envId=${envId}#/platform-run)\n` +
|
|
9903
|
+
`初始化完成后重新调用 manageCloudRun(action="deploy")。`);
|
|
9904
|
+
}
|
|
9789
9905
|
/**
|
|
9790
9906
|
* Detect likely TCP database/cache env usage without VpcConf.
|
|
9791
9907
|
* Soft signal for AI agents — does not block deploy.
|
|
@@ -9882,6 +9998,36 @@ function normalizeCloudRunDomainUrl(input) {
|
|
|
9882
9998
|
? raw
|
|
9883
9999
|
: `https://${raw}`;
|
|
9884
10000
|
}
|
|
10001
|
+
/**
|
|
10002
|
+
* 从服务详情 / 最新部署记录中提取镜像信息(镜像部署时才有)。
|
|
10003
|
+
* 镜像部署(DeployType=image)的服务详情与部署记录通常会携带 ImageUrl 等字段;
|
|
10004
|
+
* 源码构建的服务无此字段,返回 undefined 表示无镜像信息。
|
|
10005
|
+
*/
|
|
10006
|
+
function extractCloudRunImageInfo(serviceDetail, latestDeploy) {
|
|
10007
|
+
const candidates = [
|
|
10008
|
+
latestDeploy,
|
|
10009
|
+
serviceDetail?.ServerConfig,
|
|
10010
|
+
serviceDetail?.BaseInfo,
|
|
10011
|
+
serviceDetail,
|
|
10012
|
+
];
|
|
10013
|
+
for (const source of candidates) {
|
|
10014
|
+
if (!source || typeof source !== "object")
|
|
10015
|
+
continue;
|
|
10016
|
+
const imageUrl = source.ImageUrl ??
|
|
10017
|
+
source.imageUrl ??
|
|
10018
|
+
(typeof source.ImageInfo === "string" ? source.ImageInfo : undefined) ??
|
|
10019
|
+
(typeof source.ImageInfo?.ImageUrl === "string" ? source.ImageInfo.ImageUrl : undefined);
|
|
10020
|
+
if (typeof imageUrl === "string" && imageUrl.trim()) {
|
|
10021
|
+
const deployType = typeof source.DeployType === "string"
|
|
10022
|
+
? source.DeployType
|
|
10023
|
+
: typeof source.deployType === "string"
|
|
10024
|
+
? source.deployType
|
|
10025
|
+
: undefined;
|
|
10026
|
+
return { imageUrl: imageUrl.trim(), ...(deployType ? { deployType } : {}) };
|
|
10027
|
+
}
|
|
10028
|
+
}
|
|
10029
|
+
return undefined;
|
|
10030
|
+
}
|
|
9885
10031
|
function resolveCloudRunFallbackAccess(details) {
|
|
9886
10032
|
const custom = normalizeCloudRunDomainUrl(details?.BaseInfo?.CustomDomainName);
|
|
9887
10033
|
if (custom)
|
|
@@ -9912,7 +10058,7 @@ function registerCloudRunTools(server) {
|
|
|
9912
10058
|
// Tool 1: Get CloudRun service information (read operations)
|
|
9913
10059
|
server.registerTool("queryCloudRun", {
|
|
9914
10060
|
title: "查询 CloudRun 服务信息",
|
|
9915
|
-
description: "
|
|
10061
|
+
description: "查询云托管服务信息,支持获取服务列表、查询服务详情、获取可用模板列表、获取部署日志以及查询环境云托管开通状态(envStatus)。返回的服务信息包括服务名称、状态、访问类型、配置详情以及最近部署上下文。",
|
|
9916
10062
|
inputSchema: queryCloudRunInputSchema,
|
|
9917
10063
|
annotations: {
|
|
9918
10064
|
readOnlyHint: true,
|
|
@@ -10024,6 +10170,10 @@ function registerCloudRunTools(server) {
|
|
|
10024
10170
|
data: {
|
|
10025
10171
|
service: result,
|
|
10026
10172
|
latestDeploy,
|
|
10173
|
+
// 若最新部署记录带镜像信息(镜像部署),透出便于展示
|
|
10174
|
+
...(extractCloudRunImageInfo(result, latestDeploy)
|
|
10175
|
+
? { imageInfo: extractCloudRunImageInfo(result, latestDeploy) }
|
|
10176
|
+
: {}),
|
|
10027
10177
|
...(deployRecordsWarning ? { deployRecordsWarning } : {})
|
|
10028
10178
|
},
|
|
10029
10179
|
message
|
|
@@ -10122,6 +10272,65 @@ function registerCloudRunTools(server) {
|
|
|
10122
10272
|
]
|
|
10123
10273
|
};
|
|
10124
10274
|
}
|
|
10275
|
+
case 'envStatus': {
|
|
10276
|
+
const envId = input.envId?.trim() || (await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions));
|
|
10277
|
+
if (!manager.commonService) {
|
|
10278
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot query CloudRun env status.");
|
|
10279
|
+
}
|
|
10280
|
+
let status;
|
|
10281
|
+
try {
|
|
10282
|
+
status = await describeCloudRunEnvStatus(manager, envId);
|
|
10283
|
+
}
|
|
10284
|
+
catch (error) {
|
|
10285
|
+
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
10286
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
|
|
10287
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
10288
|
+
status = { isExist: false, status: "unopened", baseInfo: {} };
|
|
10289
|
+
}
|
|
10290
|
+
else {
|
|
10291
|
+
throw new Error(`[queryCloudRun/envStatus] ${baseMessage}`);
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
let message;
|
|
10295
|
+
if (!status.isExist) {
|
|
10296
|
+
message = `环境 ${envId} 尚未开通云托管。请先调用 manageCloudRun(action="initEnv", envId="${envId}") 开通(异步、幂等),或前往控制台 环境 → 云托管 → 开通;Status=normal 后即可 deploy。`;
|
|
10297
|
+
}
|
|
10298
|
+
else if (status.status === "creating") {
|
|
10299
|
+
message = `环境 ${envId} 云托管正在开通中(Status=creating)。请稍后重试 manageCloudRun(action="deploy"),或用本 action 再次查询直到 Status=normal。`;
|
|
10300
|
+
}
|
|
10301
|
+
else if (status.status === "normal") {
|
|
10302
|
+
message = `环境 ${envId} 云托管已开通(Status=normal),可直接 manageCloudRun(action="deploy")。`;
|
|
10303
|
+
}
|
|
10304
|
+
else {
|
|
10305
|
+
message = `环境 ${envId} 云托管状态未知(Status=${status.status ?? "unknown"}),请稍后重试。`;
|
|
10306
|
+
}
|
|
10307
|
+
return {
|
|
10308
|
+
content: [
|
|
10309
|
+
{
|
|
10310
|
+
type: "text",
|
|
10311
|
+
text: JSON.stringify({
|
|
10312
|
+
success: true,
|
|
10313
|
+
data: {
|
|
10314
|
+
envId,
|
|
10315
|
+
status: status.status,
|
|
10316
|
+
isExist: status.isExist,
|
|
10317
|
+
...(status.isExist
|
|
10318
|
+
? {
|
|
10319
|
+
envBaseInfo: {
|
|
10320
|
+
Status: status.baseInfo.Status ?? null,
|
|
10321
|
+
PackageType: status.baseInfo.PackageType ?? null,
|
|
10322
|
+
Region: status.baseInfo.Region ?? null,
|
|
10323
|
+
EnvType: status.baseInfo.EnvType ?? null,
|
|
10324
|
+
},
|
|
10325
|
+
}
|
|
10326
|
+
: {}),
|
|
10327
|
+
},
|
|
10328
|
+
message
|
|
10329
|
+
}, null, 2)
|
|
10330
|
+
}
|
|
10331
|
+
]
|
|
10332
|
+
};
|
|
10333
|
+
}
|
|
10125
10334
|
default:
|
|
10126
10335
|
throw new Error(`Unsupported action: ${input.action}`);
|
|
10127
10336
|
}
|
|
@@ -10131,7 +10340,7 @@ function registerCloudRunTools(server) {
|
|
|
10131
10340
|
// Tool 2: Manage CloudRun services (write operations)
|
|
10132
10341
|
server.registerTool("manageCloudRun", {
|
|
10133
10342
|
title: "管理 CloudRun 服务",
|
|
10134
|
-
description: "
|
|
10343
|
+
description: "管理云托管服务,按开发顺序支持:开通云托管环境(initEnv)、初始化项目(可从模板开始,模板列表可通过 queryCloudRun 查询)、下载服务代码、本地运行(仅函数型服务)、部署代码、仅更新配置(updateConfig,无需重新上传代码)、删除服务。deploy 支持两种方式:1) 源码构建(传入 targetPath,本地代码打包上传,默认路径);2) 已有镜像部署(传入 imageUrl,如 ccr.ccs.tencentyun.com/ns/img:v1,走 DeployType=image 容器型部署,targetPath 可省略)。deploy 对已存在服务会先读取远程配置再合并(保留 VpcConf/EnvParams/OpenAccessTypes)。updateConfig 对齐控制台服务设置页。删除操作需要确认,建议设置force=true。新环境首次部署前若提示未开通云托管,先调用 initEnv 开通(异步、幂等)。",
|
|
10135
10344
|
inputSchema: ManageCloudRunInputSchema,
|
|
10136
10345
|
annotations: {
|
|
10137
10346
|
readOnlyHint: false,
|
|
@@ -10152,6 +10361,99 @@ function registerCloudRunTools(server) {
|
|
|
10152
10361
|
targetPath = validateAndNormalizePath(input.targetPath);
|
|
10153
10362
|
}
|
|
10154
10363
|
switch (input.action) {
|
|
10364
|
+
case 'initEnv': {
|
|
10365
|
+
const envId = input.envId?.trim() || (await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions));
|
|
10366
|
+
const packageType = input.packageType || 'Trial';
|
|
10367
|
+
if (!manager.commonService) {
|
|
10368
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot initialize CloudRun env.");
|
|
10369
|
+
}
|
|
10370
|
+
// 幂等:先查当前开通状态,已开通 / 开通中不重复创建。
|
|
10371
|
+
let current;
|
|
10372
|
+
try {
|
|
10373
|
+
current = await describeCloudRunEnvStatus(manager, envId);
|
|
10374
|
+
}
|
|
10375
|
+
catch (error) {
|
|
10376
|
+
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
10377
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
|
|
10378
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
10379
|
+
current = { isExist: false, status: "unopened", baseInfo: {} };
|
|
10380
|
+
}
|
|
10381
|
+
else {
|
|
10382
|
+
throw new Error(`[manageCloudRun/initEnv] ${baseMessage}`);
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
if (current.isExist && current.status === "normal") {
|
|
10386
|
+
return {
|
|
10387
|
+
content: [
|
|
10388
|
+
{
|
|
10389
|
+
type: "text",
|
|
10390
|
+
text: JSON.stringify({
|
|
10391
|
+
success: true,
|
|
10392
|
+
data: {
|
|
10393
|
+
envId,
|
|
10394
|
+
status: "normal",
|
|
10395
|
+
packageType: current.baseInfo.PackageType ?? packageType,
|
|
10396
|
+
created: false
|
|
10397
|
+
},
|
|
10398
|
+
message: `环境 ${envId} 已开通云托管(Status=normal),无需重复开通。可直接 manageCloudRun(action="deploy")。`
|
|
10399
|
+
}, null, 2)
|
|
10400
|
+
}
|
|
10401
|
+
]
|
|
10402
|
+
};
|
|
10403
|
+
}
|
|
10404
|
+
if (current.isExist && current.status === "creating") {
|
|
10405
|
+
return {
|
|
10406
|
+
content: [
|
|
10407
|
+
{
|
|
10408
|
+
type: "text",
|
|
10409
|
+
text: JSON.stringify({
|
|
10410
|
+
success: true,
|
|
10411
|
+
data: {
|
|
10412
|
+
envId,
|
|
10413
|
+
status: "creating",
|
|
10414
|
+
created: false
|
|
10415
|
+
},
|
|
10416
|
+
message: `环境 ${envId} 云托管正在开通中(Status=creating),无需重复开通。请稍后用 queryCloudRun(action="envStatus", envId="${envId}") 查询,Status=normal 后即可 deploy。`
|
|
10417
|
+
}, null, 2)
|
|
10418
|
+
}
|
|
10419
|
+
]
|
|
10420
|
+
};
|
|
10421
|
+
}
|
|
10422
|
+
// 未开通 → 发起异步开通(CreateCloudRunEnv 异步,不阻塞等待)。
|
|
10423
|
+
let createResult;
|
|
10424
|
+
try {
|
|
10425
|
+
createResult = await manager
|
|
10426
|
+
.commonService("tcbr", "2022-02-17")
|
|
10427
|
+
.call({
|
|
10428
|
+
Action: "CreateCloudRunEnv",
|
|
10429
|
+
Param: { EnvId: envId, PackageType: packageType },
|
|
10430
|
+
});
|
|
10431
|
+
}
|
|
10432
|
+
catch (error) {
|
|
10433
|
+
throw new Error(buildManageCloudRunErrorMessage('initEnv', envId, error));
|
|
10434
|
+
}
|
|
10435
|
+
const tranId = createResult?.TranId ??
|
|
10436
|
+
createResult?.Response?.TranId ??
|
|
10437
|
+
createResult?.tranId;
|
|
10438
|
+
return {
|
|
10439
|
+
content: [
|
|
10440
|
+
{
|
|
10441
|
+
type: "text",
|
|
10442
|
+
text: JSON.stringify({
|
|
10443
|
+
success: true,
|
|
10444
|
+
data: {
|
|
10445
|
+
envId,
|
|
10446
|
+
status: "creating",
|
|
10447
|
+
packageType,
|
|
10448
|
+
created: true,
|
|
10449
|
+
...(tranId ? { tranId } : {})
|
|
10450
|
+
},
|
|
10451
|
+
message: `已发起云托管开通(异步,Status=creating)。请稍后用 queryCloudRun(action="envStatus", envId="${envId}") 查询状态,Status=normal 后即可 manageCloudRun(action="deploy")。`
|
|
10452
|
+
}, null, 2)
|
|
10453
|
+
}
|
|
10454
|
+
]
|
|
10455
|
+
};
|
|
10456
|
+
}
|
|
10155
10457
|
case 'createAgent': {
|
|
10156
10458
|
if (!targetPath) {
|
|
10157
10459
|
throw new Error("targetPath is required for createAgent operation");
|
|
@@ -10317,14 +10619,18 @@ for await (let x of res.textStream) {
|
|
|
10317
10619
|
};
|
|
10318
10620
|
}
|
|
10319
10621
|
case 'deploy': {
|
|
10320
|
-
if (!targetPath) {
|
|
10321
|
-
throw new Error("targetPath is required for deploy operation");
|
|
10622
|
+
if (!targetPath && !input.imageUrl) {
|
|
10623
|
+
throw new Error("targetPath (source build) or imageUrl (existing image) is required for deploy operation");
|
|
10322
10624
|
}
|
|
10323
10625
|
// Determine service type - use input.serverType if provided, otherwise auto-detect
|
|
10324
10626
|
let serverType;
|
|
10325
10627
|
let remoteServerConfig = null;
|
|
10326
10628
|
let existingService = false;
|
|
10327
|
-
if (input.
|
|
10629
|
+
if (input.imageUrl) {
|
|
10630
|
+
// Image deploy is always container type (SDK: DeployInfo={DeployType:"image", ImageUrl}).
|
|
10631
|
+
serverType = 'container';
|
|
10632
|
+
}
|
|
10633
|
+
else if (input.serverType) {
|
|
10328
10634
|
serverType = input.serverType;
|
|
10329
10635
|
}
|
|
10330
10636
|
else {
|
|
@@ -10378,6 +10684,17 @@ for await (let x of res.textStream) {
|
|
|
10378
10684
|
// New service create path
|
|
10379
10685
|
}
|
|
10380
10686
|
}
|
|
10687
|
+
// 新环境必须先初始化云托管(大租户),否则 CreateCloudRunServer 会默认
|
|
10688
|
+
// 创建小租户服务。仅创建新服务(非 existing)前探测一次。
|
|
10689
|
+
// (2026-08-13 用户实测:luapi-v2 因未初始化云托管被创建到小租户)
|
|
10690
|
+
if (!existingService) {
|
|
10691
|
+
const currentEnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
10692
|
+
await ensureCloudRunEnvInitialized({
|
|
10693
|
+
cloudBaseOptions,
|
|
10694
|
+
envId: currentEnvId,
|
|
10695
|
+
serverName: input.serverName,
|
|
10696
|
+
});
|
|
10697
|
+
}
|
|
10381
10698
|
let mergedFromRemote = [];
|
|
10382
10699
|
let effectiveServerConfig = input.serverConfig;
|
|
10383
10700
|
if (existingService) {
|
|
@@ -10395,6 +10712,9 @@ for await (let x of res.textStream) {
|
|
|
10395
10712
|
force: input.force,
|
|
10396
10713
|
serverType: serverType,
|
|
10397
10714
|
};
|
|
10715
|
+
if (input.imageUrl) {
|
|
10716
|
+
deployParams.imageUrl = input.imageUrl;
|
|
10717
|
+
}
|
|
10398
10718
|
if (effectiveServerConfig && Object.keys(effectiveServerConfig).length > 0) {
|
|
10399
10719
|
deployParams.serverConfig = effectiveServerConfig;
|
|
10400
10720
|
}
|
|
@@ -10415,22 +10735,26 @@ for await (let x of res.textStream) {
|
|
|
10415
10735
|
catch (error) {
|
|
10416
10736
|
throw new Error(buildManageCloudRunErrorMessage('deploy', input.serverName, error));
|
|
10417
10737
|
}
|
|
10418
|
-
// Generate cloudbaserc.json configuration file
|
|
10738
|
+
// Generate cloudbaserc.json configuration file (source-build only; image deploy has no local project dir)
|
|
10419
10739
|
const currentEnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10740
|
+
let cloudbasercGenerated = false;
|
|
10741
|
+
if (targetPath) {
|
|
10742
|
+
const cloudbasercPath = path_1.default.join(targetPath, 'cloudbaserc.json');
|
|
10743
|
+
const cloudbasercContent = {
|
|
10744
|
+
envId: currentEnvId,
|
|
10745
|
+
cloudrun: {
|
|
10746
|
+
name: input.serverName
|
|
10747
|
+
}
|
|
10748
|
+
};
|
|
10749
|
+
try {
|
|
10750
|
+
fs_1.default.writeFileSync(cloudbasercPath, JSON.stringify(cloudbasercContent, null, 2));
|
|
10751
|
+
cloudbasercGenerated = true;
|
|
10752
|
+
}
|
|
10753
|
+
catch (error) {
|
|
10754
|
+
(0, logger_js_1.debug)('cloudbaserc.json creation skipped:', error instanceof Error ? error : new Error(String(error)));
|
|
10425
10755
|
}
|
|
10426
|
-
};
|
|
10427
|
-
const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
|
|
10428
|
-
try {
|
|
10429
|
-
fs_1.default.writeFileSync(cloudbasercPath, JSON.stringify(cloudbasercContent, null, 2));
|
|
10430
|
-
}
|
|
10431
|
-
catch (error) {
|
|
10432
|
-
(0, logger_js_1.debug)('cloudbaserc.json creation skipped:', error instanceof Error ? error : new Error(String(error)));
|
|
10433
10756
|
}
|
|
10757
|
+
const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
|
|
10434
10758
|
let preferredAccessUrl;
|
|
10435
10759
|
let preferredAccessUrls = [];
|
|
10436
10760
|
let preferredAccessSource;
|
|
@@ -10467,7 +10791,7 @@ for await (let x of res.textStream) {
|
|
|
10467
10791
|
}
|
|
10468
10792
|
// Send deployment notification to CodeBuddy IDE
|
|
10469
10793
|
try {
|
|
10470
|
-
const projectName = path_1.default.basename(targetPath);
|
|
10794
|
+
const projectName = targetPath ? path_1.default.basename(targetPath) : input.serverName;
|
|
10471
10795
|
await (0, notification_js_1.sendDeployNotification)(server, {
|
|
10472
10796
|
deployType: 'cloudrun',
|
|
10473
10797
|
url: preferredAccessUrl ?? "",
|
|
@@ -10497,9 +10821,11 @@ for await (let x of res.textStream) {
|
|
|
10497
10821
|
data: {
|
|
10498
10822
|
serviceName: input.serverName,
|
|
10499
10823
|
status: 'deploying',
|
|
10500
|
-
|
|
10824
|
+
deployType: input.imageUrl ? 'image' : 'source',
|
|
10825
|
+
...(targetPath ? { deployPath: targetPath } : {}),
|
|
10826
|
+
...(input.imageUrl ? { imageUrl: input.imageUrl } : {}),
|
|
10501
10827
|
serverType: serverType,
|
|
10502
|
-
cloudbasercGenerated
|
|
10828
|
+
cloudbasercGenerated,
|
|
10503
10829
|
consoleUrl,
|
|
10504
10830
|
...(existingService
|
|
10505
10831
|
? {
|
|
@@ -10522,7 +10848,7 @@ for await (let x of res.textStream) {
|
|
|
10522
10848
|
: {}),
|
|
10523
10849
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
10524
10850
|
},
|
|
10525
|
-
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
10851
|
+
message: `Triggered deployment for ${serverType} service '${input.serverName}' ${input.imageUrl ? `from image ${input.imageUrl}` : `from ${targetPath}`}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
10526
10852
|
}, null, 2)
|
|
10527
10853
|
}
|
|
10528
10854
|
]
|
|
@@ -11578,6 +11904,13 @@ function registerAppAuthTools(server) {
|
|
|
11578
11904
|
}
|
|
11579
11905
|
|
|
11580
11906
|
|
|
11907
|
+
/***/ }),
|
|
11908
|
+
|
|
11909
|
+
/***/ 5663:
|
|
11910
|
+
/***/ ((module) => {
|
|
11911
|
+
|
|
11912
|
+
module.exports = __WEBPACK_EXTERNAL_MODULE_node_path_02319fef__;
|
|
11913
|
+
|
|
11581
11914
|
/***/ }),
|
|
11582
11915
|
|
|
11583
11916
|
/***/ 5814:
|
|
@@ -11650,7 +11983,7 @@ class TelemetryReporter {
|
|
|
11650
11983
|
const arch = os_1.default.arch(); // 系统架构
|
|
11651
11984
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
11652
11985
|
const mcpVersion = process.env.npm_package_version ||
|
|
11653
|
-
( true ? "2.
|
|
11986
|
+
( true ? "2.27.0" : 0);
|
|
11654
11987
|
return {
|
|
11655
11988
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
11656
11989
|
deviceId: this.deviceId,
|
|
@@ -11984,8 +12317,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11984
12317
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11985
12318
|
};
|
|
11986
12319
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
11987
|
-
exports.CUSTOM_IMAGE_RUNTIME = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
|
|
12320
|
+
exports.CUSTOM_IMAGE_RUNTIME = exports.LAYER_SOFT_WARN = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
|
|
11988
12321
|
exports.formatRuntimeList = formatRuntimeList;
|
|
12322
|
+
exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
|
|
12323
|
+
exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
|
|
11989
12324
|
exports.validateTimerCron = validateTimerCron;
|
|
11990
12325
|
exports.pickManageFunctionName = pickManageFunctionName;
|
|
11991
12326
|
exports.shouldInstallDependencyForFunction = shouldInstallDependencyForFunction;
|
|
@@ -12090,6 +12425,24 @@ exports.MANAGE_FUNCTION_ACTIONS = [
|
|
|
12090
12425
|
"updateFunctionLayers",
|
|
12091
12426
|
"incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
|
|
12092
12427
|
];
|
|
12428
|
+
/** Layer soft-warn copy — account-scoped SCF LayerName guidance (no hard fail). */
|
|
12429
|
+
exports.LAYER_SOFT_WARN = {
|
|
12430
|
+
createNameFormat: (envId) => `建议使用 {layerName}_${envId} 格式,当前名称可能与其他环境共享版本序列`,
|
|
12431
|
+
deleteVersion: "该层为账号级共享资源,删除版本会影响所有绑定该版本的环境的函数,请确认",
|
|
12432
|
+
bindShared: "层为账号级共享,绑定/解绑影响所有引用该层名的环境",
|
|
12433
|
+
accountLevelView: "返回账号级视图,含其他环境创建的层",
|
|
12434
|
+
};
|
|
12435
|
+
function layerNameIncludesEnvId(layerName, envId) {
|
|
12436
|
+
if (!envId)
|
|
12437
|
+
return true;
|
|
12438
|
+
return layerName.includes(envId);
|
|
12439
|
+
}
|
|
12440
|
+
function buildCreateLayerNameWarning(layerName, envId) {
|
|
12441
|
+
if (!envId || layerNameIncludesEnvId(layerName, envId)) {
|
|
12442
|
+
return undefined;
|
|
12443
|
+
}
|
|
12444
|
+
return exports.LAYER_SOFT_WARN.createNameFormat(envId);
|
|
12445
|
+
}
|
|
12093
12446
|
const VPC_SCHEMA = zod_1.z.object({
|
|
12094
12447
|
vpcId: zod_1.z
|
|
12095
12448
|
.string()
|
|
@@ -12331,11 +12684,12 @@ function registerFunctionTools(server) {
|
|
|
12331
12684
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
12332
12685
|
const deployOverrides = server.pluginOptions?.functions;
|
|
12333
12686
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
12334
|
-
const buildEnvelope = (data, message, nextActions) => ({
|
|
12687
|
+
const buildEnvelope = (data, message, nextActions, warnings) => ({
|
|
12335
12688
|
success: true,
|
|
12336
12689
|
data,
|
|
12337
12690
|
message,
|
|
12338
12691
|
...(nextActions?.length ? { nextActions } : {}),
|
|
12692
|
+
...(warnings?.length ? { warnings } : {}),
|
|
12339
12693
|
});
|
|
12340
12694
|
const buildErrorEnvelope = (error, errorCode) => ({
|
|
12341
12695
|
success: false,
|
|
@@ -12586,7 +12940,7 @@ function registerFunctionTools(server) {
|
|
|
12586
12940
|
action: "createLayerVersion",
|
|
12587
12941
|
reason: "发布新的层版本",
|
|
12588
12942
|
},
|
|
12589
|
-
]);
|
|
12943
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
12590
12944
|
}
|
|
12591
12945
|
case "listLayerVersions": {
|
|
12592
12946
|
if (!input.layerName) {
|
|
@@ -12614,7 +12968,7 @@ function registerFunctionTools(server) {
|
|
|
12614
12968
|
action: "attachLayer",
|
|
12615
12969
|
reason: "将某个层版本绑定到函数",
|
|
12616
12970
|
},
|
|
12617
|
-
]);
|
|
12971
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
12618
12972
|
}
|
|
12619
12973
|
case "getLayerVersionDetail": {
|
|
12620
12974
|
if (!input.layerName) {
|
|
@@ -12647,7 +13001,7 @@ function registerFunctionTools(server) {
|
|
|
12647
13001
|
action: "deleteLayerVersion",
|
|
12648
13002
|
reason: "删除该层版本",
|
|
12649
13003
|
},
|
|
12650
|
-
]);
|
|
13004
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
12651
13005
|
}
|
|
12652
13006
|
case "listFunctionTriggers": {
|
|
12653
13007
|
if (!input.functionName) {
|
|
@@ -13163,6 +13517,8 @@ function registerFunctionTools(server) {
|
|
|
13163
13517
|
if (!input.contentPath && !input.base64Content) {
|
|
13164
13518
|
throw new Error("createLayerVersion 操作时,contentPath 和 base64Content 至少需要提供一个");
|
|
13165
13519
|
}
|
|
13520
|
+
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
13521
|
+
const nameWarning = buildCreateLayerNameWarning(input.layerName, envId);
|
|
13166
13522
|
const cloudbase = await getManager();
|
|
13167
13523
|
const result = await cloudbase.functions.createLayer({
|
|
13168
13524
|
name: input.layerName,
|
|
@@ -13185,7 +13541,7 @@ function registerFunctionTools(server) {
|
|
|
13185
13541
|
action: "listLayerVersions",
|
|
13186
13542
|
reason: "查看该层的全部版本",
|
|
13187
13543
|
},
|
|
13188
|
-
]);
|
|
13544
|
+
], nameWarning ? [nameWarning] : undefined);
|
|
13189
13545
|
}
|
|
13190
13546
|
case "deleteLayerVersion": {
|
|
13191
13547
|
if (!input.layerName) {
|
|
@@ -13212,7 +13568,7 @@ function registerFunctionTools(server) {
|
|
|
13212
13568
|
action: "listLayerVersions",
|
|
13213
13569
|
reason: "确认剩余层版本",
|
|
13214
13570
|
},
|
|
13215
|
-
]);
|
|
13571
|
+
], [exports.LAYER_SOFT_WARN.deleteVersion]);
|
|
13216
13572
|
}
|
|
13217
13573
|
case "attachLayer":
|
|
13218
13574
|
case "detachLayer":
|
|
@@ -13222,6 +13578,7 @@ function registerFunctionTools(server) {
|
|
|
13222
13578
|
}
|
|
13223
13579
|
const cloudbase = await getManager();
|
|
13224
13580
|
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
13581
|
+
const bindWarnings = [exports.LAYER_SOFT_WARN.bindShared];
|
|
13225
13582
|
if (input.action === "attachLayer") {
|
|
13226
13583
|
if (!input.layerName) {
|
|
13227
13584
|
throw new Error("attachLayer 操作时,layerName 参数是必需的");
|
|
@@ -13250,7 +13607,7 @@ function registerFunctionTools(server) {
|
|
|
13250
13607
|
action: "listFunctionLayers",
|
|
13251
13608
|
reason: "确认函数当前绑定层列表",
|
|
13252
13609
|
},
|
|
13253
|
-
]);
|
|
13610
|
+
], bindWarnings);
|
|
13254
13611
|
}
|
|
13255
13612
|
if (input.action === "detachLayer") {
|
|
13256
13613
|
if (!input.layerName) {
|
|
@@ -13281,7 +13638,7 @@ function registerFunctionTools(server) {
|
|
|
13281
13638
|
action: "listFunctionLayers",
|
|
13282
13639
|
reason: "确认解绑后的层列表",
|
|
13283
13640
|
},
|
|
13284
|
-
]);
|
|
13641
|
+
], bindWarnings);
|
|
13285
13642
|
}
|
|
13286
13643
|
const normalizedLayers = normalizeManageLayers(input.layers);
|
|
13287
13644
|
if (!normalizedLayers.length) {
|
|
@@ -13306,7 +13663,7 @@ function registerFunctionTools(server) {
|
|
|
13306
13663
|
action: "listFunctionLayers",
|
|
13307
13664
|
reason: "确认最新层顺序和绑定结果",
|
|
13308
13665
|
},
|
|
13309
|
-
]);
|
|
13666
|
+
], bindWarnings);
|
|
13310
13667
|
}
|
|
13311
13668
|
default:
|
|
13312
13669
|
// incrementalDeployFunction:无默认实现,必须通过 pluginOptions 注入
|
|
@@ -13336,6 +13693,10 @@ function registerFunctionTools(server) {
|
|
|
13336
13693
|
"\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
|
|
13337
13694
|
"\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
|
|
13338
13695
|
"\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
|
|
13696
|
+
"\n\n**层(Layer)说明**:" +
|
|
13697
|
+
"\n- 层为 SCF 账号级共享命名空间:不同环境创建同名层会共享同一层的版本序列;删除某版本会影响所有绑定该版本的环境的函数" +
|
|
13698
|
+
"\n- 创建层必须用带环境标识的唯一层名,固定格式:`{layerName}_{当前envId}`(如 `common_cloud1-d9ghadgak3edf6b36`)。不要在不同环境使用相同裸层名,创建前先 `listLayers` 查重" +
|
|
13699
|
+
"\n- `listLayers` / `listLayerVersions` / `getLayerVersionDetail` 返回账号级视图,可能含其他环境创建的层" +
|
|
13339
13700
|
"\n\n**区分 `queryLogs` 工具**:" +
|
|
13340
13701
|
"\n- 本工具用于查询特定 CloudBase 云函数的执行日志" +
|
|
13341
13702
|
"\n- `queryLogs` 工具用于搜索 CLS 日志服务(跨服务日志聚合)",
|
|
@@ -13348,9 +13709,9 @@ function registerFunctionTools(server) {
|
|
|
13348
13709
|
"\n- `listFunctionLogs`: 查询 CloudBase 云函数执行日志(需要 functionName)" +
|
|
13349
13710
|
"\n- `getFunctionLogDetail`: 获取日志详情(需要 requestId)" +
|
|
13350
13711
|
"\n- `listFunctionLayers`: 列出函数绑定的层" +
|
|
13351
|
-
"\n- `listLayers`:
|
|
13352
|
-
"\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version
|
|
13353
|
-
"\n- `getLayerVersionDetail`:
|
|
13712
|
+
"\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)" +
|
|
13713
|
+
"\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)" +
|
|
13714
|
+
"\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)" +
|
|
13354
13715
|
"\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)" +
|
|
13355
13716
|
"\n- `getFunctionDownloadUrl`: 获取函数代码下载地址"),
|
|
13356
13717
|
functionName: zod_1.z
|
|
@@ -13377,7 +13738,11 @@ function registerFunctionTools(server) {
|
|
|
13377
13738
|
qualifier: zod_1.z.string().optional().describe("函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选"),
|
|
13378
13739
|
runtime: zod_1.z.string().optional().describe("层查询的运行时筛选,如 Nodejs18.15"),
|
|
13379
13740
|
searchKey: zod_1.z.string().optional().describe("层名称搜索关键字"),
|
|
13380
|
-
layerName: zod_1.z
|
|
13741
|
+
layerName: zod_1.z
|
|
13742
|
+
.string()
|
|
13743
|
+
.optional()
|
|
13744
|
+
.describe("层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填。" +
|
|
13745
|
+
"层为账号级共享命名空间;推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)"),
|
|
13381
13746
|
layerVersion: zod_1.z.number().optional().describe("层版本号。`getLayerVersionDetail` 操作必填"),
|
|
13382
13747
|
},
|
|
13383
13748
|
annotations: {
|
|
@@ -13392,12 +13757,18 @@ function registerFunctionTools(server) {
|
|
|
13392
13757
|
"如果要创建 cron 定时任务,先用 createFunction 创建函数,再用 createFunctionTrigger 创建 timer 触发器(支持7段cron表达式),deleteFunctionTrigger 删除触发器。" +
|
|
13393
13758
|
"镜像部署(Runtime=CustomImage):先把代码经 zip→COS→CloudApp custom 构建→TCR 推镜像(这一阶段为裸腾讯云 API,本工具不覆盖)," +
|
|
13394
13759
|
"再用 createFunction(func.runtime=\"CustomImage\", imageConfig) 基于 TCR 镜像创建 HTTP 函数;后续迭代用 updateFunctionCode + imageConfig 换镜像 tag。" +
|
|
13395
|
-
"危险操作需要显式 confirm=true。"
|
|
13760
|
+
"危险操作需要显式 confirm=true。" +
|
|
13761
|
+
"\n\n**层(Layer)说明**:" +
|
|
13762
|
+
"\n- 层为 SCF 账号级共享命名空间:不同环境创建同名层会共享同一层的版本序列;删除某版本会影响所有绑定该版本的环境的函数" +
|
|
13763
|
+
"\n- 创建层必须用带环境标识的唯一层名,固定格式:`{layerName}_{当前envId}`(如 `common_cloud1-d9ghadgak3edf6b36`)。不要在不同环境使用相同裸层名,创建前先 `listLayers` 查重" +
|
|
13764
|
+
"\n- 相关 action:`createLayerVersion` / `deleteLayerVersion` / `attachLayer` / `detachLayer` / `updateFunctionLayers`(只读查询见 queryFunctions 的 listLayers / listLayerVersions / getLayerVersionDetail)",
|
|
13396
13765
|
inputSchema: {
|
|
13397
13766
|
action: zod_1.z
|
|
13398
13767
|
.enum(exports.MANAGE_FUNCTION_ACTIONS)
|
|
13399
13768
|
.describe("写操作类型,例如 createFunction、updateFunctionCode、incrementalDeployFunction、invokeFunction、deleteFunction、" +
|
|
13400
|
-
"createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、
|
|
13769
|
+
"createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、" +
|
|
13770
|
+
"createLayerVersion、deleteLayerVersion、attachLayer、detachLayer、updateFunctionLayers。" +
|
|
13771
|
+
"层名推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)"),
|
|
13401
13772
|
func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
|
|
13402
13773
|
functionRootPath: zod_1.z.string().optional().describe("创建或更新函数代码时默认推荐的本地目录方式。" +
|
|
13403
13774
|
"必须是直接包含函数文件夹的目录绝对路径(如 /abs/path/cloudfunctions 或 /abs/path/functions)," +
|
|
@@ -13427,7 +13798,11 @@ function registerFunctionTools(server) {
|
|
|
13427
13798
|
.describe("createFunctionTrigger 的触发器列表,用于定时跑 / 定时任务 / scheduled job。timer 触发器使用7段 cron 表达式(秒 分 时 日 月 星期 年)," +
|
|
13428
13799
|
'如 "0 */5 * * * * *" 表示每5分钟执行一次'),
|
|
13429
13800
|
triggerName: zod_1.z.string().optional().describe("deleteFunctionTrigger 的目标触发器名称"),
|
|
13430
|
-
layerName: zod_1.z
|
|
13801
|
+
layerName: zod_1.z
|
|
13802
|
+
.string()
|
|
13803
|
+
.optional()
|
|
13804
|
+
.describe("层名称。创建层推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36);" +
|
|
13805
|
+
"不要跨环境复用裸层名。层为账号级共享命名空间"),
|
|
13431
13806
|
layerVersion: zod_1.z.number().optional().describe("层版本号"),
|
|
13432
13807
|
contentPath: zod_1.z.string().optional().describe("层内容路径,可为目录或 ZIP 文件"),
|
|
13433
13808
|
base64Content: zod_1.z.string().optional().describe("层内容的 base64 编码"),
|
|
@@ -17185,7 +17560,7 @@ function registerSetupTools(server) {
|
|
|
17185
17560
|
server.registerTool("downloadTemplate", {
|
|
17186
17561
|
title: "下载项目模板",
|
|
17187
17562
|
description: `自动下载并部署CloudBase项目模板。
|
|
17188
|
-
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.
|
|
17563
|
+
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.27.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
17189
17564
|
inputSchema: {
|
|
17190
17565
|
template: zod_1.z
|
|
17191
17566
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -17640,7 +18015,8 @@ function registerAppTools(server) {
|
|
|
17640
18015
|
.optional()
|
|
17641
18016
|
.describe("自定义部署命令。通常无需填写,默认自动生成 tcb hosting deploy 命令。" +
|
|
17642
18017
|
"有 buildPath 时远端已 cd 到该目录,默认用 . 作为源码路径;无 buildPath 时默认用 dist。"),
|
|
17643
|
-
ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules
|
|
18018
|
+
ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules/**。\n" +
|
|
18019
|
+
"⚠️ 打包的是项目根目录(filePath)而非 buildPath 产物目录:若项目根含 target/(Rust)、.next/、dist-old/、build/ 等大构建产物,必须加进 ignore(如 **/target/**),否则整个目录被打进上传 zip(实证 54GB target → 34GB zip)。默认已排除 node_modules/.git/.DS_Store/**/target/**/.next/**/.next.bak/**。"),
|
|
17644
18020
|
versionName: zod_1.z
|
|
17645
18021
|
.string()
|
|
17646
18022
|
.optional()
|
|
@@ -17660,6 +18036,17 @@ function registerAppTools(server) {
|
|
|
17660
18036
|
if (!appService) {
|
|
17661
18037
|
throw new Error("当前 manager 未提供 cloudAppService");
|
|
17662
18038
|
}
|
|
18039
|
+
// 默认排除的大目录(2026-08-14 实证:ato 项目 target/ 54GB 被整个打进 zip)
|
|
18040
|
+
// tcb app deploy 打包的是项目根目录(localPath)而非 outputDir,必须排除构建产物
|
|
18041
|
+
const defaultPackIgnore = [
|
|
18042
|
+
"node_modules/**",
|
|
18043
|
+
".git/**",
|
|
18044
|
+
".DS_Store",
|
|
18045
|
+
"**/.DS_Store",
|
|
18046
|
+
"**/target/**",
|
|
18047
|
+
"**/.next/**",
|
|
18048
|
+
"**/.next.bak/**",
|
|
18049
|
+
];
|
|
17663
18050
|
// getUploadUrl — 获取预签名上传 URL(cloud mode 专用)
|
|
17664
18051
|
if (action === "getUploadUrl") {
|
|
17665
18052
|
if (!serviceName) {
|
|
@@ -17670,9 +18057,9 @@ function registerAppTools(server) {
|
|
|
17670
18057
|
serviceName,
|
|
17671
18058
|
});
|
|
17672
18059
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, cosInfoResult);
|
|
17673
|
-
const defaultIgnore =
|
|
18060
|
+
const defaultIgnore = defaultPackIgnore;
|
|
17674
18061
|
// eslint-disable-next-line max-len
|
|
17675
|
-
const zipCmd = "zip -r upload.zip . -x 'node_modules/**' -x '.git/**' -x '.DS_Store' -x '**/.DS_Store'";
|
|
18062
|
+
const zipCmd = "zip -r upload.zip . -x 'node_modules/**' -x '.git/**' -x '.DS_Store' -x '**/.DS_Store' -x '**/target/**' -x '**/.next/**' -x '**/.next.bak/**'";
|
|
17676
18063
|
const followupArgs = {
|
|
17677
18064
|
action: "deployApp",
|
|
17678
18065
|
serviceName,
|
|
@@ -17716,11 +18103,17 @@ function registerAppTools(server) {
|
|
|
17716
18103
|
// 上传代码到 COS(仅本地模式需要,cloud mode 用 cosTimestamp 跳过)
|
|
17717
18104
|
let cosTs = cosTimestamp;
|
|
17718
18105
|
if (filePath && !cosTs) {
|
|
18106
|
+
// 默认排除大目录(2026-08-14 实证:ato 项目 target/ 54GB 被整个打进 zip)
|
|
18107
|
+
// 用户显式传 ignore 时合并,避免覆盖默认值
|
|
18108
|
+
const mergedIgnore = Array.from(new Set([
|
|
18109
|
+
...defaultPackIgnore,
|
|
18110
|
+
...(ignore ?? []),
|
|
18111
|
+
]));
|
|
17719
18112
|
const uploadResult = await appService.uploadCode({
|
|
17720
18113
|
deployType: "static-hosting",
|
|
17721
18114
|
serviceName,
|
|
17722
18115
|
localPath: filePath,
|
|
17723
|
-
ignore,
|
|
18116
|
+
ignore: mergedIgnore,
|
|
17724
18117
|
});
|
|
17725
18118
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
|
|
17726
18119
|
cosTs = uploadResult.cosTimestamp;
|
|
@@ -19278,7 +19671,7 @@ exports.getLoginState = getLoginState;
|
|
|
19278
19671
|
exports.logout = logout;
|
|
19279
19672
|
const toolbox_1 = __webpack_require__(2090);
|
|
19280
19673
|
const logger_js_1 = __webpack_require__(3039);
|
|
19281
|
-
const
|
|
19674
|
+
const site_map_js_1 = __webpack_require__(8107);
|
|
19282
19675
|
const auth = toolbox_1.AuthSupervisor.getInstance({});
|
|
19283
19676
|
const authProgressState = {
|
|
19284
19677
|
status: "IDLE",
|
|
@@ -19527,6 +19920,98 @@ function resetAuthProgressState() {
|
|
|
19527
19920
|
lastError: undefined,
|
|
19528
19921
|
});
|
|
19529
19922
|
}
|
|
19923
|
+
const LEGACY_SITE = "domestic";
|
|
19924
|
+
function isSlottedCredential(value) {
|
|
19925
|
+
if (!value || typeof value !== "object") {
|
|
19926
|
+
return false;
|
|
19927
|
+
}
|
|
19928
|
+
return "domestic" in value || "intl" in value;
|
|
19929
|
+
}
|
|
19930
|
+
async function readStoredCredentialRaw() {
|
|
19931
|
+
return toolbox_1.authStore.get("credential");
|
|
19932
|
+
}
|
|
19933
|
+
/**
|
|
19934
|
+
* 读取指定 site 槽位的原始凭证;旧格式(单槽 flat)视为 domestic 槽位。
|
|
19935
|
+
*/
|
|
19936
|
+
async function readSiteCredentialRaw(site) {
|
|
19937
|
+
const raw = await readStoredCredentialRaw();
|
|
19938
|
+
if (!raw) {
|
|
19939
|
+
return undefined;
|
|
19940
|
+
}
|
|
19941
|
+
if (isSlottedCredential(raw)) {
|
|
19942
|
+
return raw[site];
|
|
19943
|
+
}
|
|
19944
|
+
return site === LEGACY_SITE ? raw : undefined;
|
|
19945
|
+
}
|
|
19946
|
+
/**
|
|
19947
|
+
* 将凭证写入指定 site 槽位,保留其他槽位数据。
|
|
19948
|
+
* base 缺省时读取当前存储;旧单槽数据会被迁移为 domestic 槽位(写回时升级分槽)。
|
|
19949
|
+
*/
|
|
19950
|
+
async function writeSiteCredential(site, credential, base) {
|
|
19951
|
+
const current = base ?? (await readStoredCredentialRaw());
|
|
19952
|
+
const slotted = current && isSlottedCredential(current)
|
|
19953
|
+
? { ...current }
|
|
19954
|
+
: { domestic: current ?? {} };
|
|
19955
|
+
slotted[site] = credential ?? {};
|
|
19956
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
19957
|
+
}
|
|
19958
|
+
/**
|
|
19959
|
+
* 确保存储已为分槽格式并返回槽位快照。
|
|
19960
|
+
* 用于登录前迁移旧单槽数据,避免 @cloudbase/toolbox 内部误命中旧凭证而跳过新站点登录。
|
|
19961
|
+
*/
|
|
19962
|
+
async function ensureSlottedCredential() {
|
|
19963
|
+
const raw = await readStoredCredentialRaw();
|
|
19964
|
+
if (raw && isSlottedCredential(raw)) {
|
|
19965
|
+
return { ...raw };
|
|
19966
|
+
}
|
|
19967
|
+
const slotted = { domestic: raw ?? {} };
|
|
19968
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
19969
|
+
return slotted;
|
|
19970
|
+
}
|
|
19971
|
+
function isSlotTokenExpired(credential, gap = 120) {
|
|
19972
|
+
return (!!credential.accessTokenExpired &&
|
|
19973
|
+
Number(credential.accessTokenExpired) < Date.now() + gap * 1000);
|
|
19974
|
+
}
|
|
19975
|
+
/**
|
|
19976
|
+
* 读取并解析指定 site 的登录态,必要时走 refreshToken 续期(镜像 @cloudbase/toolbox 行为)。
|
|
19977
|
+
*/
|
|
19978
|
+
async function resolveSiteLoginState(site) {
|
|
19979
|
+
const raw = await readSiteCredentialRaw(site);
|
|
19980
|
+
if (!raw) {
|
|
19981
|
+
return null;
|
|
19982
|
+
}
|
|
19983
|
+
const credential = (0, toolbox_1.resolveCredential)(raw);
|
|
19984
|
+
if (!credential?.secretId || !credential?.secretKey) {
|
|
19985
|
+
return null;
|
|
19986
|
+
}
|
|
19987
|
+
if (credential.refreshToken) {
|
|
19988
|
+
if (!isSlotTokenExpired(credential)) {
|
|
19989
|
+
return credential;
|
|
19990
|
+
}
|
|
19991
|
+
if (Date.now() < Number(credential.expired)) {
|
|
19992
|
+
try {
|
|
19993
|
+
const refreshed = await (0, toolbox_1.refreshTmpToken)(credential);
|
|
19994
|
+
await writeSiteCredential(site, refreshed ?? {});
|
|
19995
|
+
const resolved = (0, toolbox_1.resolveCredential)(refreshed ?? {});
|
|
19996
|
+
return resolved?.secretId ? resolved : null;
|
|
19997
|
+
}
|
|
19998
|
+
catch (e) {
|
|
19999
|
+
const code = e?.code;
|
|
20000
|
+
if (code === "AUTH_FAIL" || code === "InternalError.GetRoleError") {
|
|
20001
|
+
return null;
|
|
20002
|
+
}
|
|
20003
|
+
throw e;
|
|
20004
|
+
}
|
|
20005
|
+
}
|
|
20006
|
+
return null;
|
|
20007
|
+
}
|
|
20008
|
+
return credential;
|
|
20009
|
+
}
|
|
20010
|
+
function isUsableCredential(value) {
|
|
20011
|
+
return (!!value &&
|
|
20012
|
+
typeof value === "object" &&
|
|
20013
|
+
Object.keys(value).length > 0);
|
|
20014
|
+
}
|
|
19530
20015
|
async function peekLoginState(options) {
|
|
19531
20016
|
const envVarLoginState = normalizeLoginStateFromEnvVars(options);
|
|
19532
20017
|
if (envVarLoginState) {
|
|
@@ -19548,14 +20033,25 @@ async function peekLoginState(options) {
|
|
|
19548
20033
|
(0, logger_js_1.debug)("loginByApiSecret");
|
|
19549
20034
|
return envVarLoginState;
|
|
19550
20035
|
}
|
|
19551
|
-
//
|
|
19552
|
-
|
|
20036
|
+
// 按 site 读取分槽凭证(缺省按 TCB_SITE/region 解析站点,默认 domestic)
|
|
20037
|
+
const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.resolveSite)(options?.region, process.env.TCB_SITE);
|
|
20038
|
+
const slotLoginState = await resolveSiteLoginState(site);
|
|
20039
|
+
if (slotLoginState) {
|
|
20040
|
+
return slotLoginState;
|
|
20041
|
+
}
|
|
20042
|
+
// 兼容旧单槽数据(视为 domestic),走 toolbox 读取/续期
|
|
20043
|
+
if (site === LEGACY_SITE) {
|
|
20044
|
+
return auth.getLoginState();
|
|
20045
|
+
}
|
|
20046
|
+
return null;
|
|
19553
20047
|
}
|
|
19554
20048
|
async function ensureLogin(options) {
|
|
19555
20049
|
(0, logger_js_1.debug)("TENCENTCLOUD_SECRETID", { hasSecretId: !!process.env.TENCENTCLOUD_SECRETID });
|
|
19556
20050
|
(0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!getCloudBaseApiKeyFromEnv() });
|
|
19557
20051
|
const loginState = await peekLoginState({
|
|
19558
20052
|
ignoreEnvVars: options?.ignoreEnvVars,
|
|
20053
|
+
site: options?.site,
|
|
20054
|
+
region: options?.region,
|
|
19559
20055
|
});
|
|
19560
20056
|
if (!loginState) {
|
|
19561
20057
|
const resolvedAuthOptions = resolveAuthOptions({
|
|
@@ -19571,21 +20067,31 @@ async function ensureLogin(options) {
|
|
|
19571
20067
|
throw new Error(validationError);
|
|
19572
20068
|
}
|
|
19573
20069
|
const mode = resolvedAuthOptions.authMode;
|
|
20070
|
+
// 按显式 site > TCB_SITE > region 映射表解析站点(ap-singapore 歧义默认 intl,兼容既有国际站行为)
|
|
20071
|
+
const resolvedSite = (0, site_map_js_1.resolveSite)(options?.region, options?.site ?? process.env.TCB_SITE);
|
|
19574
20072
|
const loginOptions = { flow: mode };
|
|
19575
20073
|
if (mode === "web") {
|
|
19576
20074
|
loginOptions.getAuthUrl =
|
|
19577
|
-
options?.fromCloudBaseLoginPage &&
|
|
20075
|
+
options?.fromCloudBaseLoginPage && resolvedSite === "domestic"
|
|
19578
20076
|
? (url) => {
|
|
19579
20077
|
const separator = url.includes("?") ? "&" : "?";
|
|
19580
20078
|
const urlWithParam = `${url}${separator}allowNoEnv=true`;
|
|
19581
|
-
return `https
|
|
20079
|
+
return `https://${site_map_js_1.SITE_REGION_MAP.domestic.authHost}/login?_redirect_uri=${encodeURIComponent(urlWithParam)}`;
|
|
19582
20080
|
}
|
|
19583
20081
|
: (url) => {
|
|
19584
|
-
|
|
19585
|
-
|
|
20082
|
+
let finalUrl = url;
|
|
20083
|
+
if (resolvedSite === "intl") {
|
|
20084
|
+
try {
|
|
20085
|
+
const parsed = new URL(url);
|
|
20086
|
+
parsed.host = site_map_js_1.SITE_REGION_MAP.intl.authHost;
|
|
20087
|
+
finalUrl = parsed.toString();
|
|
20088
|
+
}
|
|
20089
|
+
catch {
|
|
20090
|
+
finalUrl = url.replace("cloud.tencent.com", "tencentcloud.com");
|
|
20091
|
+
}
|
|
19586
20092
|
}
|
|
19587
|
-
const separator =
|
|
19588
|
-
return `${
|
|
20093
|
+
const separator = finalUrl.includes("?") ? "&" : "?";
|
|
20094
|
+
return `${finalUrl}${separator}allowNoEnv=true`;
|
|
19589
20095
|
};
|
|
19590
20096
|
}
|
|
19591
20097
|
else {
|
|
@@ -19606,8 +20112,23 @@ async function ensureLogin(options) {
|
|
|
19606
20112
|
}
|
|
19607
20113
|
}
|
|
19608
20114
|
(0, logger_js_1.debug)("beforeloginByWebAuth", { loginOptions });
|
|
20115
|
+
// 登录前迁移旧单槽数据为分槽格式,避免 toolbox 内部命中旧凭证而跳过新站点登录
|
|
20116
|
+
const slotsBefore = await ensureSlottedCredential();
|
|
19609
20117
|
try {
|
|
19610
|
-
await auth.loginByWebAuth(loginOptions);
|
|
20118
|
+
const loginResult = await auth.loginByWebAuth(loginOptions);
|
|
20119
|
+
// toolbox 将新凭证写入 flat 'credential',将其并入对应 site 槽位,保留其他槽位
|
|
20120
|
+
const newRaw = await readStoredCredentialRaw();
|
|
20121
|
+
let credentialToSlot = loginResult;
|
|
20122
|
+
if (isSlottedCredential(newRaw)) {
|
|
20123
|
+
credentialToSlot = newRaw[resolvedSite] ?? newRaw.domestic;
|
|
20124
|
+
}
|
|
20125
|
+
else if (newRaw) {
|
|
20126
|
+
credentialToSlot = newRaw;
|
|
20127
|
+
}
|
|
20128
|
+
if (isUsableCredential(credentialToSlot)) {
|
|
20129
|
+
const merged = { ...slotsBefore, [resolvedSite]: credentialToSlot };
|
|
20130
|
+
await toolbox_1.authStore.set("credential", merged);
|
|
20131
|
+
}
|
|
19611
20132
|
resolveAuthProgressState();
|
|
19612
20133
|
}
|
|
19613
20134
|
catch (error) {
|
|
@@ -19616,6 +20137,8 @@ async function ensureLogin(options) {
|
|
|
19616
20137
|
}
|
|
19617
20138
|
const loginState = await peekLoginState({
|
|
19618
20139
|
ignoreEnvVars: options?.ignoreEnvVars,
|
|
20140
|
+
site: options?.site,
|
|
20141
|
+
region: options?.region,
|
|
19619
20142
|
});
|
|
19620
20143
|
(0, logger_js_1.debug)("loginByWebAuth", { mode, hasLoginState: !!loginState });
|
|
19621
20144
|
return loginState;
|
|
@@ -19628,11 +20151,19 @@ async function ensureLogin(options) {
|
|
|
19628
20151
|
async function getLoginState(options) {
|
|
19629
20152
|
return ensureLogin(options);
|
|
19630
20153
|
}
|
|
19631
|
-
async function logout() {
|
|
19632
|
-
const
|
|
19633
|
-
const
|
|
20154
|
+
async function logout(options) {
|
|
20155
|
+
const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? "domestic";
|
|
20156
|
+
const raw = await readStoredCredentialRaw();
|
|
20157
|
+
if (raw && isSlottedCredential(raw)) {
|
|
20158
|
+
const slotted = { ...raw };
|
|
20159
|
+
delete slotted[site];
|
|
20160
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
20161
|
+
}
|
|
20162
|
+
else if (site === LEGACY_SITE) {
|
|
20163
|
+
const cwd = process.env.WORKSPACE_FOLDER_PATHS || process.cwd();
|
|
20164
|
+
await auth.logout({ cwd });
|
|
20165
|
+
}
|
|
19634
20166
|
resetAuthProgressState();
|
|
19635
|
-
return result;
|
|
19636
20167
|
}
|
|
19637
20168
|
|
|
19638
20169
|
|
|
@@ -19657,6 +20188,126 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_crypto__;
|
|
|
19657
20188
|
|
|
19658
20189
|
module.exports = __WEBPACK_EXTERNAL_MODULE_fs_promises_f8dae9d1__;
|
|
19659
20190
|
|
|
20191
|
+
/***/ }),
|
|
20192
|
+
|
|
20193
|
+
/***/ 8107:
|
|
20194
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
20195
|
+
|
|
20196
|
+
|
|
20197
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
20198
|
+
exports.SITE_IDS = exports.SITE_REGION_MAP = void 0;
|
|
20199
|
+
exports.isSiteId = isSiteId;
|
|
20200
|
+
exports.normalizeSite = normalizeSite;
|
|
20201
|
+
exports.getSite = getSite;
|
|
20202
|
+
exports.resolveSite = resolveSite;
|
|
20203
|
+
exports.resolveSiteAndRegion = resolveSiteAndRegion;
|
|
20204
|
+
const project_config_js_1 = __webpack_require__(8445);
|
|
20205
|
+
exports.SITE_REGION_MAP = {
|
|
20206
|
+
domestic: {
|
|
20207
|
+
id: "domestic",
|
|
20208
|
+
label: "国内站",
|
|
20209
|
+
authHost: "tcb.cloud.tencent.com",
|
|
20210
|
+
consoleHost: "tcb.cloud.tencent.com",
|
|
20211
|
+
defaultRegion: "ap-shanghai",
|
|
20212
|
+
regions: ["ap-shanghai", "ap-guangzhou", "ap-singapore"],
|
|
20213
|
+
capabilities: { noSql: true },
|
|
20214
|
+
},
|
|
20215
|
+
intl: {
|
|
20216
|
+
id: "intl",
|
|
20217
|
+
label: "国际站",
|
|
20218
|
+
authHost: "tcb.tencentcloud.com",
|
|
20219
|
+
consoleHost: "tcb.tencentcloud.com",
|
|
20220
|
+
defaultRegion: "ap-singapore",
|
|
20221
|
+
regions: ["ap-singapore"],
|
|
20222
|
+
capabilities: { noSql: false },
|
|
20223
|
+
},
|
|
20224
|
+
};
|
|
20225
|
+
exports.SITE_IDS = Object.keys(exports.SITE_REGION_MAP);
|
|
20226
|
+
function isSiteId(value) {
|
|
20227
|
+
return value === "domestic" || value === "intl";
|
|
20228
|
+
}
|
|
20229
|
+
function normalizeSite(value) {
|
|
20230
|
+
if (typeof value !== "string") {
|
|
20231
|
+
return undefined;
|
|
20232
|
+
}
|
|
20233
|
+
const normalized = value.trim().toLowerCase();
|
|
20234
|
+
if (normalized === "intl" || normalized === "international") {
|
|
20235
|
+
return "intl";
|
|
20236
|
+
}
|
|
20237
|
+
if (normalized === "domestic" || normalized === "cn") {
|
|
20238
|
+
return "domestic";
|
|
20239
|
+
}
|
|
20240
|
+
return undefined;
|
|
20241
|
+
}
|
|
20242
|
+
/**
|
|
20243
|
+
* 根据 region 与显式 site 解析站点。
|
|
20244
|
+
*
|
|
20245
|
+
* - 显式 site 优先(`domestic`/`intl`,兼容 `cn`/`international` 别名)
|
|
20246
|
+
* - 否则查 SITE_REGION_MAP:region 只属于一个站点时返回该站点
|
|
20247
|
+
* - 当 region 同时存在于多个站点(如 ap-singapore)时返回 `ambiguous`,由上层决定
|
|
20248
|
+
* - 未知 region 或未提供 region 时回退 domestic(全局默认)
|
|
20249
|
+
*/
|
|
20250
|
+
function getSite(region, explicitSite) {
|
|
20251
|
+
const explicit = normalizeSite(explicitSite);
|
|
20252
|
+
if (explicit) {
|
|
20253
|
+
return explicit;
|
|
20254
|
+
}
|
|
20255
|
+
if (!region) {
|
|
20256
|
+
return "domestic";
|
|
20257
|
+
}
|
|
20258
|
+
const matches = exports.SITE_IDS.filter((site) => exports.SITE_REGION_MAP[site].regions.includes(region));
|
|
20259
|
+
if (matches.length === 1) {
|
|
20260
|
+
return matches[0];
|
|
20261
|
+
}
|
|
20262
|
+
if (matches.length > 1) {
|
|
20263
|
+
return "ambiguous";
|
|
20264
|
+
}
|
|
20265
|
+
return "domestic";
|
|
20266
|
+
}
|
|
20267
|
+
/**
|
|
20268
|
+
* 认证/路由场景下解析站点:歧义(如 ap-singapore 未配 site)时默认 intl,保持既有国际站行为。
|
|
20269
|
+
*/
|
|
20270
|
+
function resolveSite(region, explicitSite) {
|
|
20271
|
+
const site = getSite(region, explicitSite);
|
|
20272
|
+
return site === "ambiguous" ? "intl" : site;
|
|
20273
|
+
}
|
|
20274
|
+
/**
|
|
20275
|
+
* 统一 site/region 解析入口(收敛各调用点分散的 region fallback)。
|
|
20276
|
+
*
|
|
20277
|
+
* 优先级:显式参数 > 环境变量(TCB_SITE/TCB_REGION)> 项目配置(.cloudbase/project.json)> 全局默认
|
|
20278
|
+
* (site=domestic, region=ap-shanghai)。
|
|
20279
|
+
*
|
|
20280
|
+
* 当仅指定 region 且该 region 在多个 site 的地域列表中都存在(如 ap-singapore)时,
|
|
20281
|
+
* 返回 site=intl 并标记 ambiguous=true(兼容既有国际站行为),调用方可用 ambiguous 提示用户显式指定 site。
|
|
20282
|
+
*/
|
|
20283
|
+
function resolveSiteAndRegion(opts = {}) {
|
|
20284
|
+
const projectConfig = (0, project_config_js_1.readProjectConfig)();
|
|
20285
|
+
const explicitSite = normalizeSite(opts.site) ??
|
|
20286
|
+
normalizeSite(process.env.TCB_SITE) ??
|
|
20287
|
+
normalizeSite(projectConfig?.site);
|
|
20288
|
+
const explicitRegion = opts.region ?? process.env.TCB_REGION ?? projectConfig?.region;
|
|
20289
|
+
let site = explicitSite;
|
|
20290
|
+
let ambiguous = false;
|
|
20291
|
+
if (!site && explicitRegion) {
|
|
20292
|
+
const inferred = getSite(explicitRegion);
|
|
20293
|
+
if (inferred === "ambiguous") {
|
|
20294
|
+
site = "intl";
|
|
20295
|
+
ambiguous = true;
|
|
20296
|
+
}
|
|
20297
|
+
else if (inferred !== "domestic") {
|
|
20298
|
+
site = inferred;
|
|
20299
|
+
}
|
|
20300
|
+
}
|
|
20301
|
+
const resolvedSite = site ?? "domestic";
|
|
20302
|
+
const region = explicitRegion ?? exports.SITE_REGION_MAP[resolvedSite].defaultRegion;
|
|
20303
|
+
return {
|
|
20304
|
+
site: resolvedSite,
|
|
20305
|
+
region,
|
|
20306
|
+
...(ambiguous ? { ambiguous: true } : {}),
|
|
20307
|
+
};
|
|
20308
|
+
}
|
|
20309
|
+
|
|
20310
|
+
|
|
19660
20311
|
/***/ }),
|
|
19661
20312
|
|
|
19662
20313
|
/***/ 8116:
|
|
@@ -19879,418 +20530,38 @@ function listLikelyRedeployFields(config) {
|
|
|
19879
20530
|
|
|
19880
20531
|
/***/ }),
|
|
19881
20532
|
|
|
19882
|
-
/***/
|
|
19883
|
-
/***/ (
|
|
20533
|
+
/***/ 8445:
|
|
20534
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
19884
20535
|
|
|
19885
20536
|
|
|
19886
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19887
|
-
if (k2 === undefined) k2 = k;
|
|
19888
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19889
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19890
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19891
|
-
}
|
|
19892
|
-
Object.defineProperty(o, k2, desc);
|
|
19893
|
-
}) : (function(o, m, k, k2) {
|
|
19894
|
-
if (k2 === undefined) k2 = k;
|
|
19895
|
-
o[k2] = m[k];
|
|
19896
|
-
}));
|
|
19897
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19898
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19899
|
-
}) : function(o, v) {
|
|
19900
|
-
o["default"] = v;
|
|
19901
|
-
});
|
|
19902
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19903
|
-
var ownKeys = function(o) {
|
|
19904
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
19905
|
-
var ar = [];
|
|
19906
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
19907
|
-
return ar;
|
|
19908
|
-
};
|
|
19909
|
-
return ownKeys(o);
|
|
19910
|
-
};
|
|
19911
|
-
return function (mod) {
|
|
19912
|
-
if (mod && mod.__esModule) return mod;
|
|
19913
|
-
var result = {};
|
|
19914
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
19915
|
-
__setModuleDefault(result, mod);
|
|
19916
|
-
return result;
|
|
19917
|
-
};
|
|
19918
|
-
})();
|
|
19919
20537
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
19920
|
-
exports.
|
|
19921
|
-
const
|
|
19922
|
-
const
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
const
|
|
19933
|
-
const
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
"image/",
|
|
19937
|
-
"application/json",
|
|
19938
|
-
"application/xml",
|
|
19939
|
-
"application/pdf",
|
|
19940
|
-
"application/zip",
|
|
19941
|
-
"application/x-zip-compressed"
|
|
19942
|
-
];
|
|
19943
|
-
// 获取项目根目录
|
|
19944
|
-
function getProjectRoot() {
|
|
19945
|
-
// 优先级:环境变量 > 当前工作目录
|
|
19946
|
-
return process.env.WORKSPACE_FOLDER_PATHS ||
|
|
19947
|
-
process.env.PROJECT_ROOT ||
|
|
19948
|
-
process.env.GITHUB_WORKSPACE ||
|
|
19949
|
-
process.env.CI_PROJECT_DIR ||
|
|
19950
|
-
process.env.BUILD_SOURCESDIRECTORY ||
|
|
19951
|
-
process.cwd();
|
|
19952
|
-
}
|
|
19953
|
-
// 验证相对路径是否安全(不允许路径遍历)
|
|
19954
|
-
function isPathSafe(relativePath) {
|
|
19955
|
-
// 检查是否包含路径遍历操作
|
|
19956
|
-
if (relativePath.includes('..') ||
|
|
19957
|
-
relativePath.includes('~') ||
|
|
19958
|
-
path.isAbsolute(relativePath)) {
|
|
19959
|
-
return false;
|
|
19960
|
-
}
|
|
19961
|
-
// 检查路径是否规范化后仍然安全
|
|
19962
|
-
const normalizedPath = path.normalize(relativePath);
|
|
19963
|
-
if (normalizedPath.startsWith('..') ||
|
|
19964
|
-
normalizedPath.startsWith('/') ||
|
|
19965
|
-
normalizedPath.startsWith('\\')) {
|
|
19966
|
-
return false;
|
|
19967
|
-
}
|
|
19968
|
-
return true;
|
|
19969
|
-
}
|
|
19970
|
-
// 计算最终下载路径
|
|
19971
|
-
function calculateDownloadPath(relativePath) {
|
|
19972
|
-
const projectRoot = getProjectRoot();
|
|
19973
|
-
const finalPath = path.join(projectRoot, relativePath);
|
|
19974
|
-
// 确保最终路径在项目根目录内
|
|
19975
|
-
const normalizedProjectRoot = path.resolve(projectRoot);
|
|
19976
|
-
const normalizedFinalPath = path.resolve(finalPath);
|
|
19977
|
-
if (!normalizedFinalPath.startsWith(normalizedProjectRoot)) {
|
|
19978
|
-
throw new Error('相对路径超出项目根目录范围');
|
|
19979
|
-
}
|
|
19980
|
-
return finalPath;
|
|
19981
|
-
}
|
|
19982
|
-
// 生成随机文件名
|
|
19983
|
-
function generateRandomFileName(extension = '') {
|
|
19984
|
-
const randomBytes = crypto.randomBytes(16);
|
|
19985
|
-
const fileName = randomBytes.toString('hex');
|
|
19986
|
-
return `${fileName}${extension}`;
|
|
19987
|
-
}
|
|
19988
|
-
// 获取安全的临时文件路径
|
|
19989
|
-
function getSafeTempFilePath(fileName) {
|
|
19990
|
-
return path.join(os.tmpdir(), fileName);
|
|
19991
|
-
}
|
|
19992
|
-
// 从 URL 或 Content-Disposition 获取文件扩展名
|
|
19993
|
-
function getFileExtension(url, contentType, contentDisposition) {
|
|
19994
|
-
let extension = "";
|
|
19995
|
-
// 从 URL 获取扩展名
|
|
19996
|
-
const urlPath = new url_1.URL(url).pathname;
|
|
19997
|
-
const urlExt = path.extname(urlPath);
|
|
19998
|
-
if (urlExt) {
|
|
19999
|
-
extension = urlExt;
|
|
20000
|
-
}
|
|
20001
|
-
// 从 Content-Disposition 获取扩展名
|
|
20002
|
-
if (contentDisposition) {
|
|
20003
|
-
const filenameMatch = contentDisposition.match(/filename=["']?([^"']+)["']?/);
|
|
20004
|
-
if (filenameMatch) {
|
|
20005
|
-
const dispositionExt = path.extname(filenameMatch[1]);
|
|
20006
|
-
if (dispositionExt) {
|
|
20007
|
-
extension = dispositionExt;
|
|
20008
|
-
}
|
|
20538
|
+
exports.readProjectConfig = readProjectConfig;
|
|
20539
|
+
const node_fs_1 = __webpack_require__(9627);
|
|
20540
|
+
const node_path_1 = __webpack_require__(5663);
|
|
20541
|
+
/**
|
|
20542
|
+
* 读取项目级配置 `.cloudbase/project.json`。
|
|
20543
|
+
*
|
|
20544
|
+
* 该文件是 site/region/envId 的可选持久化来源,让"打开项目即连对服务"
|
|
20545
|
+
* (对齐 Vercel `.vercel/project.json` 的三级回退)。读取失败/文件不存在
|
|
20546
|
+
* 返回 undefined,不阻塞调用方。
|
|
20547
|
+
*/
|
|
20548
|
+
function readProjectConfig(cwd) {
|
|
20549
|
+
try {
|
|
20550
|
+
const projectRoot = cwd ?? process.env.WORKSPACE_FOLDER_PATHS ?? process.cwd();
|
|
20551
|
+
const configPath = (0, node_path_1.join)(projectRoot, ".cloudbase", "project.json");
|
|
20552
|
+
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
20553
|
+
return undefined;
|
|
20009
20554
|
}
|
|
20555
|
+
const raw = (0, node_fs_1.readFileSync)(configPath, "utf-8");
|
|
20556
|
+
const parsed = JSON.parse(raw);
|
|
20557
|
+
if (!parsed || typeof parsed !== "object") {
|
|
20558
|
+
return undefined;
|
|
20559
|
+
}
|
|
20560
|
+
return parsed;
|
|
20010
20561
|
}
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
const mimeToExt = {
|
|
20014
|
-
"text/plain": ".txt",
|
|
20015
|
-
"text/html": ".html",
|
|
20016
|
-
"text/css": ".css",
|
|
20017
|
-
"text/javascript": ".js",
|
|
20018
|
-
"image/jpeg": ".jpg",
|
|
20019
|
-
"image/png": ".png",
|
|
20020
|
-
"image/gif": ".gif",
|
|
20021
|
-
"image/webp": ".webp",
|
|
20022
|
-
"application/json": ".json",
|
|
20023
|
-
"application/xml": ".xml",
|
|
20024
|
-
"application/pdf": ".pdf",
|
|
20025
|
-
"application/zip": ".zip",
|
|
20026
|
-
"application/x-zip-compressed": ".zip"
|
|
20027
|
-
};
|
|
20028
|
-
extension = mimeToExt[contentType] || "";
|
|
20029
|
-
}
|
|
20030
|
-
return extension;
|
|
20031
|
-
}
|
|
20032
|
-
function isAllowedContentType(contentType) {
|
|
20033
|
-
return ALLOWED_CONTENT_TYPES.some((allowedType) => contentType.startsWith(allowedType));
|
|
20034
|
-
}
|
|
20035
|
-
// 检查是否为可重试的网络错误
|
|
20036
|
-
function isRetryableError(error) {
|
|
20037
|
-
const retryableCodes = [
|
|
20038
|
-
'ECONNRESET',
|
|
20039
|
-
'ECONNREFUSED',
|
|
20040
|
-
'ETIMEDOUT',
|
|
20041
|
-
'ESOCKETTIMEDOUT',
|
|
20042
|
-
'ENOTFOUND',
|
|
20043
|
-
'EHOSTUNREACH',
|
|
20044
|
-
'ENETUNREACH',
|
|
20045
|
-
'ECONNABORTED'
|
|
20046
|
-
];
|
|
20047
|
-
return retryableCodes.includes(error.code || '');
|
|
20048
|
-
}
|
|
20049
|
-
// 下载文件到指定路径(带重试逻辑)
|
|
20050
|
-
async function downloadFileToPath(url, targetPath, retryCount = 0, redirectCount = 0) {
|
|
20051
|
-
const MAX_RETRIES = 3;
|
|
20052
|
-
const BASE_DELAY_MS = 1000; // 1秒基础延迟
|
|
20053
|
-
if (redirectCount > MAX_REDIRECT_HOPS) {
|
|
20054
|
-
throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
|
|
20055
|
-
}
|
|
20056
|
-
const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
|
|
20057
|
-
const client = requestUrl.protocol === "https:" ? https : http;
|
|
20058
|
-
return new Promise((resolve, reject) => {
|
|
20059
|
-
const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
|
|
20060
|
-
// 处理重定向
|
|
20061
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
20062
|
-
try {
|
|
20063
|
-
const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
|
|
20064
|
-
return resolve(await downloadFileToPath(redirectUrl, targetPath, retryCount, redirectCount + 1));
|
|
20065
|
-
}
|
|
20066
|
-
catch (error) {
|
|
20067
|
-
return reject(error);
|
|
20068
|
-
}
|
|
20069
|
-
}
|
|
20070
|
-
if (res.statusCode !== 200) {
|
|
20071
|
-
reject(new Error(`HTTP Error: ${res.statusCode}`));
|
|
20072
|
-
return;
|
|
20073
|
-
}
|
|
20074
|
-
const contentType = res.headers["content-type"] || "";
|
|
20075
|
-
const contentLength = parseInt(res.headers["content-length"] || "0", 10);
|
|
20076
|
-
if (!isAllowedContentType(contentType)) {
|
|
20077
|
-
reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
|
|
20078
|
-
return;
|
|
20079
|
-
}
|
|
20080
|
-
// 文件大小检查
|
|
20081
|
-
if (contentLength > MAX_FILE_SIZE) {
|
|
20082
|
-
reject(new Error(`文件大小 ${contentLength} 字节超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
20083
|
-
return;
|
|
20084
|
-
}
|
|
20085
|
-
// 确保目标目录存在
|
|
20086
|
-
const targetDir = path.dirname(targetPath);
|
|
20087
|
-
try {
|
|
20088
|
-
await fsPromises.mkdir(targetDir, { recursive: true });
|
|
20089
|
-
}
|
|
20090
|
-
catch (error) {
|
|
20091
|
-
reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : "未知错误"}`));
|
|
20092
|
-
return;
|
|
20093
|
-
}
|
|
20094
|
-
// 创建写入流
|
|
20095
|
-
const fileStream = fs.createWriteStream(targetPath);
|
|
20096
|
-
let downloadedSize = 0;
|
|
20097
|
-
res.on("data", (chunk) => {
|
|
20098
|
-
downloadedSize += chunk.length;
|
|
20099
|
-
if (downloadedSize > MAX_FILE_SIZE) {
|
|
20100
|
-
fileStream.destroy();
|
|
20101
|
-
fsPromises.unlink(targetPath).catch(() => { });
|
|
20102
|
-
reject(new Error(`文件大小超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
20103
|
-
}
|
|
20104
|
-
});
|
|
20105
|
-
res.pipe(fileStream);
|
|
20106
|
-
fileStream.on("finish", () => {
|
|
20107
|
-
resolve({
|
|
20108
|
-
filePath: targetPath,
|
|
20109
|
-
contentType,
|
|
20110
|
-
fileSize: downloadedSize,
|
|
20111
|
-
});
|
|
20112
|
-
});
|
|
20113
|
-
fileStream.on("error", (error) => {
|
|
20114
|
-
fsPromises.unlink(targetPath).catch(() => { });
|
|
20115
|
-
reject(error);
|
|
20116
|
-
});
|
|
20117
|
-
});
|
|
20118
|
-
request.on("error", async (error) => {
|
|
20119
|
-
// 检查是否可重试
|
|
20120
|
-
if (isRetryableError(error) && retryCount < MAX_RETRIES) {
|
|
20121
|
-
const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
|
|
20122
|
-
console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
20123
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
|
|
20124
|
-
try {
|
|
20125
|
-
return resolve(await downloadFileToPath(url, targetPath, retryCount + 1, redirectCount));
|
|
20126
|
-
}
|
|
20127
|
-
catch (retryError) {
|
|
20128
|
-
return reject(retryError);
|
|
20129
|
-
}
|
|
20130
|
-
}
|
|
20131
|
-
reject(error);
|
|
20132
|
-
});
|
|
20133
|
-
});
|
|
20134
|
-
}
|
|
20135
|
-
// 下载文件到临时目录(保持向后兼容,带重试逻辑)
|
|
20136
|
-
async function downloadFile(url, retryCount = 0, redirectCount = 0) {
|
|
20137
|
-
const MAX_RETRIES = 3;
|
|
20138
|
-
const BASE_DELAY_MS = 1000; // 1秒基础延迟
|
|
20139
|
-
if (redirectCount > MAX_REDIRECT_HOPS) {
|
|
20140
|
-
throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
|
|
20141
|
-
}
|
|
20142
|
-
const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
|
|
20143
|
-
const client = requestUrl.protocol === "https:" ? https : http;
|
|
20144
|
-
return new Promise((resolve, reject) => {
|
|
20145
|
-
const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
|
|
20146
|
-
// 处理重定向
|
|
20147
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
20148
|
-
try {
|
|
20149
|
-
const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
|
|
20150
|
-
return resolve(await downloadFile(redirectUrl, retryCount, redirectCount + 1));
|
|
20151
|
-
}
|
|
20152
|
-
catch (error) {
|
|
20153
|
-
return reject(error);
|
|
20154
|
-
}
|
|
20155
|
-
}
|
|
20156
|
-
if (res.statusCode !== 200) {
|
|
20157
|
-
reject(new Error(`HTTP Error: ${res.statusCode}`));
|
|
20158
|
-
return;
|
|
20159
|
-
}
|
|
20160
|
-
const contentType = res.headers["content-type"] || "";
|
|
20161
|
-
const contentLength = parseInt(res.headers["content-length"] || "0", 10);
|
|
20162
|
-
const contentDisposition = res.headers["content-disposition"];
|
|
20163
|
-
if (!isAllowedContentType(contentType)) {
|
|
20164
|
-
reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
|
|
20165
|
-
return;
|
|
20166
|
-
}
|
|
20167
|
-
// 文件大小检查
|
|
20168
|
-
if (contentLength > MAX_FILE_SIZE) {
|
|
20169
|
-
reject(new Error(`文件大小 ${contentLength} 字节超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
20170
|
-
return;
|
|
20171
|
-
}
|
|
20172
|
-
// 生成临时文件路径
|
|
20173
|
-
const extension = getFileExtension(requestUrl.toString(), contentType, contentDisposition);
|
|
20174
|
-
const fileName = generateRandomFileName(extension);
|
|
20175
|
-
const filePath = getSafeTempFilePath(fileName);
|
|
20176
|
-
// 创建写入流
|
|
20177
|
-
const fileStream = fs.createWriteStream(filePath);
|
|
20178
|
-
let downloadedSize = 0;
|
|
20179
|
-
res.on("data", (chunk) => {
|
|
20180
|
-
downloadedSize += chunk.length;
|
|
20181
|
-
if (downloadedSize > MAX_FILE_SIZE) {
|
|
20182
|
-
fileStream.destroy();
|
|
20183
|
-
fsPromises.unlink(filePath).catch(() => { });
|
|
20184
|
-
reject(new Error(`文件大小超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
20185
|
-
}
|
|
20186
|
-
});
|
|
20187
|
-
res.pipe(fileStream);
|
|
20188
|
-
fileStream.on("finish", () => {
|
|
20189
|
-
resolve({
|
|
20190
|
-
filePath,
|
|
20191
|
-
contentType,
|
|
20192
|
-
fileSize: downloadedSize,
|
|
20193
|
-
});
|
|
20194
|
-
});
|
|
20195
|
-
fileStream.on("error", (error) => {
|
|
20196
|
-
fsPromises.unlink(filePath).catch(() => { });
|
|
20197
|
-
reject(error);
|
|
20198
|
-
});
|
|
20199
|
-
});
|
|
20200
|
-
request.on("error", async (error) => {
|
|
20201
|
-
// 检查是否可重试
|
|
20202
|
-
if (isRetryableError(error) && retryCount < MAX_RETRIES) {
|
|
20203
|
-
const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
|
|
20204
|
-
console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
20205
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
|
|
20206
|
-
try {
|
|
20207
|
-
return resolve(await downloadFile(url, retryCount + 1, redirectCount));
|
|
20208
|
-
}
|
|
20209
|
-
catch (retryError) {
|
|
20210
|
-
return reject(retryError);
|
|
20211
|
-
}
|
|
20212
|
-
}
|
|
20213
|
-
reject(error);
|
|
20214
|
-
});
|
|
20215
|
-
});
|
|
20216
|
-
}
|
|
20217
|
-
function buildDownloadRemoteFileErrorMessage(url, error) {
|
|
20218
|
-
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
20219
|
-
const suggestions = [];
|
|
20220
|
-
if (/HTTP Error:\s*404/i.test(baseMessage)) {
|
|
20221
|
-
suggestions.push("目标 URL 当前返回 404,说明资源链接已失效或路径不正确。");
|
|
20222
|
-
suggestions.push("请先在浏览器中确认该链接可访问,再重新执行下载。");
|
|
20223
|
-
}
|
|
20224
|
-
if (/ENOTFOUND|ECONNRESET|ETIMEDOUT|socket hang up/i.test(baseMessage)) {
|
|
20225
|
-
suggestions.push("下载过程中出现网络异常,系统已自动重试 3 次但均失败。");
|
|
20226
|
-
suggestions.push("建议:1) 检查本地网络或代理设置;2) 该网站可能限频,请尝试更换其他图片源(如 Unsplash、Pexels);3) 稍后重试。");
|
|
20227
|
-
}
|
|
20228
|
-
if (suggestions.length === 0) {
|
|
20229
|
-
suggestions.push("请检查 URL 是否可访问,以及目标资源是否允许直接下载。");
|
|
20562
|
+
catch {
|
|
20563
|
+
return undefined;
|
|
20230
20564
|
}
|
|
20231
|
-
return `[downloadRemoteFile] ${baseMessage}\nURL: ${url}\n建议:${suggestions.join(" ")}`;
|
|
20232
|
-
}
|
|
20233
|
-
function registerDownloadTools(server) {
|
|
20234
|
-
server.registerTool("downloadRemoteFile", {
|
|
20235
|
-
title: "下载远程文件到指定路径",
|
|
20236
|
-
description: "下载远程文件到项目根目录下的指定相对路径。例如:小程序的 Tabbar 等素材图片,必须使用 **png** 格式,可以从 Unsplash、wikimedia【一般选用 500 大小即可、Pexels、Apple 官方 UI 等资源中选择来下载。",
|
|
20237
|
-
inputSchema: {
|
|
20238
|
-
url: zod_1.z.string().describe("远程文件的 URL 地址"),
|
|
20239
|
-
relativePath: zod_1.z.string().describe("相对于项目根目录的路径,例如:'assets/images/logo.png' 或 'docs/api.md'。不允许使用 ../ 等路径遍历操作。")
|
|
20240
|
-
},
|
|
20241
|
-
annotations: {
|
|
20242
|
-
readOnlyHint: false,
|
|
20243
|
-
destructiveHint: false,
|
|
20244
|
-
idempotentHint: false,
|
|
20245
|
-
openWorldHint: true,
|
|
20246
|
-
category: "download"
|
|
20247
|
-
}
|
|
20248
|
-
}, async ({ url, relativePath }) => {
|
|
20249
|
-
if (!isPathSafe(relativePath)) {
|
|
20250
|
-
return {
|
|
20251
|
-
content: [
|
|
20252
|
-
{
|
|
20253
|
-
type: "text",
|
|
20254
|
-
text: JSON.stringify({
|
|
20255
|
-
success: false,
|
|
20256
|
-
error: "不安全的相对路径",
|
|
20257
|
-
message: "相对路径包含路径遍历操作(../)或绝对路径,出于安全考虑已拒绝",
|
|
20258
|
-
suggestion: "请使用项目根目录下的相对路径,例如:'assets/images/logo.png'"
|
|
20259
|
-
}, null, 2)
|
|
20260
|
-
}
|
|
20261
|
-
]
|
|
20262
|
-
};
|
|
20263
|
-
}
|
|
20264
|
-
const targetPath = calculateDownloadPath(relativePath);
|
|
20265
|
-
const projectRoot = getProjectRoot();
|
|
20266
|
-
console.log(`📁 项目根目录: ${projectRoot}`);
|
|
20267
|
-
console.log(`📁 相对路径: ${relativePath}`);
|
|
20268
|
-
console.log(`📁 最终路径: ${targetPath}`);
|
|
20269
|
-
let result;
|
|
20270
|
-
try {
|
|
20271
|
-
result = await downloadFileToPath(url, targetPath);
|
|
20272
|
-
}
|
|
20273
|
-
catch (error) {
|
|
20274
|
-
throw new Error(buildDownloadRemoteFileErrorMessage(url, error));
|
|
20275
|
-
}
|
|
20276
|
-
return {
|
|
20277
|
-
content: [
|
|
20278
|
-
{
|
|
20279
|
-
type: "text",
|
|
20280
|
-
text: JSON.stringify({
|
|
20281
|
-
success: true,
|
|
20282
|
-
filePath: result.filePath,
|
|
20283
|
-
relativePath: relativePath,
|
|
20284
|
-
contentType: result.contentType,
|
|
20285
|
-
fileSize: result.fileSize,
|
|
20286
|
-
projectRoot: projectRoot,
|
|
20287
|
-
message: "文件下载成功到指定路径",
|
|
20288
|
-
note: `文件已保存到项目目录: ${relativePath}`
|
|
20289
|
-
}, null, 2)
|
|
20290
|
-
}
|
|
20291
|
-
]
|
|
20292
|
-
};
|
|
20293
|
-
});
|
|
20294
20565
|
}
|
|
20295
20566
|
|
|
20296
20567
|
|
|
@@ -20985,6 +21256,13 @@ function registerAgentTools(server) {
|
|
|
20985
21256
|
}
|
|
20986
21257
|
|
|
20987
21258
|
|
|
21259
|
+
/***/ }),
|
|
21260
|
+
|
|
21261
|
+
/***/ 9627:
|
|
21262
|
+
/***/ ((module) => {
|
|
21263
|
+
|
|
21264
|
+
module.exports = __WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__;
|
|
21265
|
+
|
|
20988
21266
|
/***/ }),
|
|
20989
21267
|
|
|
20990
21268
|
/***/ 9684:
|
|
@@ -21059,7 +21337,6 @@ function shouldRegisterTool(toolName) {
|
|
|
21059
21337
|
'createFunction', // also involves local files
|
|
21060
21338
|
// Download tools - local file downloads
|
|
21061
21339
|
'downloadTemplate',
|
|
21062
|
-
'downloadRemoteFile',
|
|
21063
21340
|
// Setup tools - local config file operations
|
|
21064
21341
|
'setupEnvironmentId',
|
|
21065
21342
|
// CloudRun tools - local file operations
|