@cloudbase/cloudbase-mcp 2.21.1 → 2.22.0-beta.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/dist/508.index.cjs +21 -21
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +2528 -677
- package/dist/index.d.ts +133 -1
- package/dist/index.js +2550 -710
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -1914,17 +1914,17 @@ function buildEnvDomainManagementResult(params) {
|
|
|
1914
1914
|
operation: action,
|
|
1915
1915
|
targetDomains: domains,
|
|
1916
1916
|
asyncState: "PENDING",
|
|
1917
|
-
message: '安全域名已提交添加请求。该变更通常需要约 10 分钟传播,请继续轮询
|
|
1917
|
+
message: '安全域名已提交添加请求。该变更通常需要约 10 分钟传播,请继续轮询 queryEnv(action="domains"),直到目标域名状态为 ENABLE。',
|
|
1918
1918
|
propagation: {
|
|
1919
1919
|
requiresPolling: true,
|
|
1920
|
-
pollTool: "
|
|
1920
|
+
pollTool: "queryEnv",
|
|
1921
1921
|
pollAction: "domains",
|
|
1922
1922
|
pollIntervalSuggestionSeconds: 10,
|
|
1923
1923
|
timeoutSuggestionSeconds: 600,
|
|
1924
|
-
successCondition: '目标域名出现在
|
|
1924
|
+
successCondition: '目标域名出现在 queryEnv(action="domains") 返回中,且 Status 为 ENABLE。',
|
|
1925
1925
|
},
|
|
1926
1926
|
next_step: {
|
|
1927
|
-
tool: "
|
|
1927
|
+
tool: "queryEnv",
|
|
1928
1928
|
action: "domains",
|
|
1929
1929
|
suggested_args: {
|
|
1930
1930
|
action: "domains",
|
|
@@ -1939,17 +1939,17 @@ function buildEnvDomainManagementResult(params) {
|
|
|
1939
1939
|
operation: action,
|
|
1940
1940
|
targetDomains: domains,
|
|
1941
1941
|
asyncState: "PENDING",
|
|
1942
|
-
message: '安全域名已提交删除请求。该变更可能需要数分钟传播,请继续轮询
|
|
1942
|
+
message: '安全域名已提交删除请求。该变更可能需要数分钟传播,请继续轮询 queryEnv(action="domains"),直到目标域名不再出现。',
|
|
1943
1943
|
propagation: {
|
|
1944
1944
|
requiresPolling: true,
|
|
1945
|
-
pollTool: "
|
|
1945
|
+
pollTool: "queryEnv",
|
|
1946
1946
|
pollAction: "domains",
|
|
1947
1947
|
pollIntervalSuggestionSeconds: 10,
|
|
1948
1948
|
timeoutSuggestionSeconds: 600,
|
|
1949
|
-
successCondition: '目标域名不再出现在
|
|
1949
|
+
successCondition: '目标域名不再出现在 queryEnv(action="domains") 返回中。',
|
|
1950
1950
|
},
|
|
1951
1951
|
next_step: {
|
|
1952
|
-
tool: "
|
|
1952
|
+
tool: "queryEnv",
|
|
1953
1953
|
action: "domains",
|
|
1954
1954
|
suggested_args: {
|
|
1955
1955
|
action: "domains",
|
|
@@ -2223,7 +2223,7 @@ function buildEnvQueryListResult(params) {
|
|
|
2223
2223
|
const paginated = paginateEnvList(filteredList, params.filters.offset, params.filters.limit);
|
|
2224
2224
|
const exactEnvIdSummaryHint = params.filters.envId
|
|
2225
2225
|
? {
|
|
2226
|
-
tool: "
|
|
2226
|
+
tool: "queryEnv",
|
|
2227
2227
|
action: "info",
|
|
2228
2228
|
reason: "action=list with envId only returns a concise summary. Use action=info to fetch detailed environment information such as full resource metadata and additional environment details.",
|
|
2229
2229
|
}
|
|
@@ -2288,13 +2288,122 @@ async function enrichEnvInfoWithBilling(params) {
|
|
|
2288
2288
|
return params.result;
|
|
2289
2289
|
}
|
|
2290
2290
|
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Derive a RuntimeMode hint from the EnvInfo payload so the AI agent can
|
|
2293
|
+
* immediately tell which CloudBase data backends are actually present in
|
|
2294
|
+
* this environment.
|
|
2295
|
+
*
|
|
2296
|
+
* Reality model (revised after observing a real PG environment): a single
|
|
2297
|
+
* CloudBase environment can have any combination of:
|
|
2298
|
+
* - PostgreSQL (CloudBase PG / pgstore): signaled by `EnvInfo.PostgreSQL[]`
|
|
2299
|
+
* non-empty and/or `EnvInfo.Meta` containing `postgresql=enable`.
|
|
2300
|
+
* - NoSQL document database + the matching legacy COS-style bucket:
|
|
2301
|
+
* signaled by `EnvInfo.Databases[]` (with InstanceId / RUNNING) and the
|
|
2302
|
+
* bucket exposed in `EnvInfo.Storages[]`.
|
|
2303
|
+
* - MySQL: signaled by a non-empty `EnvInfo.MysqlInstances[]` (or similar
|
|
2304
|
+
* field, name varies). In a pure PG environment this is absent.
|
|
2305
|
+
*
|
|
2306
|
+
* In particular: a CloudBase PG environment commonly STILL has the NoSQL
|
|
2307
|
+
* Database + Storage running in parallel. NoSQL is therefore "co-present"
|
|
2308
|
+
* — its collection APIs, NoSQL `securityRule`s, and the legacy
|
|
2309
|
+
* `app.uploadFile()` upload flow remain valid for collections / files
|
|
2310
|
+
* that already live there. The thing that is NOT a substitute for the new
|
|
2311
|
+
* PG surface is using NoSQL collections to model a brand-new business
|
|
2312
|
+
* table that the task explicitly puts in PG.
|
|
2313
|
+
*
|
|
2314
|
+
* What this enrichment does:
|
|
2315
|
+
* - Adds `EnvInfo.RuntimeMode = "postgresql" | "nosql"` based on whether
|
|
2316
|
+
* PG is present. This is the recommended primary backend for new
|
|
2317
|
+
* business data when set to "postgresql".
|
|
2318
|
+
* - Adds `EnvInfo.RuntimeBackends`, a structured snapshot of which
|
|
2319
|
+
* backends are actually available (postgresql / nosql / mysql), so the
|
|
2320
|
+
* agent does not have to re-read `Databases`/`Storages`/`PostgreSQL`.
|
|
2321
|
+
* - Adds `EnvInfo.RuntimeModeHints` summarizing which API/tool to prefer
|
|
2322
|
+
* for new code, including an explicit `MysqlNotAvailable` line when
|
|
2323
|
+
* MySQL is absent — that one IS a hard "do not use" signal.
|
|
2324
|
+
*/
|
|
2325
|
+
function enrichEnvInfoWithRuntimeMode(result) {
|
|
2326
|
+
const envInfo = result?.EnvInfo;
|
|
2327
|
+
if (!envInfo || typeof envInfo !== "object") {
|
|
2328
|
+
return result;
|
|
2329
|
+
}
|
|
2330
|
+
const pgList = Array.isArray(envInfo.PostgreSQL) ? envInfo.PostgreSQL : [];
|
|
2331
|
+
const metaList = Array.isArray(envInfo.Meta) ? envInfo.Meta : [];
|
|
2332
|
+
const metaPostgresEnabled = metaList.some((item) => item &&
|
|
2333
|
+
typeof item.Key === "string" &&
|
|
2334
|
+
/^postgre[_]?sql$/i.test(item.Key) &&
|
|
2335
|
+
String(item.Value).toLowerCase() === "enable");
|
|
2336
|
+
const hasPostgresql = pgList.length > 0 || metaPostgresEnabled;
|
|
2337
|
+
// NoSQL document DB shows up under Databases[]; the matching legacy bucket
|
|
2338
|
+
// shows up under Storages[]. Treat NoSQL as available when either side is
|
|
2339
|
+
// non-empty (an old env may keep one without the other).
|
|
2340
|
+
const databasesList = Array.isArray(envInfo.Databases)
|
|
2341
|
+
? envInfo.Databases
|
|
2342
|
+
: [];
|
|
2343
|
+
const storagesList = Array.isArray(envInfo.Storages) ? envInfo.Storages : [];
|
|
2344
|
+
const hasNoSql = databasesList.length > 0 || storagesList.length > 0;
|
|
2345
|
+
// MySQL field name has been seen as MysqlInstances / MySQLInstances /
|
|
2346
|
+
// MySQL across API versions; check any of them.
|
|
2347
|
+
const mysqlList = (() => {
|
|
2348
|
+
for (const k of ["MysqlInstances", "MySQLInstances", "MySQL"]) {
|
|
2349
|
+
const v = envInfo[k];
|
|
2350
|
+
if (Array.isArray(v) && v.length > 0)
|
|
2351
|
+
return v;
|
|
2352
|
+
}
|
|
2353
|
+
return [];
|
|
2354
|
+
})();
|
|
2355
|
+
const hasMysql = mysqlList.length > 0;
|
|
2356
|
+
// Primary mode: prefer PG when it is provisioned, otherwise fall back to
|
|
2357
|
+
// legacy NoSQL labeling. This drives "what skill to read first / what API
|
|
2358
|
+
// to reach for when starting new business code".
|
|
2359
|
+
const runtimeMode = hasPostgresql
|
|
2360
|
+
? "postgresql"
|
|
2361
|
+
: "nosql";
|
|
2362
|
+
const hints = hasPostgresql
|
|
2363
|
+
? {
|
|
2364
|
+
PrimaryBackend: "PostgreSQL (CloudBase PG) is provisioned — prefer it for any NEW business data the task introduces.",
|
|
2365
|
+
BusinessDataAPI: "For NEW business data on PG: use CloudBase JS SDK v3 `app.rdb()` (Supabase-style chained query). Existing NoSQL collections in this env keep working through `app.database()`; do not migrate them unless the task asks.",
|
|
2366
|
+
Permissions: "PG table permissions use Row-Level Security. Run `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` and `CREATE POLICY` via `managePgDatabase(action=\"execute\", confirm=true)`. `managePermissions(resourceType=\"noSqlDatabase\", securityRule=...)` only governs NoSQL collection rules and has NO effect on PG tables — keep using it for the NoSQL collections that already exist here.",
|
|
2367
|
+
Storage: "PG-mode browser uploads should use `app.storage.from().upload(<bucket>/<key>, file)` against an explicitly-created `pgstore` bucket (same model as Supabase Storage; the v3 SDK does not auto-create one). `EnvInfo.Storages[]` here is the legacy NoSQL bucket — it is still usable for the legacy `app.uploadFile()` flow but is NOT a valid pgstore target.",
|
|
2368
|
+
CoexistingNoSQL: hasNoSql
|
|
2369
|
+
? "This env also has the legacy NoSQL Database + Storage running. Existing collections, existing `app.uploadFile()` calls, existing `managePermissions(resourceType=\"noSqlDatabase\")` rules all remain valid for legacy data."
|
|
2370
|
+
: "No legacy NoSQL Database/Storage observed in this env.",
|
|
2371
|
+
MysqlNotAvailable: hasMysql
|
|
2372
|
+
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
2373
|
+
: "No MySQL instance is provisioned for this env. Do NOT use `manageSqlDatabase` / `querySqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-tool` skill — that family targets MySQL, not CloudBase PG.",
|
|
2374
|
+
RecommendedSkills: "Read `postgresql-development` first for new PG code. `no-sql-web-sdk` is still applicable for existing NoSQL collections in this env. Skip `relational-database-tool` (MySQL-only) entirely.",
|
|
2375
|
+
}
|
|
2376
|
+
: {
|
|
2377
|
+
PrimaryBackend: "PostgreSQL is NOT provisioned in this env — this is a legacy NoSQL CloudBase backend.",
|
|
2378
|
+
BusinessDataAPI: "Use `app.database()` collections via `@cloudbase/js-sdk` for browser business data. Do not switch to `app.rdb()` here — PG is not available.",
|
|
2379
|
+
Permissions: "Use `managePermissions(resourceType=\"noSqlDatabase\", securityRule=...)` for collection rules. PG-only RLS guidance (e.g. `auth.uid()` SQL policies) does not apply here.",
|
|
2380
|
+
Storage: "Browser uploads use `app.uploadFile()` against the bucket exposed in `EnvInfo.Storages[].Bucket`.",
|
|
2381
|
+
MysqlNotAvailable: hasMysql
|
|
2382
|
+
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
2383
|
+
: "No MySQL instance in this env. `manageSqlDatabase` / `querySqlDatabase` and the `relational-database-tool` skill are not applicable.",
|
|
2384
|
+
RecommendedSkills: "Read `no-sql-web-sdk` (and `cloud-storage-web` for uploads). `postgresql-development` and `relational-database-tool` are not applicable to this env.",
|
|
2385
|
+
};
|
|
2386
|
+
return {
|
|
2387
|
+
...result,
|
|
2388
|
+
EnvInfo: {
|
|
2389
|
+
...envInfo,
|
|
2390
|
+
RuntimeMode: runtimeMode,
|
|
2391
|
+
RuntimeBackends: {
|
|
2392
|
+
postgresql: hasPostgresql,
|
|
2393
|
+
nosql: hasNoSql,
|
|
2394
|
+
mysql: hasMysql,
|
|
2395
|
+
},
|
|
2396
|
+
RuntimeModeHints: hints,
|
|
2397
|
+
},
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2291
2400
|
function normalizeOptionalToolString(value) {
|
|
2292
2401
|
return typeof value === "string" && value.trim().length > 0
|
|
2293
2402
|
? value.trim()
|
|
2294
2403
|
: undefined;
|
|
2295
2404
|
}
|
|
2296
2405
|
/**
|
|
2297
|
-
* Build enhanced error message for
|
|
2406
|
+
* Build enhanced error message for queryEnv tool errors
|
|
2298
2407
|
* Provides actionable guidance based on error patterns
|
|
2299
2408
|
*/
|
|
2300
2409
|
function buildEnvQueryErrorMessage(error, action) {
|
|
@@ -2310,7 +2419,7 @@ function buildEnvQueryErrorMessage(error, action) {
|
|
|
2310
2419
|
suggestions.push("参数错误:可能是认证信息无效或已过期,请尝试以下步骤:");
|
|
2311
2420
|
suggestions.push("1. 先调用 auth(action=\"status\") 检查当前登录状态");
|
|
2312
2421
|
suggestions.push("2. 如果未登录,调用 auth(action=\"start_auth\", authMode=\"device\") 完成登录");
|
|
2313
|
-
suggestions.push("3. 登录完成后再次调用
|
|
2422
|
+
suggestions.push("3. 登录完成后再次调用 queryEnv(action=\"list\")");
|
|
2314
2423
|
}
|
|
2315
2424
|
if (hasAuthError) {
|
|
2316
2425
|
suggestions.push("认证错误:当前未登录或认证已过期。");
|
|
@@ -2322,7 +2431,7 @@ function buildEnvQueryErrorMessage(error, action) {
|
|
|
2322
2431
|
}
|
|
2323
2432
|
if (hasEnvNotFoundError) {
|
|
2324
2433
|
suggestions.push("环境错误:指定的环境不存在或无法访问。");
|
|
2325
|
-
suggestions.push("请使用
|
|
2434
|
+
suggestions.push("请使用 queryEnv(action=\"list\") 查看可用的环境列表。");
|
|
2326
2435
|
}
|
|
2327
2436
|
if (hasNetworkError) {
|
|
2328
2437
|
suggestions.push("网络错误:请检查网络连接,稍后重试。");
|
|
@@ -2334,7 +2443,7 @@ function buildEnvQueryErrorMessage(error, action) {
|
|
|
2334
2443
|
suggestions.push("2. 如未登录,执行 auth(action=\"start_auth\") 完成认证");
|
|
2335
2444
|
suggestions.push("3. 确认环境 ID 正确且可访问");
|
|
2336
2445
|
}
|
|
2337
|
-
return `[
|
|
2446
|
+
return `[queryEnv/${action}] 调用失败: ${baseMessage}\n\n解决建议:\n${suggestions.join("\n")}`;
|
|
2338
2447
|
}
|
|
2339
2448
|
function normalizeOptionalToolBoolean(value) {
|
|
2340
2449
|
return typeof value === "boolean" ? value : undefined;
|
|
@@ -2363,519 +2472,500 @@ function registerEnvTools(server) {
|
|
|
2363
2472
|
const supportedAuthActions = getSupportedAuthActions(server);
|
|
2364
2473
|
const authActionEnum = [...supportedAuthActions];
|
|
2365
2474
|
// auth - CloudBase (云开发) 开发阶段登录与环境绑定
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
.
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
.
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
.
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
.
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
.
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
envId: zod_1.z
|
|
2395
|
-
.string()
|
|
2396
|
-
.optional()
|
|
2397
|
-
.describe("环境ID(CloudBase 环境唯一标识),绑定后工具将操作该环境。action=set_env 时必填"),
|
|
2398
|
-
...(supportedAuthActions.includes("logout")
|
|
2399
|
-
? {
|
|
2400
|
-
confirm: zod_1.z
|
|
2401
|
-
.literal("yes")
|
|
2402
|
-
.optional()
|
|
2403
|
-
.describe("action=logout 时确认操作,传 yes"),
|
|
2404
|
-
}
|
|
2405
|
-
: {}),
|
|
2406
|
-
...(supportedAuthActions.includes("get_temp_credentials")
|
|
2407
|
-
? {
|
|
2408
|
-
reveal: zod_1.z
|
|
2409
|
-
.boolean()
|
|
2410
|
-
.optional()
|
|
2411
|
-
.describe("action=get_temp_credentials 时可选。true=返回明文临时密钥;默认 false 仅返回脱敏结果"),
|
|
2412
|
-
}
|
|
2413
|
-
: {}),
|
|
2414
|
-
},
|
|
2415
|
-
annotations: {
|
|
2416
|
-
readOnlyHint: false,
|
|
2417
|
-
destructiveHint: false,
|
|
2418
|
-
idempotentHint: false,
|
|
2419
|
-
openWorldHint: true,
|
|
2420
|
-
category: "env",
|
|
2421
|
-
},
|
|
2422
|
-
}, async (rawArgs) => {
|
|
2423
|
-
const action = rawArgs.action ?? "status";
|
|
2424
|
-
const authMode = rawArgs.authMode === "device" || rawArgs.authMode === "web"
|
|
2425
|
-
? rawArgs.authMode
|
|
2426
|
-
: undefined;
|
|
2427
|
-
const oauthEndpoint = normalizeOptionalToolString(rawArgs.oauthEndpoint);
|
|
2428
|
-
const clientId = normalizeOptionalToolString(rawArgs.clientId);
|
|
2429
|
-
const oauthCustom = normalizeOptionalToolBoolean(rawArgs.oauthCustom);
|
|
2430
|
-
const envId = rawArgs.envId;
|
|
2431
|
-
const confirm = rawArgs.confirm === "yes" ? "yes" : undefined;
|
|
2432
|
-
const reveal = normalizeOptionalToolBoolean(rawArgs.reveal) === true;
|
|
2433
|
-
const resolvedAuthOptions = resolveToolAuthOptions(server, {
|
|
2434
|
-
authMode,
|
|
2435
|
-
oauthEndpoint,
|
|
2436
|
-
clientId,
|
|
2437
|
-
oauthCustom,
|
|
2438
|
-
});
|
|
2439
|
-
const authConfigSummary = (0, auth_js_1.buildAuthConfigSummary)(resolvedAuthOptions);
|
|
2440
|
-
let deviceAuthInfo;
|
|
2441
|
-
const onDeviceCode = (info) => {
|
|
2442
|
-
deviceAuthInfo = info;
|
|
2443
|
-
(0, auth_js_1.setPendingAuthProgressState)(info, "device");
|
|
2444
|
-
// emitDeviceAuthNotice(server, info);
|
|
2445
|
-
};
|
|
2446
|
-
const authChallenge = () => (0, auth_js_1.buildDeviceAuthChallengePayload)(deviceAuthInfo);
|
|
2447
|
-
try {
|
|
2448
|
-
if (!supportedAuthActions.includes(action)) {
|
|
2449
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2450
|
-
ok: false,
|
|
2451
|
-
code: "NOT_SUPPORTED",
|
|
2452
|
-
message: `当前 IDE 不支持 auth(action="${action}")。`,
|
|
2453
|
-
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2454
|
-
suggestedArgs: { action: "status" },
|
|
2455
|
-
}),
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
if (action === "status") {
|
|
2459
|
-
const loginState = await (0, auth_js_1.peekLoginState)();
|
|
2460
|
-
const authFlowState = await (0, auth_js_1.getAuthProgressState)();
|
|
2461
|
-
// 判断是否为 API Key 模式
|
|
2462
|
-
const isApiKeyMode = !!(process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID);
|
|
2463
|
-
const authStatus = loginState
|
|
2464
|
-
? "READY"
|
|
2465
|
-
: authFlowState.status === "PENDING"
|
|
2466
|
-
? "PENDING"
|
|
2467
|
-
: "REQUIRED";
|
|
2468
|
-
let envPreparation;
|
|
2469
|
-
const message = authStatus === "READY"
|
|
2470
|
-
? undefined
|
|
2471
|
-
: authStatus === "PENDING"
|
|
2472
|
-
? "设备码授权进行中,请完成浏览器授权后再次调用 auth(action=\"status\")"
|
|
2473
|
-
: isCodeBuddyIde(server)
|
|
2474
|
-
? "当前未登录。CodeBuddy 暂不支持在 tool 内发起认证,请在外部完成认证后再次调用 auth(action=\"status\")。"
|
|
2475
|
-
: "当前未登录,请先执行 auth(action=\"start_auth\")";
|
|
2476
|
-
if (authStatus === "READY" && loginState) {
|
|
2477
|
-
envPreparation = await prepareAuthEnvironment({
|
|
2478
|
-
server,
|
|
2479
|
-
cloudBaseOptions,
|
|
2480
|
-
loginState,
|
|
2481
|
-
});
|
|
2482
|
-
}
|
|
2483
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2484
|
-
ok: true,
|
|
2485
|
-
code: "STATUS",
|
|
2486
|
-
auth_status: authStatus,
|
|
2487
|
-
...(isApiKeyMode ? { auth_mode: "api_key" } : {}),
|
|
2488
|
-
auth_config: authConfigSummary,
|
|
2489
|
-
...(envPreparation
|
|
2490
|
-
? buildAuthEnvSetupPayload(envPreparation)
|
|
2491
|
-
: {
|
|
2492
|
-
env_status: "NONE",
|
|
2493
|
-
current_env_id: null,
|
|
2494
|
-
...buildEnvCandidatePayload([]),
|
|
2495
|
-
}),
|
|
2496
|
-
auth_challenge: authFlowState.status === "PENDING"
|
|
2497
|
-
? (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge)
|
|
2498
|
-
: undefined,
|
|
2499
|
-
message,
|
|
2500
|
-
next_step: authStatus === "REQUIRED"
|
|
2501
|
-
? buildAuthRequiredNextStep(server)
|
|
2502
|
-
: authStatus === "PENDING"
|
|
2503
|
-
? (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2504
|
-
suggestedArgs: { action: "status" },
|
|
2505
|
-
})
|
|
2506
|
-
: envPreparation?.nextStep,
|
|
2507
|
-
});
|
|
2508
|
-
}
|
|
2509
|
-
if (action === "start_auth") {
|
|
2510
|
-
// API Key 模式:尝试换取临时密钥
|
|
2511
|
-
if (process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID) {
|
|
2512
|
-
try {
|
|
2513
|
-
const existingLoginState = await (0, auth_js_1.peekLoginState)();
|
|
2514
|
-
if (existingLoginState) {
|
|
2515
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2516
|
-
ok: true,
|
|
2517
|
-
code: "AUTH_READY",
|
|
2518
|
-
message: "当前使用 API Key 认证模式,已自动完成登录,无需手动授权。",
|
|
2519
|
-
auth_mode: "api_key",
|
|
2520
|
-
envId: process.env.CLOUDBASE_ENV_ID,
|
|
2521
|
-
});
|
|
2522
|
-
}
|
|
2475
|
+
// 微信 IDE 使用票据认证,不需要登录工具
|
|
2476
|
+
if (server.ide !== 'wxide') {
|
|
2477
|
+
server.registerTool?.("auth", {
|
|
2478
|
+
title: "CloudBase 开发阶段登录与环境",
|
|
2479
|
+
description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、绑定环境(set_env)、退出登录。",
|
|
2480
|
+
inputSchema: {
|
|
2481
|
+
action: zod_1.z
|
|
2482
|
+
.enum(authActionEnum)
|
|
2483
|
+
.optional()
|
|
2484
|
+
.describe("动作:status=查询状态,start_auth=发起登录,set_env=绑定环境(传envId),logout=退出登录"),
|
|
2485
|
+
...(supportedAuthActions.includes("start_auth")
|
|
2486
|
+
? {
|
|
2487
|
+
authMode: zod_1.z
|
|
2488
|
+
.enum(["device", "web"])
|
|
2489
|
+
.optional()
|
|
2490
|
+
.describe("认证模式:device=设备码授权,web=浏览器回调授权"),
|
|
2491
|
+
oauthEndpoint: zod_1.z
|
|
2492
|
+
.string()
|
|
2493
|
+
.optional()
|
|
2494
|
+
.describe("高级可选:自定义 device-code 登录 endpoint。配置后 oauthCustom 默认按 true 处理"),
|
|
2495
|
+
clientId: zod_1.z
|
|
2496
|
+
.string()
|
|
2497
|
+
.optional()
|
|
2498
|
+
.describe("高级可选:自定义 device-code 登录 client_id,不传则使用默认值"),
|
|
2499
|
+
oauthCustom: zod_1.z
|
|
2500
|
+
.boolean()
|
|
2501
|
+
.optional()
|
|
2502
|
+
.describe("高级可选:自定义 endpoint 返回格式开关。未配置 endpoint 时默认 false;配置 endpoint 后默认 true,且不能设为 false"),
|
|
2523
2503
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2504
|
+
: {}),
|
|
2505
|
+
envId: zod_1.z
|
|
2506
|
+
.string()
|
|
2507
|
+
.optional()
|
|
2508
|
+
.describe("环境ID(CloudBase 环境唯一标识),绑定后工具将操作该环境。action=set_env 时必填"),
|
|
2509
|
+
...(supportedAuthActions.includes("logout")
|
|
2510
|
+
? {
|
|
2511
|
+
confirm: zod_1.z
|
|
2512
|
+
.literal("yes")
|
|
2513
|
+
.optional()
|
|
2514
|
+
.describe("action=logout 时确认操作,传 yes"),
|
|
2527
2515
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2516
|
+
: {}),
|
|
2517
|
+
...(supportedAuthActions.includes("get_temp_credentials")
|
|
2518
|
+
? {
|
|
2519
|
+
reveal: zod_1.z
|
|
2520
|
+
.boolean()
|
|
2521
|
+
.optional()
|
|
2522
|
+
.describe("action=get_temp_credentials 时可选。true=返回明文临时密钥;默认 false 仅返回脱敏结果"),
|
|
2523
|
+
}
|
|
2524
|
+
: {}),
|
|
2525
|
+
},
|
|
2526
|
+
annotations: {
|
|
2527
|
+
readOnlyHint: false,
|
|
2528
|
+
destructiveHint: false,
|
|
2529
|
+
idempotentHint: false,
|
|
2530
|
+
openWorldHint: true,
|
|
2531
|
+
category: "env",
|
|
2532
|
+
},
|
|
2533
|
+
}, async (rawArgs) => {
|
|
2534
|
+
const action = rawArgs.action ?? "status";
|
|
2535
|
+
const authMode = rawArgs.authMode === "device" || rawArgs.authMode === "web"
|
|
2536
|
+
? rawArgs.authMode
|
|
2537
|
+
: undefined;
|
|
2538
|
+
const oauthEndpoint = normalizeOptionalToolString(rawArgs.oauthEndpoint);
|
|
2539
|
+
const clientId = normalizeOptionalToolString(rawArgs.clientId);
|
|
2540
|
+
const oauthCustom = normalizeOptionalToolBoolean(rawArgs.oauthCustom);
|
|
2541
|
+
const envId = rawArgs.envId;
|
|
2542
|
+
const confirm = rawArgs.confirm === "yes" ? "yes" : undefined;
|
|
2543
|
+
const reveal = normalizeOptionalToolBoolean(rawArgs.reveal) === true;
|
|
2544
|
+
const resolvedAuthOptions = resolveToolAuthOptions(server, {
|
|
2545
|
+
authMode,
|
|
2546
|
+
oauthEndpoint,
|
|
2547
|
+
clientId,
|
|
2548
|
+
oauthCustom,
|
|
2549
|
+
});
|
|
2550
|
+
const authConfigSummary = (0, auth_js_1.buildAuthConfigSummary)(resolvedAuthOptions);
|
|
2551
|
+
let deviceAuthInfo;
|
|
2552
|
+
const onDeviceCode = (info) => {
|
|
2553
|
+
deviceAuthInfo = info;
|
|
2554
|
+
(0, auth_js_1.setPendingAuthProgressState)(info, "device");
|
|
2555
|
+
// emitDeviceAuthNotice(server, info);
|
|
2556
|
+
};
|
|
2557
|
+
const authChallenge = () => (0, auth_js_1.buildDeviceAuthChallengePayload)(deviceAuthInfo);
|
|
2558
|
+
try {
|
|
2559
|
+
if (!supportedAuthActions.includes(action)) {
|
|
2541
2560
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2542
2561
|
ok: false,
|
|
2543
|
-
code: "
|
|
2544
|
-
message:
|
|
2545
|
-
auth_mode: "api_key",
|
|
2546
|
-
envId: process.env.CLOUDBASE_ENV_ID,
|
|
2547
|
-
endpoint,
|
|
2548
|
-
});
|
|
2549
|
-
}
|
|
2550
|
-
const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
|
|
2551
|
-
const auth = toolbox_1.AuthSupervisor.getInstance({});
|
|
2552
|
-
const authFlowState = await (0, auth_js_1.getAuthProgressState)();
|
|
2553
|
-
if (authFlowState.status === "PENDING" && authFlowState.authChallenge) {
|
|
2554
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2555
|
-
ok: true,
|
|
2556
|
-
code: "AUTH_PENDING",
|
|
2557
|
-
message: "设备码授权进行中,请在浏览器中打开 verification_uri 并输入 user_code 完成授权。",
|
|
2558
|
-
auth_challenge: (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge),
|
|
2562
|
+
code: "NOT_SUPPORTED",
|
|
2563
|
+
message: `当前 IDE 不支持 auth(action="${action}")。`,
|
|
2559
2564
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2560
2565
|
suggestedArgs: { action: "status" },
|
|
2561
2566
|
}),
|
|
2562
2567
|
});
|
|
2563
2568
|
}
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
const
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
+
if (action === "status") {
|
|
2570
|
+
const loginState = await (0, auth_js_1.peekLoginState)();
|
|
2571
|
+
const authFlowState = await (0, auth_js_1.getAuthProgressState)();
|
|
2572
|
+
// 判断是否为 API Key 模式
|
|
2573
|
+
const isApiKeyMode = !!(process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID);
|
|
2574
|
+
const authStatus = loginState
|
|
2575
|
+
? "READY"
|
|
2576
|
+
: authFlowState.status === "PENDING"
|
|
2577
|
+
? "PENDING"
|
|
2578
|
+
: "REQUIRED";
|
|
2579
|
+
let envPreparation;
|
|
2580
|
+
const message = authStatus === "READY"
|
|
2581
|
+
? undefined
|
|
2582
|
+
: authStatus === "PENDING"
|
|
2583
|
+
? "设备码授权进行中,请完成浏览器授权后再次调用 auth(action=\"status\")"
|
|
2584
|
+
: isCodeBuddyIde(server)
|
|
2585
|
+
? "当前未登录。CodeBuddy 暂不支持在 tool 内发起认证,请在外部完成认证后再次调用 auth(action=\"status\")。"
|
|
2586
|
+
: "当前未登录,请先执行 auth(action=\"start_auth\")";
|
|
2587
|
+
if (authStatus === "READY" && loginState) {
|
|
2588
|
+
envPreparation = await prepareAuthEnvironment({
|
|
2569
2589
|
server,
|
|
2570
2590
|
cloudBaseOptions,
|
|
2571
|
-
loginState
|
|
2572
|
-
});
|
|
2573
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2574
|
-
ok: true,
|
|
2575
|
-
code: "AUTH_READY",
|
|
2576
|
-
message: envPreparation.message,
|
|
2577
|
-
auth_challenge: authChallenge(),
|
|
2578
|
-
...buildAuthEnvSetupPayload(envPreparation),
|
|
2579
|
-
next_step: envPreparation.nextStep,
|
|
2591
|
+
loginState,
|
|
2580
2592
|
});
|
|
2581
2593
|
}
|
|
2582
|
-
}
|
|
2583
|
-
catch {
|
|
2584
|
-
// 忽略 getLoginState 错误,继续尝试发起登录
|
|
2585
|
-
}
|
|
2586
|
-
const validationError = (0, auth_js_1.getAuthConfigValidationError)(resolvedAuthOptions);
|
|
2587
|
-
if (validationError) {
|
|
2588
2594
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2589
|
-
ok:
|
|
2590
|
-
code: "
|
|
2591
|
-
|
|
2595
|
+
ok: true,
|
|
2596
|
+
code: "STATUS",
|
|
2597
|
+
auth_status: authStatus,
|
|
2598
|
+
...(isApiKeyMode ? { auth_mode: "api_key" } : {}),
|
|
2592
2599
|
auth_config: authConfigSummary,
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2600
|
+
...(envPreparation
|
|
2601
|
+
? buildAuthEnvSetupPayload(envPreparation)
|
|
2602
|
+
: {
|
|
2603
|
+
env_status: "NONE",
|
|
2604
|
+
current_env_id: null,
|
|
2605
|
+
...buildEnvCandidatePayload([]),
|
|
2606
|
+
}),
|
|
2607
|
+
auth_challenge: authFlowState.status === "PENDING"
|
|
2608
|
+
? (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge)
|
|
2609
|
+
: undefined,
|
|
2610
|
+
message,
|
|
2611
|
+
next_step: authStatus === "REQUIRED"
|
|
2612
|
+
? buildAuthRequiredNextStep(server)
|
|
2613
|
+
: authStatus === "PENDING"
|
|
2614
|
+
? (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2615
|
+
suggestedArgs: { action: "status" },
|
|
2616
|
+
})
|
|
2617
|
+
: envPreparation?.nextStep,
|
|
2596
2618
|
});
|
|
2597
2619
|
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
}
|
|
2612
|
-
};
|
|
2613
|
-
try {
|
|
2614
|
-
// 启动 Device Flow,全流程由 toolbox 负责轮询和写入 credential,这里不等待完成
|
|
2615
|
-
auth
|
|
2616
|
-
.loginByWebAuth({
|
|
2617
|
-
flow: "device",
|
|
2618
|
-
...(resolvedAuthOptions.clientId
|
|
2619
|
-
? { client_id: resolvedAuthOptions.clientId }
|
|
2620
|
-
: {}),
|
|
2621
|
-
...(resolvedAuthOptions.oauthEndpoint
|
|
2622
|
-
? { getOAuthEndpoint: () => resolvedAuthOptions.oauthEndpoint }
|
|
2623
|
-
: {}),
|
|
2624
|
-
...(resolvedAuthOptions.oauthCustom
|
|
2625
|
-
? { custom: true }
|
|
2626
|
-
: {}),
|
|
2627
|
-
onDeviceCode: deviceOnCode,
|
|
2628
|
-
})
|
|
2629
|
-
.then(() => {
|
|
2630
|
-
(0, auth_js_1.resolveAuthProgressState)();
|
|
2631
|
-
})
|
|
2632
|
-
.catch((err) => {
|
|
2633
|
-
(0, auth_js_1.rejectAuthProgressState)(err);
|
|
2634
|
-
// 如果在拿到 device code 之前就失败,则唤醒当前调用并返回错误
|
|
2635
|
-
if (!deviceAuthInfo && rejectCode) {
|
|
2636
|
-
rejectCode(err);
|
|
2620
|
+
if (action === "start_auth") {
|
|
2621
|
+
// API Key 模式:尝试换取临时密钥
|
|
2622
|
+
if (process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID) {
|
|
2623
|
+
try {
|
|
2624
|
+
const existingLoginState = await (0, auth_js_1.peekLoginState)();
|
|
2625
|
+
if (existingLoginState) {
|
|
2626
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2627
|
+
ok: true,
|
|
2628
|
+
code: "AUTH_READY",
|
|
2629
|
+
message: "当前使用 API Key 认证模式,已自动完成登录,无需手动授权。",
|
|
2630
|
+
auth_mode: "api_key",
|
|
2631
|
+
envId: process.env.CLOUDBASE_ENV_ID,
|
|
2632
|
+
});
|
|
2637
2633
|
}
|
|
2634
|
+
}
|
|
2635
|
+
catch (e) {
|
|
2636
|
+
// peekLoginState 内部异常,记录后 fall through
|
|
2637
|
+
(0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
|
|
2638
|
+
}
|
|
2639
|
+
// API Key 换取失败:返回详细诊断信息
|
|
2640
|
+
// 尝试获取更详细的错误信息
|
|
2641
|
+
let diagMessage = "当前配置了 API Key 认证模式,但换取临时密钥失败。";
|
|
2642
|
+
const endpoint = process.env.CLOUDBASE_API_ENDPOINT || `https://${process.env.CLOUDBASE_ENV_ID}.ap-shanghai.tcb-api.tencentcloudapi.com`;
|
|
2643
|
+
diagMessage += `\n\n诊断信息:`;
|
|
2644
|
+
diagMessage += `\n- CLOUDBASE_ENV_ID: ${process.env.CLOUDBASE_ENV_ID}`;
|
|
2645
|
+
diagMessage += `\n- CLOUDBASE_API_KEY: ${process.env.CLOUDBASE_API_KEY.slice(0, 20)}...(已截断)`;
|
|
2646
|
+
diagMessage += `\n- Endpoint: ${endpoint}`;
|
|
2647
|
+
diagMessage += `\n\n可能原因:`;
|
|
2648
|
+
diagMessage += `\n1. API Key 已过期或被删除`;
|
|
2649
|
+
diagMessage += `\n2. Endpoint 不可达(网络/DNS 问题)`;
|
|
2650
|
+
diagMessage += `\n3. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
|
|
2651
|
+
diagMessage += `\n\n建议:检查 MCP 配置中的 CLOUDBASE_API_KEY 和 CLOUDBASE_ENV_ID 环境变量是否正确。`;
|
|
2652
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2653
|
+
ok: false,
|
|
2654
|
+
code: "API_KEY_AUTH_FAILED",
|
|
2655
|
+
message: diagMessage,
|
|
2656
|
+
auth_mode: "api_key",
|
|
2657
|
+
envId: process.env.CLOUDBASE_ENV_ID,
|
|
2658
|
+
endpoint,
|
|
2638
2659
|
});
|
|
2639
2660
|
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2661
|
+
const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
|
|
2662
|
+
const auth = toolbox_1.AuthSupervisor.getInstance({});
|
|
2663
|
+
const authFlowState = await (0, auth_js_1.getAuthProgressState)();
|
|
2664
|
+
if (authFlowState.status === "PENDING" && authFlowState.authChallenge) {
|
|
2665
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2666
|
+
ok: true,
|
|
2667
|
+
code: "AUTH_PENDING",
|
|
2668
|
+
message: "设备码授权进行中,请在浏览器中打开 verification_uri 并输入 user_code 完成授权。",
|
|
2669
|
+
auth_challenge: (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge),
|
|
2670
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2671
|
+
suggestedArgs: { action: "status" },
|
|
2672
|
+
}),
|
|
2673
|
+
});
|
|
2644
2674
|
}
|
|
2675
|
+
// 1. 如果已经有登录态,直接返回 AUTH_READY
|
|
2645
2676
|
try {
|
|
2646
|
-
await
|
|
2677
|
+
const existingLoginState = await (0, auth_js_1.peekLoginState)();
|
|
2678
|
+
if (existingLoginState) {
|
|
2679
|
+
const envPreparation = await prepareAuthEnvironment({
|
|
2680
|
+
server,
|
|
2681
|
+
cloudBaseOptions,
|
|
2682
|
+
loginState: existingLoginState,
|
|
2683
|
+
});
|
|
2684
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2685
|
+
ok: true,
|
|
2686
|
+
code: "AUTH_READY",
|
|
2687
|
+
message: envPreparation.message,
|
|
2688
|
+
auth_challenge: authChallenge(),
|
|
2689
|
+
...buildAuthEnvSetupPayload(envPreparation),
|
|
2690
|
+
next_step: envPreparation.nextStep,
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2647
2693
|
}
|
|
2648
|
-
catch
|
|
2649
|
-
|
|
2694
|
+
catch {
|
|
2695
|
+
// 忽略 getLoginState 错误,继续尝试发起登录
|
|
2696
|
+
}
|
|
2697
|
+
const validationError = (0, auth_js_1.getAuthConfigValidationError)(resolvedAuthOptions);
|
|
2698
|
+
if (validationError) {
|
|
2650
2699
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2651
2700
|
ok: false,
|
|
2652
|
-
code: "
|
|
2653
|
-
message:
|
|
2701
|
+
code: "INVALID_ARGS",
|
|
2702
|
+
message: validationError,
|
|
2703
|
+
auth_config: authConfigSummary,
|
|
2654
2704
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
|
|
2655
2705
|
suggestedArgs: { action: "start_auth", authMode: "device" },
|
|
2656
2706
|
}),
|
|
2657
2707
|
});
|
|
2658
2708
|
}
|
|
2659
|
-
|
|
2709
|
+
// 2. 设备码模式:监听到 device code 即返回 AUTH_PENDING,后续由 toolbox 异步轮询并更新本地 credential
|
|
2710
|
+
const effectiveMode = resolvedAuthOptions.authMode;
|
|
2711
|
+
if (effectiveMode === "device") {
|
|
2712
|
+
let resolveCode;
|
|
2713
|
+
let rejectCode;
|
|
2714
|
+
const codeReady = new Promise((resolve, reject) => {
|
|
2715
|
+
resolveCode = resolve;
|
|
2716
|
+
rejectCode = reject;
|
|
2717
|
+
});
|
|
2718
|
+
const deviceOnCode = (info) => {
|
|
2719
|
+
onDeviceCode(info);
|
|
2720
|
+
if (resolveCode) {
|
|
2721
|
+
resolveCode();
|
|
2722
|
+
}
|
|
2723
|
+
};
|
|
2724
|
+
try {
|
|
2725
|
+
// 启动 Device Flow,全流程由 toolbox 负责轮询和写入 credential,这里不等待完成
|
|
2726
|
+
auth
|
|
2727
|
+
.loginByWebAuth({
|
|
2728
|
+
flow: "device",
|
|
2729
|
+
...(resolvedAuthOptions.clientId
|
|
2730
|
+
? { client_id: resolvedAuthOptions.clientId }
|
|
2731
|
+
: {}),
|
|
2732
|
+
...(resolvedAuthOptions.oauthEndpoint
|
|
2733
|
+
? { getOAuthEndpoint: () => resolvedAuthOptions.oauthEndpoint }
|
|
2734
|
+
: {}),
|
|
2735
|
+
...(resolvedAuthOptions.oauthCustom
|
|
2736
|
+
? { custom: true }
|
|
2737
|
+
: {}),
|
|
2738
|
+
onDeviceCode: deviceOnCode,
|
|
2739
|
+
})
|
|
2740
|
+
.then(() => {
|
|
2741
|
+
(0, auth_js_1.resolveAuthProgressState)();
|
|
2742
|
+
})
|
|
2743
|
+
.catch((err) => {
|
|
2744
|
+
(0, auth_js_1.rejectAuthProgressState)(err);
|
|
2745
|
+
// 如果在拿到 device code 之前就失败,则唤醒当前调用并返回错误
|
|
2746
|
+
if (!deviceAuthInfo && rejectCode) {
|
|
2747
|
+
rejectCode(err);
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
catch (err) {
|
|
2752
|
+
if (rejectCode) {
|
|
2753
|
+
rejectCode(err);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
try {
|
|
2757
|
+
await codeReady;
|
|
2758
|
+
}
|
|
2759
|
+
catch (err) {
|
|
2760
|
+
const message = err instanceof Error ? err.message : String(err ?? "unknown error");
|
|
2761
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2762
|
+
ok: false,
|
|
2763
|
+
code: "AUTH_REQUIRED",
|
|
2764
|
+
message: `设备码登录初始化失败: ${message}`,
|
|
2765
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
|
|
2766
|
+
suggestedArgs: { action: "start_auth", authMode: "device" },
|
|
2767
|
+
}),
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
if (!deviceAuthInfo) {
|
|
2771
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2772
|
+
ok: false,
|
|
2773
|
+
code: "AUTH_REQUIRED",
|
|
2774
|
+
message: "未获取到设备码信息,请重试设备码登录",
|
|
2775
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
|
|
2776
|
+
suggestedArgs: { action: "start_auth", authMode: "device" },
|
|
2777
|
+
}),
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
const envCandidates = await fetchAvailableEnvCandidates(cloudBaseOptions, server);
|
|
2781
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2782
|
+
ok: true,
|
|
2783
|
+
code: "AUTH_PENDING",
|
|
2784
|
+
message: "已发起设备码登录,请在浏览器中打开 verification_uri 并输入 user_code 完成授权。授权完成后请再次调用 auth(action=\"status\")。",
|
|
2785
|
+
auth_challenge: authChallenge(),
|
|
2786
|
+
...buildEnvCandidatePayload(envCandidates),
|
|
2787
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2788
|
+
suggestedArgs: { action: "status" },
|
|
2789
|
+
}),
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2792
|
+
// 3. 非 Device Flow(显式 web 模式)仍然使用 getLoginState 阻塞等待
|
|
2793
|
+
const loginState = await (0, auth_js_1.ensureLogin)({
|
|
2794
|
+
region,
|
|
2795
|
+
authMode: effectiveMode,
|
|
2796
|
+
clientId: resolvedAuthOptions.clientId,
|
|
2797
|
+
oauthEndpoint: resolvedAuthOptions.oauthEndpoint,
|
|
2798
|
+
oauthCustom: resolvedAuthOptions.oauthCustom,
|
|
2799
|
+
});
|
|
2800
|
+
if (!loginState) {
|
|
2660
2801
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2661
2802
|
ok: false,
|
|
2662
2803
|
code: "AUTH_REQUIRED",
|
|
2663
|
-
message: "
|
|
2804
|
+
message: "未获取到登录态,请先完成认证",
|
|
2664
2805
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
|
|
2665
|
-
suggestedArgs: { action: "start_auth", authMode:
|
|
2806
|
+
suggestedArgs: { action: "start_auth", authMode: effectiveMode },
|
|
2666
2807
|
}),
|
|
2667
2808
|
});
|
|
2668
2809
|
}
|
|
2669
|
-
const
|
|
2810
|
+
const envPreparation = await prepareAuthEnvironment({
|
|
2811
|
+
server,
|
|
2812
|
+
cloudBaseOptions,
|
|
2813
|
+
loginState,
|
|
2814
|
+
});
|
|
2670
2815
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2671
2816
|
ok: true,
|
|
2672
|
-
code: "
|
|
2673
|
-
message:
|
|
2817
|
+
code: "AUTH_READY",
|
|
2818
|
+
message: envPreparation.message,
|
|
2674
2819
|
auth_challenge: authChallenge(),
|
|
2675
|
-
...
|
|
2676
|
-
next_step:
|
|
2677
|
-
suggestedArgs: { action: "status" },
|
|
2678
|
-
}),
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
2681
|
-
// 3. 非 Device Flow(显式 web 模式)仍然使用 getLoginState 阻塞等待
|
|
2682
|
-
const loginState = await (0, auth_js_1.ensureLogin)({
|
|
2683
|
-
region,
|
|
2684
|
-
authMode: effectiveMode,
|
|
2685
|
-
clientId: resolvedAuthOptions.clientId,
|
|
2686
|
-
oauthEndpoint: resolvedAuthOptions.oauthEndpoint,
|
|
2687
|
-
oauthCustom: resolvedAuthOptions.oauthCustom,
|
|
2688
|
-
});
|
|
2689
|
-
if (!loginState) {
|
|
2690
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2691
|
-
ok: false,
|
|
2692
|
-
code: "AUTH_REQUIRED",
|
|
2693
|
-
message: "未获取到登录态,请先完成认证",
|
|
2694
|
-
next_step: (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
|
|
2695
|
-
suggestedArgs: { action: "start_auth", authMode: effectiveMode },
|
|
2696
|
-
}),
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
const envPreparation = await prepareAuthEnvironment({
|
|
2700
|
-
server,
|
|
2701
|
-
cloudBaseOptions,
|
|
2702
|
-
loginState,
|
|
2703
|
-
});
|
|
2704
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2705
|
-
ok: true,
|
|
2706
|
-
code: "AUTH_READY",
|
|
2707
|
-
message: envPreparation.message,
|
|
2708
|
-
auth_challenge: authChallenge(),
|
|
2709
|
-
...buildAuthEnvSetupPayload(envPreparation),
|
|
2710
|
-
next_step: envPreparation.nextStep,
|
|
2711
|
-
});
|
|
2712
|
-
}
|
|
2713
|
-
if (action === "set_env") {
|
|
2714
|
-
const loginState = await (0, auth_js_1.peekLoginState)();
|
|
2715
|
-
if (!loginState) {
|
|
2716
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2717
|
-
ok: false,
|
|
2718
|
-
code: "AUTH_REQUIRED",
|
|
2719
|
-
message: isCodeBuddyIde(server)
|
|
2720
|
-
? "当前未登录。CodeBuddy 暂不支持在 tool 内发起认证,请在外部完成认证后再次调用 auth(action=\"status\")。"
|
|
2721
|
-
: "当前未登录,请先执行 auth(action=\"start_auth\")。",
|
|
2722
|
-
next_step: buildAuthRequiredNextStep(server),
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
const envCandidates = await fetchAvailableEnvCandidates(cloudBaseOptions, server);
|
|
2726
|
-
if (!envId) {
|
|
2727
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2728
|
-
ok: false,
|
|
2729
|
-
code: "INVALID_ARGS",
|
|
2730
|
-
message: "action=set_env 时必须提供 envId",
|
|
2731
|
-
...buildEnvCandidatePayload(envCandidates),
|
|
2732
|
-
next_step: buildSetEnvNextStep(envCandidates),
|
|
2820
|
+
...buildAuthEnvSetupPayload(envPreparation),
|
|
2821
|
+
next_step: envPreparation.nextStep,
|
|
2733
2822
|
});
|
|
2734
2823
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2824
|
+
if (action === "set_env") {
|
|
2825
|
+
const loginState = await (0, auth_js_1.peekLoginState)();
|
|
2826
|
+
if (!loginState) {
|
|
2827
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2828
|
+
ok: false,
|
|
2829
|
+
code: "AUTH_REQUIRED",
|
|
2830
|
+
message: isCodeBuddyIde(server)
|
|
2831
|
+
? "当前未登录。CodeBuddy 暂不支持在 tool 内发起认证,请在外部完成认证后再次调用 auth(action=\"status\")。"
|
|
2832
|
+
: "当前未登录,请先执行 auth(action=\"start_auth\")。",
|
|
2833
|
+
next_step: buildAuthRequiredNextStep(server),
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
const envCandidates = await fetchAvailableEnvCandidates(cloudBaseOptions, server);
|
|
2837
|
+
if (!envId) {
|
|
2838
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2839
|
+
ok: false,
|
|
2840
|
+
code: "INVALID_ARGS",
|
|
2841
|
+
message: "action=set_env 时必须提供 envId",
|
|
2842
|
+
...buildEnvCandidatePayload(envCandidates),
|
|
2843
|
+
next_step: buildSetEnvNextStep(envCandidates),
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
const target = envCandidates.find((item) => item.envId === envId);
|
|
2847
|
+
if (envCandidates.length > 0 && !target) {
|
|
2848
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2849
|
+
ok: false,
|
|
2850
|
+
code: "INVALID_ARGS",
|
|
2851
|
+
message: `未找到环境: ${envId}`,
|
|
2852
|
+
...buildEnvCandidatePayload(envCandidates),
|
|
2853
|
+
next_step: buildSetEnvNextStep(envCandidates),
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
await cloudbase_manager_js_1.envManager.setEnvId(envId);
|
|
2737
2857
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2738
|
-
ok:
|
|
2739
|
-
code: "
|
|
2740
|
-
message:
|
|
2741
|
-
|
|
2742
|
-
next_step: buildSetEnvNextStep(envCandidates),
|
|
2858
|
+
ok: true,
|
|
2859
|
+
code: "ENV_READY",
|
|
2860
|
+
message: `环境设置成功,当前环境: ${envId}`,
|
|
2861
|
+
current_env_id: envId,
|
|
2743
2862
|
});
|
|
2744
2863
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2864
|
+
if (action === "logout") {
|
|
2865
|
+
// API Key 模式下不允许 logout
|
|
2866
|
+
if (process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID) {
|
|
2867
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2868
|
+
ok: false,
|
|
2869
|
+
code: "LOGOUT_NOT_ALLOWED",
|
|
2870
|
+
message: "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY 环境变量后重启。",
|
|
2871
|
+
auth_mode: "api_key",
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2874
|
+
if (confirm !== "yes") {
|
|
2875
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2876
|
+
ok: false,
|
|
2877
|
+
code: "INVALID_ARGS",
|
|
2878
|
+
message: "action=logout 时必须传 confirm=\"yes\"",
|
|
2879
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("logout", {
|
|
2880
|
+
suggestedArgs: { action: "logout", confirm: "yes" },
|
|
2881
|
+
}),
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
await (0, auth_js_1.logout)();
|
|
2885
|
+
(0, cloudbase_manager_js_1.resetCloudBaseManagerCache)();
|
|
2756
2886
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2757
|
-
ok:
|
|
2758
|
-
code: "
|
|
2759
|
-
message: "
|
|
2760
|
-
auth_mode: "api_key",
|
|
2887
|
+
ok: true,
|
|
2888
|
+
code: "LOGGED_OUT",
|
|
2889
|
+
message: "✅ 已退出登录",
|
|
2761
2890
|
});
|
|
2762
2891
|
}
|
|
2763
|
-
if (
|
|
2892
|
+
if (action === "get_temp_credentials") {
|
|
2893
|
+
const loginState = (await (0, auth_js_1.peekLoginState)());
|
|
2894
|
+
if (!loginState) {
|
|
2895
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2896
|
+
ok: false,
|
|
2897
|
+
code: "AUTH_REQUIRED",
|
|
2898
|
+
message: "当前未登录,请先完成管理端认证后再获取临时密钥。",
|
|
2899
|
+
next_step: buildAuthRequiredNextStep(server),
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2902
|
+
if (confirm !== "yes") {
|
|
2903
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2904
|
+
ok: false,
|
|
2905
|
+
code: "INVALID_ARGS",
|
|
2906
|
+
message: "action=get_temp_credentials 时必须显式传 confirm=\"yes\",以确认你要导出当前管理端临时密钥。",
|
|
2907
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("get_temp_credentials", {
|
|
2908
|
+
suggestedArgs: { action: "get_temp_credentials", confirm: "yes" },
|
|
2909
|
+
}),
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
if (!isTemporaryCredentialLoginState(loginState)) {
|
|
2913
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2914
|
+
ok: false,
|
|
2915
|
+
code: "UNSUPPORTED_CREDENTIAL_TYPE",
|
|
2916
|
+
message: "当前登录态不是可导出的临时密钥。仅支持通过 Web / device 登录得到的临时密钥,永久密钥登录不允许导出。",
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
const secretId = normalizeOptionalToolString(loginState.secretId);
|
|
2920
|
+
const secretKey = normalizeOptionalToolString(loginState.secretKey);
|
|
2921
|
+
const token = normalizeOptionalToolString(loginState.token);
|
|
2922
|
+
if (!secretId || !secretKey || !token) {
|
|
2923
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2924
|
+
ok: false,
|
|
2925
|
+
code: "INTERNAL_ERROR",
|
|
2926
|
+
message: "当前登录态缺少完整的临时密钥字段,请重新登录后再试。",
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2764
2929
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2765
|
-
ok:
|
|
2766
|
-
code: "
|
|
2767
|
-
message:
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2930
|
+
ok: true,
|
|
2931
|
+
code: "TEMP_CREDENTIALS_READY",
|
|
2932
|
+
message: reveal
|
|
2933
|
+
? "当前管理端临时密钥已准备好,请注意避免泄露。"
|
|
2934
|
+
: "当前管理端临时密钥已准备好,默认仅返回脱敏结果。",
|
|
2935
|
+
env_id: normalizeOptionalToolString(loginState.envId) ?? null,
|
|
2936
|
+
credentials: {
|
|
2937
|
+
secretId: reveal ? secretId : maskSensitiveValue(secretId),
|
|
2938
|
+
secretKey: reveal ? secretKey : maskSensitiveValue(secretKey),
|
|
2939
|
+
token: reveal ? token : maskSensitiveValue(token),
|
|
2940
|
+
masked: !reveal,
|
|
2941
|
+
},
|
|
2771
2942
|
});
|
|
2772
2943
|
}
|
|
2773
|
-
await (0, auth_js_1.logout)();
|
|
2774
|
-
(0, cloudbase_manager_js_1.resetCloudBaseManagerCache)();
|
|
2775
2944
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2776
|
-
ok:
|
|
2777
|
-
code: "
|
|
2778
|
-
message:
|
|
2945
|
+
ok: false,
|
|
2946
|
+
code: "NOT_SUPPORTED",
|
|
2947
|
+
message: `不支持的 auth action: ${action}`,
|
|
2948
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2949
|
+
suggestedArgs: { action: "status" },
|
|
2950
|
+
}),
|
|
2779
2951
|
});
|
|
2780
2952
|
}
|
|
2781
|
-
|
|
2782
|
-
const
|
|
2783
|
-
if (!loginState) {
|
|
2784
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2785
|
-
ok: false,
|
|
2786
|
-
code: "AUTH_REQUIRED",
|
|
2787
|
-
message: "当前未登录,请先完成管理端认证后再获取临时密钥。",
|
|
2788
|
-
next_step: buildAuthRequiredNextStep(server),
|
|
2789
|
-
});
|
|
2790
|
-
}
|
|
2791
|
-
if (confirm !== "yes") {
|
|
2792
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2793
|
-
ok: false,
|
|
2794
|
-
code: "INVALID_ARGS",
|
|
2795
|
-
message: "action=get_temp_credentials 时必须显式传 confirm=\"yes\",以确认你要导出当前管理端临时密钥。",
|
|
2796
|
-
next_step: (0, tool_result_js_1.buildAuthNextStep)("get_temp_credentials", {
|
|
2797
|
-
suggestedArgs: { action: "get_temp_credentials", confirm: "yes" },
|
|
2798
|
-
}),
|
|
2799
|
-
});
|
|
2800
|
-
}
|
|
2801
|
-
if (!isTemporaryCredentialLoginState(loginState)) {
|
|
2802
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2803
|
-
ok: false,
|
|
2804
|
-
code: "UNSUPPORTED_CREDENTIAL_TYPE",
|
|
2805
|
-
message: "当前登录态不是可导出的临时密钥。仅支持通过 Web / device 登录得到的临时密钥,永久密钥登录不允许导出。",
|
|
2806
|
-
});
|
|
2807
|
-
}
|
|
2808
|
-
const secretId = normalizeOptionalToolString(loginState.secretId);
|
|
2809
|
-
const secretKey = normalizeOptionalToolString(loginState.secretKey);
|
|
2810
|
-
const token = normalizeOptionalToolString(loginState.token);
|
|
2811
|
-
if (!secretId || !secretKey || !token) {
|
|
2812
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2813
|
-
ok: false,
|
|
2814
|
-
code: "INTERNAL_ERROR",
|
|
2815
|
-
message: "当前登录态缺少完整的临时密钥字段,请重新登录后再试。",
|
|
2816
|
-
});
|
|
2817
|
-
}
|
|
2953
|
+
catch (error) {
|
|
2954
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2818
2955
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2819
|
-
ok:
|
|
2820
|
-
code: "
|
|
2821
|
-
message:
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
secretId: reveal ? secretId : maskSensitiveValue(secretId),
|
|
2827
|
-
secretKey: reveal ? secretKey : maskSensitiveValue(secretKey),
|
|
2828
|
-
token: reveal ? token : maskSensitiveValue(token),
|
|
2829
|
-
masked: !reveal,
|
|
2830
|
-
},
|
|
2956
|
+
ok: false,
|
|
2957
|
+
code: "INTERNAL_ERROR",
|
|
2958
|
+
message: `auth 执行失败: ${message}`,
|
|
2959
|
+
auth_challenge: authChallenge(),
|
|
2960
|
+
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2961
|
+
suggestedArgs: { action: "status" },
|
|
2962
|
+
}),
|
|
2831
2963
|
});
|
|
2832
2964
|
}
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2838
|
-
suggestedArgs: { action: "status" },
|
|
2839
|
-
}),
|
|
2840
|
-
});
|
|
2841
|
-
}
|
|
2842
|
-
catch (error) {
|
|
2843
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2844
|
-
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2845
|
-
ok: false,
|
|
2846
|
-
code: "INTERNAL_ERROR",
|
|
2847
|
-
message: `auth 执行失败: ${message}`,
|
|
2848
|
-
auth_challenge: authChallenge(),
|
|
2849
|
-
next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
|
|
2850
|
-
suggestedArgs: { action: "status" },
|
|
2851
|
-
}),
|
|
2852
|
-
});
|
|
2853
|
-
}
|
|
2854
|
-
});
|
|
2855
|
-
// envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains)
|
|
2856
|
-
server.registerTool?.("envQuery", {
|
|
2857
|
-
title: "CloudBase 环境查询",
|
|
2858
|
-
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(原工具名:listEnvs/getEnvInfo/getEnvAuthDomains,为兼容旧AI规则可继续使用这些名称)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。",
|
|
2859
|
-
inputSchema: {
|
|
2860
|
-
action: zod_1.z
|
|
2861
|
-
.enum(["list", "info", "domains"])
|
|
2862
|
-
.describe("查询类型:list=环境列表/摘要筛选(按 DescribeEnvs 语义筛选,支持通过 envId 筛选,返回 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,不支持 expiry),info=指定环境的详细信息(必须传入 envId,返回资源字段和计费信息),domains=安全域名列表"),
|
|
2863
|
-
alias: zod_1.z.string().optional().describe("按环境别名筛选。action=list 时可选"),
|
|
2864
|
-
aliasExact: zod_1.z.boolean().optional().describe("按环境别名精确筛选。action=list 时可选;与 alias 配合使用"),
|
|
2865
|
-
envId: zod_1.z.string().optional().describe("按环境 ID 筛选。action=list 时可选(仅按 DescribeEnvs 语义做筛选,仍返回摘要);action=info 时必填(返回该环境的详细信息,包含资源字段和计费信息)。如果任务已经给出了明确的 EnvId 并要求查询详情,请直接使用 action=info + envId,而不是 action=list"),
|
|
2866
|
-
limit: zod_1.z.number().int().positive().optional().describe("返回数量上限。action=list 时可选"),
|
|
2867
|
-
offset: zod_1.z.number().int().min(0).optional().describe("分页偏移。action=list 时可选"),
|
|
2868
|
-
fields: zod_1.z
|
|
2869
|
-
.array(zod_1.z.enum(DEFAULT_ENV_FIELDS))
|
|
2870
|
-
.optional()
|
|
2871
|
-
.describe("返回字段白名单。仅支持 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault。action=list 时可选"),
|
|
2872
|
-
},
|
|
2873
|
-
annotations: {
|
|
2874
|
-
readOnlyHint: true,
|
|
2875
|
-
openWorldHint: true,
|
|
2876
|
-
category: "env",
|
|
2877
|
-
},
|
|
2878
|
-
}, async ({ action, alias, aliasExact, envId, limit, offset, fields, }) => {
|
|
2965
|
+
});
|
|
2966
|
+
} // end: wxide guard for auth tool
|
|
2967
|
+
// queryEnv - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains)
|
|
2968
|
+
const queryEnvHandler = async ({ action, alias, aliasExact, envId, limit, offset, fields, }) => {
|
|
2879
2969
|
try {
|
|
2880
2970
|
let result;
|
|
2881
2971
|
switch (action) {
|
|
@@ -2889,7 +2979,8 @@ function registerEnvTools(server) {
|
|
|
2889
2979
|
// 当环境变量设置了 CLOUDBASE_ENV_ID 时(如 API Key 模式),
|
|
2890
2980
|
// 避免调用 DescribeEnvs(临时密钥可能不支持该接口),
|
|
2891
2981
|
// 改为通过 DescribeEnvInfo 获取单环境信息
|
|
2892
|
-
|
|
2982
|
+
// 注意:requestFn 模式(如微信 IDE)使用 DescribeEnvs,不走此分支
|
|
2983
|
+
const envIdFromEnv = !cloudBaseOptions?.requestFn && process.env.CLOUDBASE_ENV_ID;
|
|
2893
2984
|
if (envIdFromEnv) {
|
|
2894
2985
|
try {
|
|
2895
2986
|
const envInfo = await cloudbaseList.env.describeEnvInfo({ EnvId: envIdFromEnv });
|
|
@@ -2993,6 +3084,7 @@ function registerEnvTools(server) {
|
|
|
2993
3084
|
envId,
|
|
2994
3085
|
logger: server.logger,
|
|
2995
3086
|
});
|
|
3087
|
+
result = enrichEnvInfoWithRuntimeMode(result);
|
|
2996
3088
|
break;
|
|
2997
3089
|
case "domains":
|
|
2998
3090
|
const cloudbaseDomains = await getManager();
|
|
@@ -3056,61 +3148,91 @@ function registerEnvTools(server) {
|
|
|
3056
3148
|
],
|
|
3057
3149
|
};
|
|
3058
3150
|
}
|
|
3059
|
-
}
|
|
3060
|
-
//
|
|
3061
|
-
|
|
3062
|
-
title: "CloudBase
|
|
3063
|
-
description: "
|
|
3151
|
+
};
|
|
3152
|
+
// Register primary tool name (queryEnv)
|
|
3153
|
+
const queryEnvToolSchema = {
|
|
3154
|
+
title: "CloudBase 环境查询",
|
|
3155
|
+
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(曾用名:envQuery、listEnvs、getEnvInfo、getEnvAuthDomains)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。\n\n🔍 action=info 还会派生三个用于后端选型的字段:\n- `EnvInfo.RuntimeMode`:'postgresql' 或 'nosql',表示新业务建议默认使用的后端(PG 已开通时为 postgresql,否则为 nosql)。\n- `EnvInfo.RuntimeBackends`:`{postgresql, nosql, mysql}` 三个布尔值,描述当前环境实际并存的后端。\n- `EnvInfo.RuntimeModeHints`:每个后端对应的 API/工具/skill 提示。\n\nAI 在写业务/权限/存储代码前必须先看这三项:PG 模式下新业务推荐 `app.rdb()` + RLS(`managePgDatabase action=execute` 跑 `CREATE POLICY`)+ pgstore;已存在的 NoSQL 集合 / 旧 storage / `managePermissions(resourceType=\"noSqlDatabase\")` 在 PG 环境下仍然有效。真正不适用的是 MySQL:当 `RuntimeBackends.mysql === false` 时,`manageSqlDatabase` / `querySqlDatabase` / `relational-database-tool` skill 都不该使用。",
|
|
3064
3156
|
inputSchema: {
|
|
3065
3157
|
action: zod_1.z
|
|
3066
|
-
.enum(["
|
|
3067
|
-
.describe("
|
|
3068
|
-
|
|
3158
|
+
.enum(["list", "info", "domains"])
|
|
3159
|
+
.describe("查询类型:list=环境列表/摘要筛选(按 DescribeEnvs 语义筛选,支持通过 envId 筛选,返回 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,不支持 expiry),info=指定环境的详细信息(必须传入 envId,返回资源字段和计费信息),domains=安全域名列表"),
|
|
3160
|
+
alias: zod_1.z.string().optional().describe("按环境别名筛选。action=list 时可选"),
|
|
3161
|
+
aliasExact: zod_1.z.boolean().optional().describe("按环境别名精确筛选。action=list 时可选;与 alias 配合使用"),
|
|
3162
|
+
envId: zod_1.z.string().optional().describe("按环境 ID 筛选。action=list 时可选(仅按 DescribeEnvs 语义做筛选,仍返回摘要);action=info 时必填(返回该环境的详细信息,包含资源字段和计费信息)。如果任务已经给出了明确的 EnvId 并要求查询详情,请直接使用 action=info + envId,而不是 action=list"),
|
|
3163
|
+
limit: zod_1.z.number().int().positive().optional().describe("返回数量上限。action=list 时可选"),
|
|
3164
|
+
offset: zod_1.z.number().int().min(0).optional().describe("分页偏移。action=list 时可选"),
|
|
3165
|
+
fields: zod_1.z
|
|
3166
|
+
.array(zod_1.z.enum(DEFAULT_ENV_FIELDS))
|
|
3167
|
+
.optional()
|
|
3168
|
+
.describe("返回字段白名单。仅支持 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault。action=list 时可选"),
|
|
3069
3169
|
},
|
|
3070
3170
|
annotations: {
|
|
3071
|
-
readOnlyHint:
|
|
3072
|
-
destructiveHint: false, // 注意:delete操作虽然是破坏性的,但这里采用较宽松的标注
|
|
3073
|
-
idempotentHint: false,
|
|
3171
|
+
readOnlyHint: true,
|
|
3074
3172
|
openWorldHint: true,
|
|
3075
3173
|
category: "env",
|
|
3076
3174
|
},
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3175
|
+
};
|
|
3176
|
+
server.registerTool?.("queryEnv", queryEnvToolSchema, queryEnvHandler);
|
|
3177
|
+
// 向后兼容:envQuery 作为 queryEnv 的别名注册
|
|
3178
|
+
server.registerTool?.("envQuery", queryEnvToolSchema, queryEnvHandler);
|
|
3179
|
+
// envDomainManagement - 环境域名管理(合并 createEnvDomain + deleteEnvDomain)
|
|
3180
|
+
// 微信 IDE 场景不需要域名管理
|
|
3181
|
+
if (server.ide !== 'wxide') {
|
|
3182
|
+
server.registerTool?.("envDomainManagement", {
|
|
3183
|
+
title: "CloudBase 环境域名管理(安全域名 / CORS 白名单)",
|
|
3184
|
+
description: "管理 CloudBase 环境的安全域名(安全域名 / CORS 白名单),支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 直接访问 CloudBase 资源时,应先用 queryEnv(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。新增或删除后通常需要继续轮询 queryEnv(action=domains) 确认状态收敛;安全域名一般约 10 分钟生效。⚠️ 重要:此工具仅用于 CORS/请求来源验证,不涉及 SSL 证书。如需绑定自定义域名供公网 HTTPS 访问,请使用 manageGateway(action=\"bindCustomDomain\")。",
|
|
3185
|
+
inputSchema: {
|
|
3186
|
+
action: zod_1.z
|
|
3187
|
+
.enum(["create", "delete"])
|
|
3188
|
+
.describe("操作类型:create=添加安全域名,delete=删除安全域名"),
|
|
3189
|
+
domains: zod_1.z.array(zod_1.z.string()).describe("安全域名数组(格式:host:port,例如 localhost:5173 或 127.0.0.1:4173)。注意:不是自定义域名,不需要证书。"),
|
|
3190
|
+
},
|
|
3191
|
+
annotations: {
|
|
3192
|
+
readOnlyHint: false,
|
|
3193
|
+
destructiveHint: false, // 注意:delete操作虽然是破坏性的,但这里采用较宽松的标注
|
|
3194
|
+
idempotentHint: false,
|
|
3195
|
+
openWorldHint: true,
|
|
3196
|
+
category: "env",
|
|
3197
|
+
},
|
|
3198
|
+
}, async ({ action, domains, }) => {
|
|
3199
|
+
try {
|
|
3200
|
+
const cloudbase = await getManager();
|
|
3201
|
+
let result;
|
|
3202
|
+
switch (action) {
|
|
3203
|
+
case "create":
|
|
3204
|
+
result = await cloudbase.env.createEnvDomain(domains);
|
|
3205
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
3206
|
+
break;
|
|
3207
|
+
case "delete":
|
|
3208
|
+
result = await cloudbase.env.deleteEnvDomain(domains);
|
|
3209
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
3210
|
+
break;
|
|
3211
|
+
default:
|
|
3212
|
+
throw new Error(`不支持的操作类型: ${action}`);
|
|
3213
|
+
}
|
|
3214
|
+
return (0, tool_result_js_1.buildJsonToolResult)(buildEnvDomainManagementResult({
|
|
3215
|
+
action,
|
|
3216
|
+
domains,
|
|
3217
|
+
result,
|
|
3218
|
+
}));
|
|
3092
3219
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3220
|
+
catch (error) {
|
|
3221
|
+
const toolPayloadResult = (0, tool_result_js_1.toolPayloadErrorToResult)(error);
|
|
3222
|
+
if (toolPayloadResult) {
|
|
3223
|
+
return toolPayloadResult;
|
|
3224
|
+
}
|
|
3225
|
+
return {
|
|
3226
|
+
content: [
|
|
3227
|
+
{
|
|
3228
|
+
type: "text",
|
|
3229
|
+
text: `域名管理操作失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
3230
|
+
},
|
|
3231
|
+
],
|
|
3232
|
+
};
|
|
3103
3233
|
}
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
{
|
|
3107
|
-
type: "text",
|
|
3108
|
-
text: `域名管理操作失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
3109
|
-
},
|
|
3110
|
-
],
|
|
3111
|
-
};
|
|
3112
|
-
}
|
|
3113
|
-
});
|
|
3234
|
+
});
|
|
3235
|
+
} // end: wxide guard for envDomainManagement
|
|
3114
3236
|
}
|
|
3115
3237
|
|
|
3116
3238
|
|
|
@@ -3638,7 +3760,7 @@ OAuth.prototype.onRequest = function (_oauth) {
|
|
|
3638
3760
|
}
|
|
3639
3761
|
}
|
|
3640
3762
|
|
|
3641
|
-
exports.
|
|
3763
|
+
exports.OAuth = OAuth
|
|
3642
3764
|
|
|
3643
3765
|
|
|
3644
3766
|
/***/ }),
|
|
@@ -24985,8 +25107,9 @@ async function getDatabaseInstanceId(options) {
|
|
|
24985
25107
|
async function getCloudBaseManager(options = {}) {
|
|
24986
25108
|
const { requireEnvId = true, cloudBaseOptions, authStrategy = 'fail_fast', } = options;
|
|
24987
25109
|
const hasDirectCredentials = !!(cloudBaseOptions?.secretId && cloudBaseOptions?.secretKey);
|
|
24988
|
-
|
|
24989
|
-
|
|
25110
|
+
const hasRequestFn = !!(cloudBaseOptions?.requestFn);
|
|
25111
|
+
// 如果传入了完整凭据或 requestFn,优先使用显式 CloudBase 配置
|
|
25112
|
+
if (cloudBaseOptions && (hasDirectCredentials || hasRequestFn)) {
|
|
24990
25113
|
let resolvedEnvId = cloudBaseOptions.envId;
|
|
24991
25114
|
if (requireEnvId && !resolvedEnvId) {
|
|
24992
25115
|
const envCandidates = await listAvailableEnvCandidates({ cloudBaseOptions });
|
|
@@ -28413,7 +28536,7 @@ function buildWebSdkHint(loginMethods) {
|
|
|
28413
28536
|
}
|
|
28414
28537
|
return {
|
|
28415
28538
|
blocked: false,
|
|
28416
|
-
register: "
|
|
28539
|
+
register: "direct username/password signUp is SDK/provider dependent; verify before use, otherwise create users through a backend or management API boundary",
|
|
28417
28540
|
login: "auth.signInWithPassword({ username, password })",
|
|
28418
28541
|
accountInputType: "text",
|
|
28419
28542
|
avoidEmailHelpers: true,
|
|
@@ -29045,7 +29168,7 @@ function ReadableState(options, stream) {
|
|
|
29045
29168
|
this.decoder = null;
|
|
29046
29169
|
this.encoding = null;
|
|
29047
29170
|
if (options.encoding) {
|
|
29048
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(84824)
|
|
29171
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(84824).StringDecoder);
|
|
29049
29172
|
this.decoder = new StringDecoder(options.encoding);
|
|
29050
29173
|
this.encoding = options.encoding;
|
|
29051
29174
|
}
|
|
@@ -29201,7 +29324,7 @@ Readable.prototype.isPaused = function () {
|
|
|
29201
29324
|
|
|
29202
29325
|
// backwards compatibility.
|
|
29203
29326
|
Readable.prototype.setEncoding = function (enc) {
|
|
29204
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(84824)
|
|
29327
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(84824).StringDecoder);
|
|
29205
29328
|
this._readableState.decoder = new StringDecoder(enc);
|
|
29206
29329
|
this._readableState.encoding = enc;
|
|
29207
29330
|
return this;
|
|
@@ -31342,7 +31465,7 @@ Promise.prototype._resolveFromSyncValue = function (value) {
|
|
|
31342
31465
|
/***/ ((module) => {
|
|
31343
31466
|
|
|
31344
31467
|
"use strict";
|
|
31345
|
-
module.exports = {"version":"3.17.0"};
|
|
31468
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"winston","description":"A logger for just about everything.","version":"3.17.0","author":"Charlie Robbins <charlie.robbins@gmail.com>","maintainers":["David Hyde <dabh@alumni.stanford.edu>"],"repository":{"type":"git","url":"https://github.com/winstonjs/winston.git"},"keywords":["winston","logger","logging","logs","sysadmin","bunyan","pino","loglevel","tools","json","stream"],"dependencies":{"@dabh/diagnostics":"^2.0.2","@colors/colors":"^1.6.0","async":"^3.2.3","is-stream":"^2.0.0","logform":"^2.7.0","one-time":"^1.0.0","readable-stream":"^3.4.0","safe-stable-stringify":"^2.3.1","stack-trace":"0.0.x","triple-beam":"^1.3.0","winston-transport":"^4.9.0"},"devDependencies":{"@babel/cli":"^7.23.9","@babel/core":"^7.24.0","@babel/preset-env":"^7.24.0","@dabh/eslint-config-populist":"^4.4.0","@types/node":"^20.11.24","abstract-winston-transport":"^0.5.1","assume":"^2.2.0","cross-spawn-async":"^2.2.5","eslint":"^8.57.0","hock":"^1.4.1","mocha":"^10.3.0","nyc":"^17.1.0","rimraf":"5.0.1","split2":"^4.1.0","std-mocks":"^2.0.0","through2":"^4.0.2","winston-compat":"^0.1.5"},"main":"./lib/winston.js","browser":"./dist/winston","types":"./index.d.ts","scripts":{"lint":"eslint lib/*.js lib/winston/*.js lib/winston/**/*.js --resolve-plugins-relative-to ./node_modules/@dabh/eslint-config-populist","test":"rimraf test/fixtures/logs/* && mocha","test:coverage":"nyc npm run test:unit","test:unit":"mocha test/unit","test:integration":"mocha test/integration","build":"rimraf dist && babel lib -d dist","prepublishOnly":"npm run build"},"engines":{"node":">= 12.0.0"},"license":"MIT"}');
|
|
31346
31469
|
|
|
31347
31470
|
/***/ }),
|
|
31348
31471
|
|
|
@@ -34992,7 +35115,7 @@ function normalizeEncoding(enc) {
|
|
|
34992
35115
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
34993
35116
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
34994
35117
|
// characters.
|
|
34995
|
-
exports.
|
|
35118
|
+
exports.StringDecoder = StringDecoder;
|
|
34996
35119
|
function StringDecoder(encoding) {
|
|
34997
35120
|
this.encoding = normalizeEncoding(encoding);
|
|
34998
35121
|
var nb;
|
|
@@ -40102,6 +40225,44 @@ const zod_1 = __webpack_require__(21614);
|
|
|
40102
40225
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
40103
40226
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
40104
40227
|
const CATEGORY = "SQL database";
|
|
40228
|
+
const MYSQL_GATE_CACHE = new Map();
|
|
40229
|
+
/**
|
|
40230
|
+
* Check whether MySQL tools should be blocked because the env is in PG-only mode.
|
|
40231
|
+
* Returns an error result if blocked, or null if MySQL is available/can't be determined.
|
|
40232
|
+
*/
|
|
40233
|
+
async function checkMysqlGate(server) {
|
|
40234
|
+
const { envId } = server.cloudBaseOptions ?? {};
|
|
40235
|
+
if (!envId)
|
|
40236
|
+
return null;
|
|
40237
|
+
if (MYSQL_GATE_CACHE.has(envId)) {
|
|
40238
|
+
if (MYSQL_GATE_CACHE.get(envId))
|
|
40239
|
+
return null; // cached as available
|
|
40240
|
+
// cached as blocked — return the gate error
|
|
40241
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
40242
|
+
success: false,
|
|
40243
|
+
errorCode: "MYSQL_NOT_AVAILABLE",
|
|
40244
|
+
message: `This environment (${envId}) does not have MySQL. Use managePgDatabase / queryPgDatabase instead.`,
|
|
40245
|
+
nextActions: [{ tool: "managePgDatabase", params: { action: "execute" }, description: "Use managePgDatabase" }],
|
|
40246
|
+
});
|
|
40247
|
+
}
|
|
40248
|
+
try {
|
|
40249
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions: { envId } });
|
|
40250
|
+
const envInfo = await manager.env.getEnvInfo?.();
|
|
40251
|
+
const backends = envInfo?.EnvInfo?.RuntimeBackends;
|
|
40252
|
+
if (backends?.postgresql === true && backends?.mysql !== true) {
|
|
40253
|
+
MYSQL_GATE_CACHE.set(envId, false);
|
|
40254
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
40255
|
+
success: false,
|
|
40256
|
+
errorCode: "MYSQL_NOT_AVAILABLE",
|
|
40257
|
+
message: `This environment (${envId}) has PostgreSQL enabled but MySQL is NOT available. Use managePgDatabase / queryPgDatabase instead of manageSqlDatabase / querySqlDatabase.`,
|
|
40258
|
+
nextActions: [{ tool: "managePgDatabase", params: { action: "execute" }, description: "Use managePgDatabase for PostgreSQL operations" }],
|
|
40259
|
+
});
|
|
40260
|
+
}
|
|
40261
|
+
}
|
|
40262
|
+
catch { /* probe failure — allow through */ }
|
|
40263
|
+
MYSQL_GATE_CACHE.set(envId, true);
|
|
40264
|
+
return null;
|
|
40265
|
+
}
|
|
40105
40266
|
const QUERY_SQL_DATABASE = "querySqlDatabase";
|
|
40106
40267
|
const MANAGE_SQL_DATABASE = "manageSqlDatabase";
|
|
40107
40268
|
const QUERY_PERMISSIONS = "queryPermissions";
|
|
@@ -40929,8 +41090,8 @@ function registerSQLDatabaseTools(server) {
|
|
|
40929
41090
|
server,
|
|
40930
41091
|
};
|
|
40931
41092
|
server.registerTool?.(QUERY_SQL_DATABASE, {
|
|
40932
|
-
title: "
|
|
40933
|
-
description: "
|
|
41093
|
+
title: "查询 CloudBase MySQL 数据库状态或执行只读 SQL",
|
|
41094
|
+
description: "查询 CloudBase MySQL 数据库信息。支持执行只读 SQL、查询 MySQL 开通结果、查询 MySQL 任务状态,以及获取当前实例上下文信息。",
|
|
40934
41095
|
inputSchema: {
|
|
40935
41096
|
action: zod_1.z
|
|
40936
41097
|
.enum(QUERY_ACTIONS)
|
|
@@ -40957,6 +41118,9 @@ function registerSQLDatabaseTools(server) {
|
|
|
40957
41118
|
category: CATEGORY,
|
|
40958
41119
|
},
|
|
40959
41120
|
}, async (args) => {
|
|
41121
|
+
const gate = await checkMysqlGate(server);
|
|
41122
|
+
if (gate)
|
|
41123
|
+
return gate;
|
|
40960
41124
|
switch (args.action) {
|
|
40961
41125
|
case "runQuery":
|
|
40962
41126
|
return handleRunQuery(args, context);
|
|
@@ -40972,8 +41136,8 @@ function registerSQLDatabaseTools(server) {
|
|
|
40972
41136
|
}
|
|
40973
41137
|
});
|
|
40974
41138
|
server.registerTool?.(MANAGE_SQL_DATABASE, {
|
|
40975
|
-
title: "
|
|
40976
|
-
description: "
|
|
41139
|
+
title: "管理 CloudBase MySQL 数据库生命周期或执行写入 SQL",
|
|
41140
|
+
description: "管理 CloudBase MySQL 数据库资源。支持开通 MySQL、销毁 MySQL、执行写入 SQL/DDL,以及初始化数据库 Schema。注意:必须先开通 MySQL(action=provisionMySQL,confirm=true)才能执行 runStatement 或 initializeSchema。若 MySQL 尚未开通,工具会返回 MYSQL_NOT_CREATED 并给出开通的 nextAction 提示。",
|
|
40977
41141
|
inputSchema: {
|
|
40978
41142
|
action: zod_1.z
|
|
40979
41143
|
.enum(MANAGE_ACTIONS)
|
|
@@ -41021,6 +41185,9 @@ function registerSQLDatabaseTools(server) {
|
|
|
41021
41185
|
category: CATEGORY,
|
|
41022
41186
|
},
|
|
41023
41187
|
}, async (args) => {
|
|
41188
|
+
const gate = await checkMysqlGate(server);
|
|
41189
|
+
if (gate)
|
|
41190
|
+
return gate;
|
|
41024
41191
|
switch (args.action) {
|
|
41025
41192
|
case "provisionMySQL":
|
|
41026
41193
|
return handleProvisionMySQL(args, context);
|
|
@@ -55763,7 +55930,7 @@ if (shouldUseConsole()) {
|
|
|
55763
55930
|
stderrLevels: ['error', 'warn', 'info', 'debug'], // All logs go to stderr
|
|
55764
55931
|
}));
|
|
55765
55932
|
}
|
|
55766
|
-
if (!(0, cloud_mode_js_1.isCloudMode)()) {
|
|
55933
|
+
if (!(0, cloud_mode_js_1.isCloudMode)() && process.env.CLOUDBASE_MCP_DISABLE_LOG_FILE !== 'true') {
|
|
55767
55934
|
// File transport with daily rotation
|
|
55768
55935
|
transports.push(new DailyRotateFile({
|
|
55769
55936
|
dirname: logDir,
|
|
@@ -67273,7 +67440,7 @@ function ReadableState(options, stream) {
|
|
|
67273
67440
|
this.decoder = null;
|
|
67274
67441
|
this.encoding = null;
|
|
67275
67442
|
if (options.encoding) {
|
|
67276
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(99198)
|
|
67443
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(99198).StringDecoder);
|
|
67277
67444
|
this.decoder = new StringDecoder(options.encoding);
|
|
67278
67445
|
this.encoding = options.encoding;
|
|
67279
67446
|
}
|
|
@@ -67429,7 +67596,7 @@ Readable.prototype.isPaused = function () {
|
|
|
67429
67596
|
|
|
67430
67597
|
// backwards compatibility.
|
|
67431
67598
|
Readable.prototype.setEncoding = function (enc) {
|
|
67432
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(99198)
|
|
67599
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(99198).StringDecoder);
|
|
67433
67600
|
this._readableState.decoder = new StringDecoder(enc);
|
|
67434
67601
|
this._readableState.encoding = enc;
|
|
67435
67602
|
return this;
|
|
@@ -68523,7 +68690,7 @@ function _isUint8Array(obj) {
|
|
|
68523
68690
|
var destroyImpl = __webpack_require__(75896);
|
|
68524
68691
|
var _require = __webpack_require__(65291),
|
|
68525
68692
|
getHighWaterMark = _require.getHighWaterMark;
|
|
68526
|
-
var _require$codes = (__webpack_require__(30113)
|
|
68693
|
+
var _require$codes = (__webpack_require__(30113).codes),
|
|
68527
68694
|
ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
|
|
68528
68695
|
ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
|
|
68529
68696
|
ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
|
|
@@ -83399,7 +83566,7 @@ Har.prototype.options = function (options) {
|
|
|
83399
83566
|
return options
|
|
83400
83567
|
}
|
|
83401
83568
|
|
|
83402
|
-
exports.
|
|
83569
|
+
exports.Har = Har
|
|
83403
83570
|
|
|
83404
83571
|
|
|
83405
83572
|
/***/ }),
|
|
@@ -88853,6 +89020,7 @@ exports.MANAGE_FUNCTION_ACTIONS = [
|
|
|
88853
89020
|
"attachLayer",
|
|
88854
89021
|
"detachLayer",
|
|
88855
89022
|
"updateFunctionLayers",
|
|
89023
|
+
"incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
|
|
88856
89024
|
];
|
|
88857
89025
|
const VPC_SCHEMA = zod_1.z.object({
|
|
88858
89026
|
vpcId: zod_1.z.string(),
|
|
@@ -89034,6 +89202,7 @@ function wrapFunctionOperationError(operation, functionName, functionRootPath, e
|
|
|
89034
89202
|
}
|
|
89035
89203
|
function registerFunctionTools(server) {
|
|
89036
89204
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
89205
|
+
const deployOverrides = server.pluginOptions?.functions;
|
|
89037
89206
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
89038
89207
|
const buildEnvelope = (data, message, nextActions) => ({
|
|
89039
89208
|
success: true,
|
|
@@ -89398,6 +89567,16 @@ function registerFunctionTools(server) {
|
|
|
89398
89567
|
ensureActionAllowedInCloudMode(input);
|
|
89399
89568
|
switch (input.action) {
|
|
89400
89569
|
case "createFunction": {
|
|
89570
|
+
if (deployOverrides?.createFunction) {
|
|
89571
|
+
const result = await deployOverrides.createFunction({
|
|
89572
|
+
functionName: String(input.func?.name ?? input.functionName ?? ''),
|
|
89573
|
+
functionRootPath: input.functionRootPath ?? '',
|
|
89574
|
+
runtime: input.func?.runtime,
|
|
89575
|
+
force: input.force,
|
|
89576
|
+
installDependency: input.func?.installDependency,
|
|
89577
|
+
});
|
|
89578
|
+
return buildEnvelope({ action: input.action, result }, '云函数部署成功(override)');
|
|
89579
|
+
}
|
|
89401
89580
|
if (!input.func?.name || typeof input.func.name !== "string") {
|
|
89402
89581
|
throw new Error("createFunction 操作时,func.name 参数是必需的");
|
|
89403
89582
|
}
|
|
@@ -89485,6 +89664,15 @@ function registerFunctionTools(server) {
|
|
|
89485
89664
|
}, message, nextActions);
|
|
89486
89665
|
}
|
|
89487
89666
|
case "updateFunctionCode": {
|
|
89667
|
+
if (deployOverrides?.updateFunctionCode) {
|
|
89668
|
+
const result = await deployOverrides.updateFunctionCode({
|
|
89669
|
+
functionName: input.functionName ?? '',
|
|
89670
|
+
functionRootPath: input.functionRootPath ?? '',
|
|
89671
|
+
force: input.force,
|
|
89672
|
+
installDependency: input.func?.installDependency,
|
|
89673
|
+
});
|
|
89674
|
+
return buildEnvelope({ action: input.action, result }, '云函数代码更新成功(override)');
|
|
89675
|
+
}
|
|
89488
89676
|
if (!input.functionName) {
|
|
89489
89677
|
throw new Error("updateFunctionCode 操作时,functionName 参数是必需的");
|
|
89490
89678
|
}
|
|
@@ -89818,6 +90006,19 @@ function registerFunctionTools(server) {
|
|
|
89818
90006
|
]);
|
|
89819
90007
|
}
|
|
89820
90008
|
default:
|
|
90009
|
+
// incrementalDeployFunction:无默认实现,必须通过 pluginOptions 注入
|
|
90010
|
+
if (input.action === 'incrementalDeployFunction') {
|
|
90011
|
+
const fn = deployOverrides?.incrementalDeployFunction;
|
|
90012
|
+
if (!fn) {
|
|
90013
|
+
throw new Error('incrementalDeployFunction 需要通过 pluginOptions.functions.incrementalDeployFunction 注入实现(仅在支持的 IDE 环境中可用)');
|
|
90014
|
+
}
|
|
90015
|
+
const result = await fn({
|
|
90016
|
+
functionName: input.functionName ?? '',
|
|
90017
|
+
functionRootPath: input.functionRootPath ?? '',
|
|
90018
|
+
incrementalFile: input.incrementalFile ?? '',
|
|
90019
|
+
});
|
|
90020
|
+
return buildEnvelope({ action: input.action, result }, '云函数增量部署成功');
|
|
90021
|
+
}
|
|
89821
90022
|
throw new Error(`不支持的操作类型: ${input.action}`);
|
|
89822
90023
|
}
|
|
89823
90024
|
};
|
|
@@ -89890,7 +90091,7 @@ function registerFunctionTools(server) {
|
|
|
89890
90091
|
inputSchema: {
|
|
89891
90092
|
action: zod_1.z
|
|
89892
90093
|
.enum(exports.MANAGE_FUNCTION_ACTIONS)
|
|
89893
|
-
.describe("写操作类型,例如 createFunction、updateFunctionCode、invokeFunction、deleteFunction、" +
|
|
90094
|
+
.describe("写操作类型,例如 createFunction、updateFunctionCode、incrementalDeployFunction、invokeFunction、deleteFunction、" +
|
|
89894
90095
|
"createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、attachLayer、detachLayer"),
|
|
89895
90096
|
func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
|
|
89896
90097
|
functionRootPath: zod_1.z.string().optional().describe("创建或更新函数代码时默认推荐的本地目录方式。" +
|
|
@@ -89926,6 +90127,7 @@ function registerFunctionTools(server) {
|
|
|
89926
90127
|
.describe("updateFunctionLayers 的目标层列表,顺序即最终顺序"),
|
|
89927
90128
|
codeSecret: zod_1.z.string().optional().describe("层绑定时的代码保护密钥"),
|
|
89928
90129
|
confirm: zod_1.z.boolean().optional().describe("危险操作确认开关。deleteFunction、deleteFunctionTrigger、deleteLayerVersion、detachLayer 等删除类操作需要显式传入 confirm=true"),
|
|
90130
|
+
incrementalFile: zod_1.z.string().optional().describe("incrementalDeployFunction 增量部署时的变更文件路径"),
|
|
89929
90131
|
},
|
|
89930
90132
|
annotations: {
|
|
89931
90133
|
readOnlyHint: false,
|
|
@@ -96135,7 +96337,7 @@ createErrorType('ERR_UNKNOWN_ENCODING', function (arg) {
|
|
|
96135
96337
|
}, TypeError);
|
|
96136
96338
|
createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event');
|
|
96137
96339
|
|
|
96138
|
-
module.exports.
|
|
96340
|
+
module.exports.codes = codes;
|
|
96139
96341
|
|
|
96140
96342
|
|
|
96141
96343
|
/***/ }),
|
|
@@ -102072,6 +102274,7 @@ exports.createCloudBaseMcpServer = createCloudBaseMcpServer;
|
|
|
102072
102274
|
exports.getDefaultServer = getDefaultServer;
|
|
102073
102275
|
const mcp_js_1 = __webpack_require__(7011);
|
|
102074
102276
|
const databaseNoSQL_js_1 = __webpack_require__(73903);
|
|
102277
|
+
const databasePG_js_1 = __webpack_require__(56473);
|
|
102075
102278
|
const databaseSQL_js_1 = __webpack_require__(9782);
|
|
102076
102279
|
const download_js_1 = __webpack_require__(9097);
|
|
102077
102280
|
const env_js_1 = __webpack_require__(622);
|
|
@@ -102079,6 +102282,7 @@ const functions_js_1 = __webpack_require__(25936);
|
|
|
102079
102282
|
const hosting_js_1 = __webpack_require__(37279);
|
|
102080
102283
|
const rag_js_1 = __webpack_require__(64215);
|
|
102081
102284
|
const setup_js_1 = __webpack_require__(76556);
|
|
102285
|
+
const storagePG_js_1 = __webpack_require__(39189);
|
|
102082
102286
|
const storage_js_1 = __webpack_require__(94848);
|
|
102083
102287
|
// import { registerMiniprogramTools } from "./tools/miniprogram.js";
|
|
102084
102288
|
const types_js_1 = __webpack_require__(1294);
|
|
@@ -102101,6 +102305,8 @@ const tool_wrapper_js_1 = __webpack_require__(71363);
|
|
|
102101
102305
|
const DEFAULT_PLUGINS = [
|
|
102102
102306
|
"env",
|
|
102103
102307
|
"database",
|
|
102308
|
+
"pg_database",
|
|
102309
|
+
"pg_storage",
|
|
102104
102310
|
"functions",
|
|
102105
102311
|
"hosting",
|
|
102106
102312
|
"storage",
|
|
@@ -102123,13 +102329,31 @@ function registerDatabase(server) {
|
|
|
102123
102329
|
if (!(0, tencent_cloud_js_1.isInternationalRegion)(region)) {
|
|
102124
102330
|
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
102125
102331
|
}
|
|
102126
|
-
|
|
102332
|
+
const pgPluginEnabled = server.enabledPlugins?.some((pluginName) => pluginName === "pg_database" || pluginName === "pg_storage");
|
|
102333
|
+
if (!pgPluginEnabled) {
|
|
102334
|
+
(0, databaseSQL_js_1.registerSQLDatabaseTools)(server);
|
|
102335
|
+
}
|
|
102127
102336
|
(0, dataModel_js_1.registerDataModelTools)(server);
|
|
102128
102337
|
}
|
|
102338
|
+
function registerMysqlDatabase(server) {
|
|
102339
|
+
(0, databaseSQL_js_1.registerSQLDatabaseTools)(server);
|
|
102340
|
+
}
|
|
102341
|
+
function registerNoSQLDatabase(server) {
|
|
102342
|
+
const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
|
|
102343
|
+
if (!(0, tencent_cloud_js_1.isInternationalRegion)(region)) {
|
|
102344
|
+
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
102345
|
+
}
|
|
102346
|
+
}
|
|
102129
102347
|
// 可用插件映射
|
|
102130
102348
|
const AVAILABLE_PLUGINS = {
|
|
102131
102349
|
env: { name: "env", register: env_js_1.registerEnvTools },
|
|
102132
102350
|
database: { name: "database", register: registerDatabase },
|
|
102351
|
+
mysql_database: { name: "mysql_database", register: registerMysqlDatabase },
|
|
102352
|
+
pg_database: { name: "pg_database", register: databasePG_js_1.registerPGDatabaseTools },
|
|
102353
|
+
pg_storage: { name: "pg_storage", register: storagePG_js_1.registerPGStorageTools },
|
|
102354
|
+
"database-nosql": { name: "database-nosql", register: registerNoSQLDatabase },
|
|
102355
|
+
"database-sql": { name: "database-sql", register: databaseSQL_js_1.registerSQLDatabaseTools },
|
|
102356
|
+
"data-model": { name: "data-model", register: dataModel_js_1.registerDataModelTools },
|
|
102133
102357
|
functions: { name: "functions", register: functions_js_1.registerFunctionTools },
|
|
102134
102358
|
hosting: { name: "hosting", register: hosting_js_1.registerHostingTools },
|
|
102135
102359
|
storage: { name: "storage", register: storage_js_1.registerStorageTools },
|
|
@@ -102153,6 +102377,9 @@ const PLUGIN_ALIASES = {
|
|
|
102153
102377
|
"security-rules": "permissions",
|
|
102154
102378
|
"secret-rule": "permissions",
|
|
102155
102379
|
"secret-rules": "permissions",
|
|
102380
|
+
mysql: "mysql_database",
|
|
102381
|
+
"mysql-database": "mysql_database",
|
|
102382
|
+
"sql-database": "mysql_database",
|
|
102156
102383
|
users: "permissions",
|
|
102157
102384
|
};
|
|
102158
102385
|
function normalizePluginName(name) {
|
|
@@ -102217,7 +102444,7 @@ function parseEnabledPlugins(pluginsEnabled, pluginsDisabled) {
|
|
|
102217
102444
|
* await server.connect(transport);
|
|
102218
102445
|
*/
|
|
102219
102446
|
async function createCloudBaseMcpServer(options) {
|
|
102220
|
-
const { name = "cloudbase-mcp", version = "1.0.0", enableTelemetry = true, cloudBaseOptions, authOptions, cloudMode = false, ide, logger, pluginsEnabled, pluginsDisabled, } = options ?? {};
|
|
102447
|
+
const { name = "cloudbase-mcp", version = "1.0.0", enableTelemetry = true, cloudBaseOptions, authOptions, cloudMode = false, ide, logger, pluginsEnabled, pluginsDisabled, pluginOptions, } = options ?? {};
|
|
102221
102448
|
// Enable cloud mode if specified
|
|
102222
102449
|
if (cloudMode) {
|
|
102223
102450
|
(0, cloud_mode_js_1.enableCloudMode)();
|
|
@@ -102232,20 +102459,31 @@ async function createCloudBaseMcpServer(options) {
|
|
|
102232
102459
|
...(ide === "CodeBuddy" ? { logging: {} } : {}),
|
|
102233
102460
|
},
|
|
102234
102461
|
});
|
|
102462
|
+
// 初始化 toolDefs,用于外部提取工具列表(如微信 IDE)
|
|
102463
|
+
server.toolDefs = [];
|
|
102235
102464
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
102236
|
-
server.registerTool = ((name, meta, handler) =>
|
|
102237
|
-
|
|
102238
|
-
|
|
102239
|
-
|
|
102240
|
-
|
|
102241
|
-
|
|
102242
|
-
|
|
102243
|
-
|
|
102244
|
-
|
|
102465
|
+
server.registerTool = ((name, meta, handler) => {
|
|
102466
|
+
// 同步记录到 toolDefs
|
|
102467
|
+
server.toolDefs.push({
|
|
102468
|
+
name,
|
|
102469
|
+
description: meta?.description ?? meta?.title ?? '',
|
|
102470
|
+
inputSchema: meta?.inputSchema ?? {},
|
|
102471
|
+
handler,
|
|
102472
|
+
});
|
|
102473
|
+
return originalRegisterTool(name, meta, async (args) => {
|
|
102474
|
+
try {
|
|
102475
|
+
return await handler(args);
|
|
102245
102476
|
}
|
|
102246
|
-
|
|
102247
|
-
|
|
102248
|
-
|
|
102477
|
+
catch (error) {
|
|
102478
|
+
if ((0, tool_result_js_1.isToolPayloadError)(error)) {
|
|
102479
|
+
// 在结构化错误返回中注入 MCP 版本号
|
|
102480
|
+
const payload = { ...error.payload, mcpVersion: version };
|
|
102481
|
+
return (0, tool_result_js_1.buildJsonToolResult)(payload);
|
|
102482
|
+
}
|
|
102483
|
+
throw error;
|
|
102484
|
+
}
|
|
102485
|
+
});
|
|
102486
|
+
});
|
|
102249
102487
|
// Only set logging handler if logging capability is declared
|
|
102250
102488
|
if (ide === "CodeBuddy") {
|
|
102251
102489
|
server.server.setRequestHandler(types_js_1.SetLevelRequestSchema, (request, extra) => {
|
|
@@ -102260,6 +102498,10 @@ async function createCloudBaseMcpServer(options) {
|
|
|
102260
102498
|
if (authOptions) {
|
|
102261
102499
|
server.authOptions = authOptions;
|
|
102262
102500
|
}
|
|
102501
|
+
// Store pluginOptions in server instance for plugins to access
|
|
102502
|
+
if (pluginOptions) {
|
|
102503
|
+
server.pluginOptions = pluginOptions;
|
|
102504
|
+
}
|
|
102263
102505
|
// Store ide in server instance for telemetry
|
|
102264
102506
|
if (ide) {
|
|
102265
102507
|
server.ide = ide;
|
|
@@ -102277,6 +102519,7 @@ async function createCloudBaseMcpServer(options) {
|
|
|
102277
102519
|
}
|
|
102278
102520
|
// Register plugins based on configuration
|
|
102279
102521
|
const enabledPlugins = parseEnabledPlugins(pluginsEnabled, pluginsDisabled);
|
|
102522
|
+
server.enabledPlugins = enabledPlugins;
|
|
102280
102523
|
for (const pluginName of enabledPlugins) {
|
|
102281
102524
|
const plugin = AVAILABLE_PLUGINS[pluginName];
|
|
102282
102525
|
if (plugin) {
|
|
@@ -104358,7 +104601,7 @@ function ReadableState(options, stream) {
|
|
|
104358
104601
|
this.decoder = null;
|
|
104359
104602
|
this.encoding = null;
|
|
104360
104603
|
if (options.encoding) {
|
|
104361
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(35360)
|
|
104604
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(35360).StringDecoder);
|
|
104362
104605
|
this.decoder = new StringDecoder(options.encoding);
|
|
104363
104606
|
this.encoding = options.encoding;
|
|
104364
104607
|
}
|
|
@@ -104514,7 +104757,7 @@ Readable.prototype.isPaused = function () {
|
|
|
104514
104757
|
|
|
104515
104758
|
// backwards compatibility.
|
|
104516
104759
|
Readable.prototype.setEncoding = function (enc) {
|
|
104517
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(35360)
|
|
104760
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(35360).StringDecoder);
|
|
104518
104761
|
this._readableState.decoder = new StringDecoder(enc);
|
|
104519
104762
|
this._readableState.encoding = enc;
|
|
104520
104763
|
return this;
|
|
@@ -105437,7 +105680,7 @@ function ReadableState(options, stream) {
|
|
|
105437
105680
|
this.decoder = null;
|
|
105438
105681
|
this.encoding = null;
|
|
105439
105682
|
if (options.encoding) {
|
|
105440
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(7357)
|
|
105683
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(7357).StringDecoder);
|
|
105441
105684
|
this.decoder = new StringDecoder(options.encoding);
|
|
105442
105685
|
this.encoding = options.encoding;
|
|
105443
105686
|
}
|
|
@@ -105593,7 +105836,7 @@ Readable.prototype.isPaused = function () {
|
|
|
105593
105836
|
|
|
105594
105837
|
// backwards compatibility.
|
|
105595
105838
|
Readable.prototype.setEncoding = function (enc) {
|
|
105596
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(7357)
|
|
105839
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(7357).StringDecoder);
|
|
105597
105840
|
this._readableState.decoder = new StringDecoder(enc);
|
|
105598
105841
|
this._readableState.encoding = enc;
|
|
105599
105842
|
return this;
|
|
@@ -114157,7 +114400,7 @@ function normalizeEncoding(enc) {
|
|
|
114157
114400
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
114158
114401
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
114159
114402
|
// characters.
|
|
114160
|
-
exports.
|
|
114403
|
+
exports.StringDecoder = StringDecoder;
|
|
114161
114404
|
function StringDecoder(encoding) {
|
|
114162
114405
|
this.encoding = normalizeEncoding(encoding);
|
|
114163
114406
|
var nb;
|
|
@@ -121509,6 +121752,202 @@ function Extract (opts) {
|
|
|
121509
121752
|
}
|
|
121510
121753
|
|
|
121511
121754
|
|
|
121755
|
+
/***/ }),
|
|
121756
|
+
|
|
121757
|
+
/***/ 39189:
|
|
121758
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
121759
|
+
|
|
121760
|
+
"use strict";
|
|
121761
|
+
|
|
121762
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
121763
|
+
exports.registerPGStorageTools = registerPGStorageTools;
|
|
121764
|
+
const zod_1 = __webpack_require__(21614);
|
|
121765
|
+
const tool_result_js_1 = __webpack_require__(9835);
|
|
121766
|
+
const CATEGORY = "PostgreSQL storage";
|
|
121767
|
+
const QUERY_PG_STORAGE = "queryPgStorage";
|
|
121768
|
+
const STORAGE_ACTIONS = [
|
|
121769
|
+
"buckets",
|
|
121770
|
+
"config",
|
|
121771
|
+
"uploadPlan",
|
|
121772
|
+
"objectInfo",
|
|
121773
|
+
"signUpload",
|
|
121774
|
+
"signDownload",
|
|
121775
|
+
];
|
|
121776
|
+
function buildPgStorageResult(payload) {
|
|
121777
|
+
return (0, tool_result_js_1.buildJsonToolResult)(payload);
|
|
121778
|
+
}
|
|
121779
|
+
function requireBucket(args) {
|
|
121780
|
+
if (!args.bucket?.trim()) {
|
|
121781
|
+
return buildPgStorageResult({
|
|
121782
|
+
success: false,
|
|
121783
|
+
errorCode: "BUCKET_REQUIRED",
|
|
121784
|
+
message: "Provide bucket when querying PostgreSQL storage.",
|
|
121785
|
+
});
|
|
121786
|
+
}
|
|
121787
|
+
return undefined;
|
|
121788
|
+
}
|
|
121789
|
+
function normalizeObjects(args) {
|
|
121790
|
+
if (args.objects?.length) {
|
|
121791
|
+
return args.objects.map((object) => ({
|
|
121792
|
+
objectKey: object.objectKey,
|
|
121793
|
+
contentType: object.contentType ?? "application/octet-stream",
|
|
121794
|
+
sizeBytes: object.sizeBytes ?? null,
|
|
121795
|
+
}));
|
|
121796
|
+
}
|
|
121797
|
+
const objectKeys = args.objectKeys?.length
|
|
121798
|
+
? args.objectKeys
|
|
121799
|
+
: args.objectKey
|
|
121800
|
+
? [args.objectKey]
|
|
121801
|
+
: [];
|
|
121802
|
+
return objectKeys.map((objectKey) => ({
|
|
121803
|
+
objectKey,
|
|
121804
|
+
contentType: "application/octet-stream",
|
|
121805
|
+
sizeBytes: null,
|
|
121806
|
+
}));
|
|
121807
|
+
}
|
|
121808
|
+
function buildHttpApiPlan(server, bucket) {
|
|
121809
|
+
const envId = server.cloudBaseOptions?.envId ?? "${envId}";
|
|
121810
|
+
const region = server.cloudBaseOptions?.region ?? "${region}";
|
|
121811
|
+
return {
|
|
121812
|
+
envId,
|
|
121813
|
+
region,
|
|
121814
|
+
bucket,
|
|
121815
|
+
auth: "accessToken",
|
|
121816
|
+
transport: "CloudBase HTTP API",
|
|
121817
|
+
endpointTemplate: "https://api.weixin.qq.com/tcb/{storageAction}?access_token={accessToken}",
|
|
121818
|
+
headersTemplate: {
|
|
121819
|
+
"content-type": "application/json",
|
|
121820
|
+
},
|
|
121821
|
+
bodyTemplate: {
|
|
121822
|
+
env: envId,
|
|
121823
|
+
bucket,
|
|
121824
|
+
},
|
|
121825
|
+
};
|
|
121826
|
+
}
|
|
121827
|
+
async function handleBuckets(server) {
|
|
121828
|
+
return buildPgStorageResult({
|
|
121829
|
+
success: true,
|
|
121830
|
+
data: {
|
|
121831
|
+
capability: "storageManagerSdkOrHttpApi",
|
|
121832
|
+
envId: server.cloudBaseOptions?.envId ?? null,
|
|
121833
|
+
supportedActions: ["buckets", "config", "uploadPlan", "objectInfo"],
|
|
121834
|
+
note: "PG environment storage is exposed as metadata and implementation plans in MCP. Application data-plane upload/download should use SDK or HTTP API code.",
|
|
121835
|
+
},
|
|
121836
|
+
message: "Resolved PostgreSQL storage capability summary. Use uploadPlan for application-side upload implementation.",
|
|
121837
|
+
});
|
|
121838
|
+
}
|
|
121839
|
+
async function handleUploadPlan(args, server) {
|
|
121840
|
+
const missingBucket = requireBucket(args);
|
|
121841
|
+
if (missingBucket)
|
|
121842
|
+
return missingBucket;
|
|
121843
|
+
const objects = normalizeObjects(args);
|
|
121844
|
+
const bucket = args.bucket;
|
|
121845
|
+
return buildPgStorageResult({
|
|
121846
|
+
success: true,
|
|
121847
|
+
data: {
|
|
121848
|
+
action: "uploadPlan",
|
|
121849
|
+
uploadMode: "directHttpApi",
|
|
121850
|
+
implementation: "use_sdk_or_http_api_in_app_code",
|
|
121851
|
+
...buildHttpApiPlan(server, bucket),
|
|
121852
|
+
objectCount: objects.length,
|
|
121853
|
+
objects,
|
|
121854
|
+
constraints: {
|
|
121855
|
+
mcpDoesNotReadLocalFiles: true,
|
|
121856
|
+
mcpDoesNotStreamFileContent: true,
|
|
121857
|
+
noSignedUrlByDefault: true,
|
|
121858
|
+
multiFileOutputIsMetadataOnly: true,
|
|
121859
|
+
},
|
|
121860
|
+
steps: [
|
|
121861
|
+
"Get an application-side or service-side access token.",
|
|
121862
|
+
"Call the CloudBase storage HTTP API or Manager SDK from application code.",
|
|
121863
|
+
"Upload file bytes outside MCP, then call objectInfo if metadata verification is needed.",
|
|
121864
|
+
],
|
|
121865
|
+
},
|
|
121866
|
+
message: "Generated an HTTP API upload plan without signed URLs or file content. This keeps MCP token usage stable for multi-file uploads.",
|
|
121867
|
+
});
|
|
121868
|
+
}
|
|
121869
|
+
async function handleObjectInfo(args, server) {
|
|
121870
|
+
const missingBucket = requireBucket(args);
|
|
121871
|
+
if (missingBucket)
|
|
121872
|
+
return missingBucket;
|
|
121873
|
+
const objectKeys = normalizeObjects(args).map((object) => object.objectKey);
|
|
121874
|
+
return buildPgStorageResult({
|
|
121875
|
+
success: true,
|
|
121876
|
+
data: {
|
|
121877
|
+
action: "objectInfo",
|
|
121878
|
+
...buildHttpApiPlan(server, args.bucket),
|
|
121879
|
+
objectKeys,
|
|
121880
|
+
implementation: "use_sdk_or_http_api_in_app_code",
|
|
121881
|
+
mcpFileIo: false,
|
|
121882
|
+
},
|
|
121883
|
+
message: "Generated an object metadata query plan. Use application code or service code for actual storage data-plane calls.",
|
|
121884
|
+
});
|
|
121885
|
+
}
|
|
121886
|
+
async function handleSignedUrl(args) {
|
|
121887
|
+
const missingBucket = requireBucket(args);
|
|
121888
|
+
if (missingBucket)
|
|
121889
|
+
return missingBucket;
|
|
121890
|
+
return buildPgStorageResult({
|
|
121891
|
+
success: false,
|
|
121892
|
+
errorCode: "SIGNED_URL_NOT_DEFAULT",
|
|
121893
|
+
data: {
|
|
121894
|
+
action: args.action,
|
|
121895
|
+
bucket: args.bucket,
|
|
121896
|
+
expiresIn: args.expiresIn ?? 900,
|
|
121897
|
+
recommendation: "Use queryPgStorage(action=uploadPlan) and HTTP API/SDK application code for multi-file flows.",
|
|
121898
|
+
},
|
|
121899
|
+
message: "Signed URL generation is intentionally not the default PG storage path because multi-file signed outputs waste tokens. Use uploadPlan unless a delegated one-off upload/download is explicitly required.",
|
|
121900
|
+
});
|
|
121901
|
+
}
|
|
121902
|
+
function registerPGStorageTools(server) {
|
|
121903
|
+
server.registerTool?.(QUERY_PG_STORAGE, {
|
|
121904
|
+
title: "查询 PostgreSQL 环境云存储能力和上传方案",
|
|
121905
|
+
description: "查询 CloudBase PostgreSQL 环境下的云存储能力。返回 bucket/config 能力摘要、对象信息查询方案,以及基于 HTTP API 或 SDK 的上传实现方案;不会读取本地文件,也不会默认输出大量签名 URL。",
|
|
121906
|
+
inputSchema: {
|
|
121907
|
+
action: zod_1.z
|
|
121908
|
+
.enum(STORAGE_ACTIONS)
|
|
121909
|
+
.describe("操作类型:buckets/config=查询存储能力摘要;uploadPlan=生成 HTTP API/SDK 上传方案;objectInfo=生成对象元信息查询方案;signUpload/signDownload=显式的一次性签名 URL 请求占位"),
|
|
121910
|
+
bucket: zod_1.z.string().optional().describe("云存储 bucket 名称"),
|
|
121911
|
+
objectKey: zod_1.z.string().optional().describe("单个对象 key"),
|
|
121912
|
+
objectKeys: zod_1.z.array(zod_1.z.string()).optional().describe("多个对象 key,用于对象元信息查询规划"),
|
|
121913
|
+
objects: zod_1.z
|
|
121914
|
+
.array(zod_1.z.object({
|
|
121915
|
+
objectKey: zod_1.z.string(),
|
|
121916
|
+
contentType: zod_1.z.string().optional(),
|
|
121917
|
+
sizeBytes: zod_1.z.number().int().nonnegative().optional(),
|
|
121918
|
+
}))
|
|
121919
|
+
.optional()
|
|
121920
|
+
.describe("待上传对象的元信息。文件字节内容不会通过 MCP 传递。"),
|
|
121921
|
+
expiresIn: zod_1.z.number().int().min(60).max(86400).optional().describe("签名 URL 有效期,单位秒,范围 60 到 86400。"),
|
|
121922
|
+
},
|
|
121923
|
+
annotations: {
|
|
121924
|
+
readOnlyHint: true,
|
|
121925
|
+
openWorldHint: true,
|
|
121926
|
+
category: CATEGORY,
|
|
121927
|
+
},
|
|
121928
|
+
}, async (args) => {
|
|
121929
|
+
switch (args.action) {
|
|
121930
|
+
case "buckets":
|
|
121931
|
+
case "config":
|
|
121932
|
+
return handleBuckets(server);
|
|
121933
|
+
case "uploadPlan":
|
|
121934
|
+
return handleUploadPlan(args, server);
|
|
121935
|
+
case "objectInfo":
|
|
121936
|
+
return handleObjectInfo(args, server);
|
|
121937
|
+
case "signUpload":
|
|
121938
|
+
case "signDownload":
|
|
121939
|
+
return handleSignedUrl(args);
|
|
121940
|
+
default:
|
|
121941
|
+
return buildPgStorageResult({
|
|
121942
|
+
success: false,
|
|
121943
|
+
errorCode: "UNSUPPORTED_ACTION",
|
|
121944
|
+
message: `Unsupported PostgreSQL storage action: ${args.action}`,
|
|
121945
|
+
});
|
|
121946
|
+
}
|
|
121947
|
+
});
|
|
121948
|
+
}
|
|
121949
|
+
|
|
121950
|
+
|
|
121512
121951
|
/***/ }),
|
|
121513
121952
|
|
|
121514
121953
|
/***/ 39287:
|
|
@@ -131649,7 +132088,7 @@ Multipart.prototype.onRequest = function (options) {
|
|
|
131649
132088
|
self.body = self.build(parts, chunked)
|
|
131650
132089
|
}
|
|
131651
132090
|
|
|
131652
|
-
exports.
|
|
132091
|
+
exports.Multipart = Multipart
|
|
131653
132092
|
|
|
131654
132093
|
|
|
131655
132094
|
/***/ }),
|
|
@@ -133006,7 +133445,7 @@ var BufferList = __webpack_require__(80345);
|
|
|
133006
133445
|
var destroyImpl = __webpack_require__(75896);
|
|
133007
133446
|
var _require = __webpack_require__(65291),
|
|
133008
133447
|
getHighWaterMark = _require.getHighWaterMark;
|
|
133009
|
-
var _require$codes = (__webpack_require__(30113)
|
|
133448
|
+
var _require$codes = (__webpack_require__(30113).codes),
|
|
133010
133449
|
ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
|
|
133011
133450
|
ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF,
|
|
133012
133451
|
ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
|
|
@@ -133098,7 +133537,7 @@ function ReadableState(options, stream, isDuplex) {
|
|
|
133098
133537
|
this.decoder = null;
|
|
133099
133538
|
this.encoding = null;
|
|
133100
133539
|
if (options.encoding) {
|
|
133101
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(83141)
|
|
133540
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(83141).StringDecoder);
|
|
133102
133541
|
this.decoder = new StringDecoder(options.encoding);
|
|
133103
133542
|
this.encoding = options.encoding;
|
|
133104
133543
|
}
|
|
@@ -133241,7 +133680,7 @@ Readable.prototype.isPaused = function () {
|
|
|
133241
133680
|
|
|
133242
133681
|
// backwards compatibility.
|
|
133243
133682
|
Readable.prototype.setEncoding = function (enc) {
|
|
133244
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(83141)
|
|
133683
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(83141).StringDecoder);
|
|
133245
133684
|
var decoder = new StringDecoder(enc);
|
|
133246
133685
|
this._readableState.decoder = decoder;
|
|
133247
133686
|
// If setEncoding(null), decoder.encoding equals utf8
|
|
@@ -135157,7 +135596,7 @@ class TelemetryReporter {
|
|
|
135157
135596
|
const nodeVersion = process.version; // Node.js版本
|
|
135158
135597
|
const arch = os_1.default.arch(); // 系统架构
|
|
135159
135598
|
// 从构建时注入的版本号获取MCP版本信息
|
|
135160
|
-
const mcpVersion = process.env.npm_package_version || "2.
|
|
135599
|
+
const mcpVersion = process.env.npm_package_version || "2.22.0-beta.0" || 0;
|
|
135161
135600
|
return {
|
|
135162
135601
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
135163
135602
|
deviceId: this.deviceId,
|
|
@@ -152497,14 +152936,14 @@ exports.REQUEST_TIMEOUT = 15000;
|
|
|
152497
152936
|
|
|
152498
152937
|
|
|
152499
152938
|
exports.URL = __webpack_require__(76648)["interface"];
|
|
152500
|
-
|
|
152501
|
-
|
|
152502
|
-
|
|
152503
|
-
|
|
152504
|
-
|
|
152505
|
-
|
|
152506
|
-
|
|
152507
|
-
|
|
152939
|
+
exports.serializeURL = __webpack_require__(95484).serializeURL;
|
|
152940
|
+
exports.serializeURLOrigin = __webpack_require__(95484).serializeURLOrigin;
|
|
152941
|
+
exports.basicURLParse = __webpack_require__(95484).basicURLParse;
|
|
152942
|
+
exports.setTheUsername = __webpack_require__(95484).setTheUsername;
|
|
152943
|
+
exports.setThePassword = __webpack_require__(95484).setThePassword;
|
|
152944
|
+
exports.serializeHost = __webpack_require__(95484).serializeHost;
|
|
152945
|
+
exports.serializeInteger = __webpack_require__(95484).serializeInteger;
|
|
152946
|
+
exports.parseURL = __webpack_require__(95484).parseURL;
|
|
152508
152947
|
|
|
152509
152948
|
|
|
152510
152949
|
/***/ }),
|
|
@@ -158589,6 +159028,1264 @@ exports["default"] = applicator;
|
|
|
158589
159028
|
})));
|
|
158590
159029
|
|
|
158591
159030
|
|
|
159031
|
+
/***/ }),
|
|
159032
|
+
|
|
159033
|
+
/***/ 56473:
|
|
159034
|
+
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
159035
|
+
|
|
159036
|
+
"use strict";
|
|
159037
|
+
|
|
159038
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
159039
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
159040
|
+
};
|
|
159041
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
159042
|
+
exports.registerPGDatabaseTools = registerPGDatabaseTools;
|
|
159043
|
+
const node_fs_1 = __webpack_require__(89971);
|
|
159044
|
+
const node_path_1 = __importDefault(__webpack_require__(26743));
|
|
159045
|
+
const zod_1 = __webpack_require__(21614);
|
|
159046
|
+
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
159047
|
+
const tool_result_js_1 = __webpack_require__(9835);
|
|
159048
|
+
const CATEGORY = "PostgreSQL database";
|
|
159049
|
+
const QUERY_PG_DATABASE = "queryPgDatabase";
|
|
159050
|
+
const MANAGE_PG_DATABASE = "managePgDatabase";
|
|
159051
|
+
const QUERY_ACTIONS = [
|
|
159052
|
+
"context",
|
|
159053
|
+
"objects",
|
|
159054
|
+
"metadata",
|
|
159055
|
+
"schema",
|
|
159056
|
+
"sql",
|
|
159057
|
+
];
|
|
159058
|
+
const MANAGE_ACTIONS = [
|
|
159059
|
+
"init",
|
|
159060
|
+
"execute",
|
|
159061
|
+
"dryRun",
|
|
159062
|
+
"planMigration",
|
|
159063
|
+
"applyMigration",
|
|
159064
|
+
"listMigrations",
|
|
159065
|
+
"migrationDetail",
|
|
159066
|
+
"rollbackMigration",
|
|
159067
|
+
];
|
|
159068
|
+
function buildPgToolResult(payload) {
|
|
159069
|
+
return (0, tool_result_js_1.buildJsonToolResult)(payload);
|
|
159070
|
+
}
|
|
159071
|
+
function buildNextAction(tool, action, reason, suggestedArgs) {
|
|
159072
|
+
return {
|
|
159073
|
+
tool,
|
|
159074
|
+
action,
|
|
159075
|
+
suggested_args: suggestedArgs,
|
|
159076
|
+
required_params: undefined,
|
|
159077
|
+
reason,
|
|
159078
|
+
};
|
|
159079
|
+
}
|
|
159080
|
+
function getPgContextStorePath() {
|
|
159081
|
+
return (process.env.CLOUDBASE_PG_CONTEXT_PATH ??
|
|
159082
|
+
node_path_1.default.join(process.env.HOME ?? ".", ".cloudbase-mcp", "pg-context.json"));
|
|
159083
|
+
}
|
|
159084
|
+
async function loadStoredPgContext() {
|
|
159085
|
+
if (!process.env.CLOUDBASE_PG_CONTEXT_PATH) {
|
|
159086
|
+
return undefined;
|
|
159087
|
+
}
|
|
159088
|
+
try {
|
|
159089
|
+
const raw = await node_fs_1.promises.readFile(getPgContextStorePath(), "utf8");
|
|
159090
|
+
return JSON.parse(raw);
|
|
159091
|
+
}
|
|
159092
|
+
catch {
|
|
159093
|
+
return undefined;
|
|
159094
|
+
}
|
|
159095
|
+
}
|
|
159096
|
+
async function savePgContext(server, context) {
|
|
159097
|
+
server.pgRuntimeContext = context;
|
|
159098
|
+
if (!process.env.CLOUDBASE_PG_CONTEXT_PATH) {
|
|
159099
|
+
return;
|
|
159100
|
+
}
|
|
159101
|
+
const storePath = getPgContextStorePath();
|
|
159102
|
+
await node_fs_1.promises.mkdir(node_path_1.default.dirname(storePath), { recursive: true });
|
|
159103
|
+
await node_fs_1.promises.writeFile(storePath, JSON.stringify(context, null, 2), {
|
|
159104
|
+
encoding: "utf8",
|
|
159105
|
+
mode: 0o600,
|
|
159106
|
+
});
|
|
159107
|
+
await node_fs_1.promises.chmod(storePath, 0o600).catch(() => undefined);
|
|
159108
|
+
}
|
|
159109
|
+
async function getPgContext(server) {
|
|
159110
|
+
if (server.pgRuntimeContext) {
|
|
159111
|
+
return server.pgRuntimeContext;
|
|
159112
|
+
}
|
|
159113
|
+
const stored = await loadStoredPgContext();
|
|
159114
|
+
if (stored) {
|
|
159115
|
+
server.pgRuntimeContext = stored;
|
|
159116
|
+
}
|
|
159117
|
+
return stored;
|
|
159118
|
+
}
|
|
159119
|
+
function buildMissingContextResult() {
|
|
159120
|
+
return buildPgToolResult({
|
|
159121
|
+
success: false,
|
|
159122
|
+
errorCode: "PG_CONTEXT_NOT_INITIALIZED",
|
|
159123
|
+
message: "PostgreSQL context is not initialized yet. Call managePgDatabase(action=init) to bind the current CloudBase environment's built-in PostgreSQL runtime.",
|
|
159124
|
+
nextActions: [
|
|
159125
|
+
buildNextAction(MANAGE_PG_DATABASE, "init", "初始化 CloudBase PG 云端执行上下文,然后再查询 schema 或执行 SQL。", { action: "init" }),
|
|
159126
|
+
],
|
|
159127
|
+
});
|
|
159128
|
+
}
|
|
159129
|
+
function sanitizePgContext(context) {
|
|
159130
|
+
return {
|
|
159131
|
+
envId: context.envId,
|
|
159132
|
+
instanceId: context.instanceId,
|
|
159133
|
+
defaultSchema: context.defaultSchema,
|
|
159134
|
+
runtimeMode: context.runtimeMode,
|
|
159135
|
+
bootstrapMode: context.bootstrapMode,
|
|
159136
|
+
role: context.role,
|
|
159137
|
+
};
|
|
159138
|
+
}
|
|
159139
|
+
function normalizeLimit(limit, fallback = 20, max = 200) {
|
|
159140
|
+
if (!Number.isFinite(limit)) {
|
|
159141
|
+
return fallback;
|
|
159142
|
+
}
|
|
159143
|
+
return Math.max(1, Math.min(max, Math.floor(limit)));
|
|
159144
|
+
}
|
|
159145
|
+
function stripLeadingSqlComments(sql) {
|
|
159146
|
+
let normalized = sql.trim();
|
|
159147
|
+
while (normalized.length > 0) {
|
|
159148
|
+
if (normalized.startsWith("--")) {
|
|
159149
|
+
normalized = normalized.replace(/^--.*(?:\r?\n|$)/, "").trimStart();
|
|
159150
|
+
continue;
|
|
159151
|
+
}
|
|
159152
|
+
if (normalized.startsWith("#")) {
|
|
159153
|
+
normalized = normalized.replace(/^#.*(?:\r?\n|$)/, "").trimStart();
|
|
159154
|
+
continue;
|
|
159155
|
+
}
|
|
159156
|
+
if (normalized.startsWith("/*")) {
|
|
159157
|
+
normalized = normalized.replace(/^\/\*[\s\S]*?\*\//, "").trimStart();
|
|
159158
|
+
continue;
|
|
159159
|
+
}
|
|
159160
|
+
break;
|
|
159161
|
+
}
|
|
159162
|
+
return normalized;
|
|
159163
|
+
}
|
|
159164
|
+
function getSqlVerb(sql) {
|
|
159165
|
+
const normalized = stripLeadingSqlComments(sql);
|
|
159166
|
+
const match = normalized.match(/^([a-zA-Z]+)/);
|
|
159167
|
+
return match ? match[1].toUpperCase() : "";
|
|
159168
|
+
}
|
|
159169
|
+
function isReadOnlySql(sql) {
|
|
159170
|
+
const normalized = stripLeadingSqlComments(sql);
|
|
159171
|
+
const verb = getSqlVerb(normalized);
|
|
159172
|
+
const readOnlyVerbs = new Set([
|
|
159173
|
+
"SELECT",
|
|
159174
|
+
"SHOW",
|
|
159175
|
+
"EXPLAIN",
|
|
159176
|
+
"WITH",
|
|
159177
|
+
"VALUES",
|
|
159178
|
+
]);
|
|
159179
|
+
if (!readOnlyVerbs.has(verb)) {
|
|
159180
|
+
return false;
|
|
159181
|
+
}
|
|
159182
|
+
return !/\b(INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|TRUNCATE|REPLACE|RENAME|GRANT|REVOKE|COMMENT|VACUUM|ANALYZE|FOR\s+UPDATE|FOR\s+SHARE)\b/i.test(normalized);
|
|
159183
|
+
}
|
|
159184
|
+
function buildLimitedReadOnlySql(sql, limit) {
|
|
159185
|
+
const normalized = stripLeadingSqlComments(sql).replace(/;\s*$/, "");
|
|
159186
|
+
const verb = getSqlVerb(normalized);
|
|
159187
|
+
if (!["SELECT", "WITH", "VALUES"].includes(verb)) {
|
|
159188
|
+
return sql;
|
|
159189
|
+
}
|
|
159190
|
+
return `SELECT * FROM (${normalized}) AS cloudbase_mcp_readonly_limit LIMIT ${limit}`;
|
|
159191
|
+
}
|
|
159192
|
+
function isDestructiveSql(sql) {
|
|
159193
|
+
const normalized = stripLeadingSqlComments(sql);
|
|
159194
|
+
const verb = getSqlVerb(normalized);
|
|
159195
|
+
if (["DROP", "TRUNCATE", "DELETE"].includes(verb)) {
|
|
159196
|
+
return true;
|
|
159197
|
+
}
|
|
159198
|
+
if (verb === "ALTER") {
|
|
159199
|
+
return /\b(DROP|RENAME)\b/i.test(normalized);
|
|
159200
|
+
}
|
|
159201
|
+
return false;
|
|
159202
|
+
}
|
|
159203
|
+
function classifySqlRisk(sql) {
|
|
159204
|
+
const normalized = stripLeadingSqlComments(sql);
|
|
159205
|
+
const verb = getSqlVerb(normalized);
|
|
159206
|
+
if (!verb) {
|
|
159207
|
+
return {
|
|
159208
|
+
risk: "unknown_risk",
|
|
159209
|
+
readOnly: false,
|
|
159210
|
+
requiresConfirm: true,
|
|
159211
|
+
};
|
|
159212
|
+
}
|
|
159213
|
+
if (isReadOnlySql(normalized)) {
|
|
159214
|
+
return {
|
|
159215
|
+
risk: "read_only",
|
|
159216
|
+
readOnly: true,
|
|
159217
|
+
requiresConfirm: false,
|
|
159218
|
+
};
|
|
159219
|
+
}
|
|
159220
|
+
if (isDestructiveSql(normalized)) {
|
|
159221
|
+
return {
|
|
159222
|
+
risk: "destructive",
|
|
159223
|
+
readOnly: false,
|
|
159224
|
+
requiresConfirm: true,
|
|
159225
|
+
};
|
|
159226
|
+
}
|
|
159227
|
+
if (/\b(GRANT|REVOKE|CREATE\s+POLICY|ALTER\s+POLICY|DROP\s+POLICY|ENABLE\s+ROW\s+LEVEL\s+SECURITY|DISABLE\s+ROW\s+LEVEL\s+SECURITY)\b/i.test(normalized)) {
|
|
159228
|
+
return {
|
|
159229
|
+
risk: "security_change",
|
|
159230
|
+
readOnly: false,
|
|
159231
|
+
requiresConfirm: true,
|
|
159232
|
+
};
|
|
159233
|
+
}
|
|
159234
|
+
if (["CREATE", "ALTER", "COMMENT"].includes(verb)) {
|
|
159235
|
+
return {
|
|
159236
|
+
risk: "schema_change",
|
|
159237
|
+
readOnly: false,
|
|
159238
|
+
requiresConfirm: true,
|
|
159239
|
+
};
|
|
159240
|
+
}
|
|
159241
|
+
if (["INSERT", "UPDATE", "DELETE", "MERGE"].includes(verb)) {
|
|
159242
|
+
return {
|
|
159243
|
+
risk: "normal_write",
|
|
159244
|
+
readOnly: false,
|
|
159245
|
+
requiresConfirm: true,
|
|
159246
|
+
};
|
|
159247
|
+
}
|
|
159248
|
+
return {
|
|
159249
|
+
risk: "unknown_risk",
|
|
159250
|
+
readOnly: false,
|
|
159251
|
+
requiresConfirm: true,
|
|
159252
|
+
};
|
|
159253
|
+
}
|
|
159254
|
+
function quoteIdentifier(identifier) {
|
|
159255
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
159256
|
+
}
|
|
159257
|
+
function buildSchemaNextAction(reason, objectName) {
|
|
159258
|
+
return buildNextAction(QUERY_PG_DATABASE, "schema", reason, {
|
|
159259
|
+
action: "schema",
|
|
159260
|
+
objectName,
|
|
159261
|
+
});
|
|
159262
|
+
}
|
|
159263
|
+
function parseSchemaQualifiedName(objectName) {
|
|
159264
|
+
const match = objectName
|
|
159265
|
+
.trim()
|
|
159266
|
+
.match(/^([A-Za-z_][A-Za-z0-9_$]*)\.([A-Za-z_][A-Za-z0-9_$]*)$/);
|
|
159267
|
+
if (!match) {
|
|
159268
|
+
return null;
|
|
159269
|
+
}
|
|
159270
|
+
return {
|
|
159271
|
+
schema: match[1],
|
|
159272
|
+
name: match[2],
|
|
159273
|
+
};
|
|
159274
|
+
}
|
|
159275
|
+
function serializeValue(value) {
|
|
159276
|
+
if (value instanceof Date) {
|
|
159277
|
+
return value.toISOString();
|
|
159278
|
+
}
|
|
159279
|
+
if (typeof value === "bigint") {
|
|
159280
|
+
return value.toString();
|
|
159281
|
+
}
|
|
159282
|
+
if (Array.isArray(value)) {
|
|
159283
|
+
return value.map((item) => serializeValue(item));
|
|
159284
|
+
}
|
|
159285
|
+
if (value && typeof value === "object") {
|
|
159286
|
+
return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
|
|
159287
|
+
key,
|
|
159288
|
+
serializeValue(nestedValue),
|
|
159289
|
+
]));
|
|
159290
|
+
}
|
|
159291
|
+
return value;
|
|
159292
|
+
}
|
|
159293
|
+
function truncateText(value, maxLength = 180) {
|
|
159294
|
+
if (value === null || value === undefined) {
|
|
159295
|
+
return null;
|
|
159296
|
+
}
|
|
159297
|
+
const text = String(value);
|
|
159298
|
+
return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
|
|
159299
|
+
}
|
|
159300
|
+
function buildSchemaTable(schema, name) {
|
|
159301
|
+
return `${schema}.${name}`;
|
|
159302
|
+
}
|
|
159303
|
+
function parseTargetTableFromSql(sql, defaultSchema) {
|
|
159304
|
+
const normalized = stripLeadingSqlComments(sql);
|
|
159305
|
+
const identifier = String.raw `((?:[A-Za-z_][A-Za-z0-9_$]*\.)?[A-Za-z_][A-Za-z0-9_$]*)`;
|
|
159306
|
+
const patterns = [
|
|
159307
|
+
new RegExp(String.raw `\bcreate\s+(?:temporary\s+|temp\s+|unlogged\s+)?table\s+(?:if\s+not\s+exists\s+)?${identifier}`, "i"),
|
|
159308
|
+
new RegExp(String.raw `\b(?:alter|drop|truncate)\s+table\s+(?:if\s+exists\s+)?${identifier}`, "i"),
|
|
159309
|
+
new RegExp(String.raw `\binsert\s+into\s+${identifier}`, "i"),
|
|
159310
|
+
new RegExp(String.raw `\bupdate\s+${identifier}`, "i"),
|
|
159311
|
+
new RegExp(String.raw `\b(?:delete\s+from|from)\s+${identifier}`, "i"),
|
|
159312
|
+
];
|
|
159313
|
+
const match = patterns
|
|
159314
|
+
.map((pattern) => normalized.match(pattern))
|
|
159315
|
+
.find(Boolean);
|
|
159316
|
+
if (!match?.[1]) {
|
|
159317
|
+
return undefined;
|
|
159318
|
+
}
|
|
159319
|
+
if (match[1].includes(".")) {
|
|
159320
|
+
return match[1];
|
|
159321
|
+
}
|
|
159322
|
+
return `${defaultSchema}.${match[1]}`;
|
|
159323
|
+
}
|
|
159324
|
+
function summarizeQueryResult(result, limit) {
|
|
159325
|
+
const rows = result.rows.map((row) => serializeValue(row));
|
|
159326
|
+
const trimmedRows = rows.slice(0, limit);
|
|
159327
|
+
return {
|
|
159328
|
+
columns: result.fields?.map((field) => field.name) ??
|
|
159329
|
+
(trimmedRows[0] ? Object.keys(trimmedRows[0]) : []),
|
|
159330
|
+
rows: trimmedRows,
|
|
159331
|
+
returnedRows: rows.length,
|
|
159332
|
+
truncated: rows.length > trimmedRows.length,
|
|
159333
|
+
truncatedCount: Math.max(rows.length - trimmedRows.length, 0),
|
|
159334
|
+
};
|
|
159335
|
+
}
|
|
159336
|
+
async function withPgClient(context, deps, callback) {
|
|
159337
|
+
const client = await deps.createClient(context);
|
|
159338
|
+
await client.connect();
|
|
159339
|
+
try {
|
|
159340
|
+
return await callback(client);
|
|
159341
|
+
}
|
|
159342
|
+
finally {
|
|
159343
|
+
await client.end();
|
|
159344
|
+
}
|
|
159345
|
+
}
|
|
159346
|
+
async function getTableRowCount(client, schema, table) {
|
|
159347
|
+
const qualifiedName = `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
159348
|
+
const result = await client.query(`SELECT COUNT(*)::bigint AS row_count FROM ${qualifiedName}`);
|
|
159349
|
+
const rawValue = result.rows[0]?.row_count;
|
|
159350
|
+
const parsed = Number(rawValue);
|
|
159351
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
159352
|
+
}
|
|
159353
|
+
async function listObjects(client, schema, limit) {
|
|
159354
|
+
const result = await client.query(`
|
|
159355
|
+
SELECT
|
|
159356
|
+
n.nspname AS schema,
|
|
159357
|
+
c.relname AS name,
|
|
159358
|
+
CASE c.relkind
|
|
159359
|
+
WHEN 'r' THEN 'table'
|
|
159360
|
+
WHEN 'p' THEN 'partitioned_table'
|
|
159361
|
+
WHEN 'v' THEN 'view'
|
|
159362
|
+
WHEN 'm' THEN 'materialized_view'
|
|
159363
|
+
WHEN 'f' THEN 'foreign_table'
|
|
159364
|
+
ELSE c.relkind::text
|
|
159365
|
+
END AS kind,
|
|
159366
|
+
CASE
|
|
159367
|
+
WHEN c.reltuples >= 0 THEN c.reltuples::bigint
|
|
159368
|
+
ELSE NULL
|
|
159369
|
+
END AS estimated_rows
|
|
159370
|
+
FROM pg_class c
|
|
159371
|
+
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
159372
|
+
WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f')
|
|
159373
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
159374
|
+
AND n.nspname NOT LIKE 'pg_toast%'
|
|
159375
|
+
AND ($1::text IS NULL OR n.nspname = $1)
|
|
159376
|
+
ORDER BY n.nspname, c.relname
|
|
159377
|
+
LIMIT $2
|
|
159378
|
+
`, [schema ?? null, limit]);
|
|
159379
|
+
return result.rows.map((row) => ({
|
|
159380
|
+
schema: String(row.schema),
|
|
159381
|
+
name: String(row.name),
|
|
159382
|
+
schemaTable: buildSchemaTable(String(row.schema), String(row.name)),
|
|
159383
|
+
kind: String(row.kind),
|
|
159384
|
+
estimatedRows: row.estimated_rows === null || row.estimated_rows === undefined
|
|
159385
|
+
? null
|
|
159386
|
+
: Number(row.estimated_rows),
|
|
159387
|
+
}));
|
|
159388
|
+
}
|
|
159389
|
+
async function summarizeMetadata(client, schema, limit) {
|
|
159390
|
+
const result = await client.query(`
|
|
159391
|
+
SELECT
|
|
159392
|
+
n.nspname AS schema,
|
|
159393
|
+
c.relname AS name,
|
|
159394
|
+
CASE c.relkind
|
|
159395
|
+
WHEN 'r' THEN 'table'
|
|
159396
|
+
WHEN 'p' THEN 'partitioned_table'
|
|
159397
|
+
WHEN 'v' THEN 'view'
|
|
159398
|
+
WHEN 'm' THEN 'materialized_view'
|
|
159399
|
+
ELSE c.relkind::text
|
|
159400
|
+
END AS kind,
|
|
159401
|
+
CASE
|
|
159402
|
+
WHEN c.reltuples >= 0 THEN c.reltuples::bigint
|
|
159403
|
+
ELSE NULL
|
|
159404
|
+
END AS estimated_rows,
|
|
159405
|
+
c.relrowsecurity AS rls_enabled,
|
|
159406
|
+
COALESCE(col_counts.column_count, 0)::int AS column_count
|
|
159407
|
+
FROM pg_class c
|
|
159408
|
+
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
159409
|
+
LEFT JOIN LATERAL (
|
|
159410
|
+
SELECT COUNT(*) AS column_count
|
|
159411
|
+
FROM information_schema.columns cols
|
|
159412
|
+
WHERE cols.table_schema = n.nspname
|
|
159413
|
+
AND cols.table_name = c.relname
|
|
159414
|
+
) AS col_counts ON TRUE
|
|
159415
|
+
WHERE c.relkind IN ('r', 'p', 'v', 'm')
|
|
159416
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
159417
|
+
AND n.nspname NOT LIKE 'pg_toast%'
|
|
159418
|
+
AND ($1::text IS NULL OR n.nspname = $1)
|
|
159419
|
+
ORDER BY n.nspname, c.relname
|
|
159420
|
+
LIMIT $2
|
|
159421
|
+
`, [schema ?? null, limit]);
|
|
159422
|
+
const summaries = [];
|
|
159423
|
+
for (const row of result.rows) {
|
|
159424
|
+
const summary = {
|
|
159425
|
+
schema: String(row.schema),
|
|
159426
|
+
name: String(row.name),
|
|
159427
|
+
schemaTable: buildSchemaTable(String(row.schema), String(row.name)),
|
|
159428
|
+
kind: String(row.kind),
|
|
159429
|
+
estimatedRows: row.estimated_rows === null || row.estimated_rows === undefined
|
|
159430
|
+
? null
|
|
159431
|
+
: Number(row.estimated_rows),
|
|
159432
|
+
columnCount: Number(row.column_count ?? 0),
|
|
159433
|
+
rlsEnabled: Boolean(row.rls_enabled),
|
|
159434
|
+
};
|
|
159435
|
+
if (summary.kind === "table" || summary.kind === "partitioned_table") {
|
|
159436
|
+
summary.rowCount = summary.estimatedRows ?? null;
|
|
159437
|
+
summary.rowCountSource = "estimated";
|
|
159438
|
+
}
|
|
159439
|
+
else {
|
|
159440
|
+
summary.rowCount = null;
|
|
159441
|
+
summary.rowCountSource = "not_applicable";
|
|
159442
|
+
}
|
|
159443
|
+
summaries.push(summary);
|
|
159444
|
+
}
|
|
159445
|
+
return summaries;
|
|
159446
|
+
}
|
|
159447
|
+
async function readSchemaInfo(client, schema, table) {
|
|
159448
|
+
const objectCheck = await client.query(`
|
|
159449
|
+
SELECT
|
|
159450
|
+
CASE c.relkind
|
|
159451
|
+
WHEN 'r' THEN 'table'
|
|
159452
|
+
WHEN 'p' THEN 'partitioned_table'
|
|
159453
|
+
WHEN 'v' THEN 'view'
|
|
159454
|
+
WHEN 'm' THEN 'materialized_view'
|
|
159455
|
+
WHEN 'f' THEN 'foreign_table'
|
|
159456
|
+
ELSE c.relkind::text
|
|
159457
|
+
END AS kind,
|
|
159458
|
+
c.relrowsecurity AS row_security_enabled,
|
|
159459
|
+
c.relforcerowsecurity AS force_row_security
|
|
159460
|
+
FROM pg_class c
|
|
159461
|
+
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
159462
|
+
WHERE n.nspname = $1 AND c.relname = $2
|
|
159463
|
+
LIMIT 1
|
|
159464
|
+
`, [schema, table]);
|
|
159465
|
+
if (!objectCheck.rows[0]) {
|
|
159466
|
+
return null;
|
|
159467
|
+
}
|
|
159468
|
+
const columnsResult = await client.query(`
|
|
159469
|
+
SELECT
|
|
159470
|
+
column_name,
|
|
159471
|
+
data_type,
|
|
159472
|
+
udt_name,
|
|
159473
|
+
is_nullable,
|
|
159474
|
+
column_default,
|
|
159475
|
+
character_maximum_length,
|
|
159476
|
+
numeric_precision,
|
|
159477
|
+
numeric_scale
|
|
159478
|
+
FROM information_schema.columns
|
|
159479
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
159480
|
+
ORDER BY ordinal_position
|
|
159481
|
+
`, [schema, table]);
|
|
159482
|
+
const primaryKeyResult = await client.query(`
|
|
159483
|
+
SELECT kcu.column_name
|
|
159484
|
+
FROM information_schema.table_constraints tc
|
|
159485
|
+
INNER JOIN information_schema.key_column_usage kcu
|
|
159486
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
159487
|
+
AND tc.table_schema = kcu.table_schema
|
|
159488
|
+
AND tc.table_name = kcu.table_name
|
|
159489
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
159490
|
+
AND tc.table_schema = $1
|
|
159491
|
+
AND tc.table_name = $2
|
|
159492
|
+
ORDER BY kcu.ordinal_position
|
|
159493
|
+
`, [schema, table]);
|
|
159494
|
+
const foreignKeyResult = await client.query(`
|
|
159495
|
+
SELECT
|
|
159496
|
+
tc.constraint_name,
|
|
159497
|
+
kcu.column_name,
|
|
159498
|
+
ccu.table_schema AS foreign_table_schema,
|
|
159499
|
+
ccu.table_name AS foreign_table_name,
|
|
159500
|
+
ccu.column_name AS foreign_column_name
|
|
159501
|
+
FROM information_schema.table_constraints tc
|
|
159502
|
+
INNER JOIN information_schema.key_column_usage kcu
|
|
159503
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
159504
|
+
AND tc.table_schema = kcu.table_schema
|
|
159505
|
+
AND tc.table_name = kcu.table_name
|
|
159506
|
+
INNER JOIN information_schema.constraint_column_usage ccu
|
|
159507
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
159508
|
+
AND tc.table_schema = ccu.table_schema
|
|
159509
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
159510
|
+
AND tc.table_schema = $1
|
|
159511
|
+
AND tc.table_name = $2
|
|
159512
|
+
ORDER BY tc.constraint_name, kcu.ordinal_position
|
|
159513
|
+
`, [schema, table]);
|
|
159514
|
+
const indexesResult = await client.query(`
|
|
159515
|
+
SELECT indexname, indexdef
|
|
159516
|
+
FROM pg_indexes
|
|
159517
|
+
WHERE schemaname = $1 AND tablename = $2
|
|
159518
|
+
ORDER BY indexname
|
|
159519
|
+
`, [schema, table]);
|
|
159520
|
+
const policiesResult = await client.query(`
|
|
159521
|
+
SELECT policyname, permissive, roles, cmd, qual, with_check
|
|
159522
|
+
FROM pg_policies
|
|
159523
|
+
WHERE schemaname = $1 AND tablename = $2
|
|
159524
|
+
ORDER BY policyname
|
|
159525
|
+
`, [schema, table]);
|
|
159526
|
+
let rowCount = null;
|
|
159527
|
+
try {
|
|
159528
|
+
rowCount = await getTableRowCount(client, schema, table);
|
|
159529
|
+
}
|
|
159530
|
+
catch {
|
|
159531
|
+
rowCount = null;
|
|
159532
|
+
}
|
|
159533
|
+
const columns = columnsResult.rows.map((row) => ({
|
|
159534
|
+
name: String(row.column_name),
|
|
159535
|
+
dataType: String(row.data_type),
|
|
159536
|
+
isNullable: String(row.is_nullable).toUpperCase() === "YES",
|
|
159537
|
+
defaultValue: row.column_default === undefined
|
|
159538
|
+
? null
|
|
159539
|
+
: truncateText(row.column_default, 120),
|
|
159540
|
+
}));
|
|
159541
|
+
return {
|
|
159542
|
+
schemaTable: buildSchemaTable(schema, table),
|
|
159543
|
+
kind: String(objectCheck.rows[0].kind),
|
|
159544
|
+
rowCount,
|
|
159545
|
+
columns,
|
|
159546
|
+
primaryKey: primaryKeyResult.rows.map((row) => String(row.column_name)),
|
|
159547
|
+
foreignKeys: foreignKeyResult.rows.map((row) => ({
|
|
159548
|
+
constraintName: String(row.constraint_name),
|
|
159549
|
+
columnName: String(row.column_name),
|
|
159550
|
+
references: buildSchemaTable(String(row.foreign_table_schema), String(row.foreign_table_name)),
|
|
159551
|
+
referencedColumn: String(row.foreign_column_name),
|
|
159552
|
+
})),
|
|
159553
|
+
indexes: indexesResult.rows.map((row) => ({
|
|
159554
|
+
name: String(row.indexname),
|
|
159555
|
+
definition: truncateText(row.indexdef, 180),
|
|
159556
|
+
})),
|
|
159557
|
+
security: {
|
|
159558
|
+
rowLevelSecurityEnabled: Boolean(objectCheck.rows[0].row_security_enabled),
|
|
159559
|
+
forceRowLevelSecurity: Boolean(objectCheck.rows[0].force_row_security),
|
|
159560
|
+
policies: policiesResult.rows.map((row) => ({
|
|
159561
|
+
name: String(row.policyname),
|
|
159562
|
+
permissive: String(row.permissive),
|
|
159563
|
+
roles: Array.isArray(row.roles)
|
|
159564
|
+
? row.roles.map((role) => String(role))
|
|
159565
|
+
: [],
|
|
159566
|
+
command: String(row.cmd),
|
|
159567
|
+
using: truncateText(row.qual, 180),
|
|
159568
|
+
withCheck: truncateText(row.with_check, 180),
|
|
159569
|
+
})),
|
|
159570
|
+
},
|
|
159571
|
+
};
|
|
159572
|
+
}
|
|
159573
|
+
function createDefaultDependencies(server) {
|
|
159574
|
+
return {
|
|
159575
|
+
createClient: async (context) => createManagerPgClient(context, server?.cloudBaseOptions),
|
|
159576
|
+
};
|
|
159577
|
+
}
|
|
159578
|
+
function isExecutePGSqlDatabase(value) {
|
|
159579
|
+
return Boolean(value &&
|
|
159580
|
+
typeof value === "object" &&
|
|
159581
|
+
"executePGSql" in value &&
|
|
159582
|
+
typeof value.executePGSql === "function");
|
|
159583
|
+
}
|
|
159584
|
+
function isCloudBaseWithCommonService(value) {
|
|
159585
|
+
return Boolean(value &&
|
|
159586
|
+
typeof value === "object" &&
|
|
159587
|
+
"commonService" in value &&
|
|
159588
|
+
typeof value.commonService === "function");
|
|
159589
|
+
}
|
|
159590
|
+
async function executeManagerPGSql(context, sql, cloudBaseOptions) {
|
|
159591
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
|
|
159592
|
+
cloudBaseOptions: cloudBaseOptions
|
|
159593
|
+
? {
|
|
159594
|
+
...cloudBaseOptions,
|
|
159595
|
+
envId: context.envId,
|
|
159596
|
+
}
|
|
159597
|
+
: {
|
|
159598
|
+
envId: context.envId,
|
|
159599
|
+
},
|
|
159600
|
+
});
|
|
159601
|
+
const options = {
|
|
159602
|
+
Sql: sql,
|
|
159603
|
+
Role: context.role,
|
|
159604
|
+
EnvId: context.envId,
|
|
159605
|
+
};
|
|
159606
|
+
if (isExecutePGSqlDatabase(manager.database)) {
|
|
159607
|
+
return manager.database.executePGSql(options);
|
|
159608
|
+
}
|
|
159609
|
+
if (isCloudBaseWithCommonService(manager)) {
|
|
159610
|
+
const result = await manager.commonService("tcb", "2018-06-08").call({
|
|
159611
|
+
Action: "ExecutePGSql",
|
|
159612
|
+
Param: options,
|
|
159613
|
+
});
|
|
159614
|
+
return "Response" in result && result.Response ? result.Response : result;
|
|
159615
|
+
}
|
|
159616
|
+
throw new Error("Current @cloudbase/manager-node runtime does not expose database.executePGSql or commonService fallback. Upgrade to @cloudbase/manager-node >= 5.4.0.");
|
|
159617
|
+
}
|
|
159618
|
+
/** Call a CloudBase PG migration API via commonService fallback */
|
|
159619
|
+
async function callPgMigrationApi(context, action, params, cloudBaseOptions) {
|
|
159620
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
|
|
159621
|
+
cloudBaseOptions: cloudBaseOptions
|
|
159622
|
+
? { ...cloudBaseOptions, envId: context.envId }
|
|
159623
|
+
: { envId: context.envId },
|
|
159624
|
+
});
|
|
159625
|
+
if (!isCloudBaseWithCommonService(manager)) {
|
|
159626
|
+
throw new Error("Current @cloudbase/manager-node runtime does not support migration APIs. Upgrade to @cloudbase/manager-node >= 5.4.0.");
|
|
159627
|
+
}
|
|
159628
|
+
const result = await manager.commonService("tcb", "2018-06-08").call({
|
|
159629
|
+
Action: action,
|
|
159630
|
+
Param: { EnvId: context.envId, ...params },
|
|
159631
|
+
});
|
|
159632
|
+
return "Response" in result && result.Response
|
|
159633
|
+
? result.Response
|
|
159634
|
+
: result;
|
|
159635
|
+
}
|
|
159636
|
+
function parseManagerRows(result) {
|
|
159637
|
+
const columns = result.Columns ?? [];
|
|
159638
|
+
return (result.Rows ?? []).map((rowText) => {
|
|
159639
|
+
let values;
|
|
159640
|
+
try {
|
|
159641
|
+
values = JSON.parse(rowText);
|
|
159642
|
+
}
|
|
159643
|
+
catch {
|
|
159644
|
+
values = [];
|
|
159645
|
+
}
|
|
159646
|
+
const arrayValues = Array.isArray(values) ? values : [];
|
|
159647
|
+
return Object.fromEntries(columns.map((column, index) => [column, arrayValues[index] ?? null]));
|
|
159648
|
+
});
|
|
159649
|
+
}
|
|
159650
|
+
function renderPgLiteral(value) {
|
|
159651
|
+
if (value === null || value === undefined) {
|
|
159652
|
+
return "NULL";
|
|
159653
|
+
}
|
|
159654
|
+
if (typeof value === "number") {
|
|
159655
|
+
return Number.isFinite(value) ? String(value) : "NULL";
|
|
159656
|
+
}
|
|
159657
|
+
if (typeof value === "boolean") {
|
|
159658
|
+
return value ? "TRUE" : "FALSE";
|
|
159659
|
+
}
|
|
159660
|
+
if (value instanceof Date) {
|
|
159661
|
+
return `'${value.toISOString().replace(/'/g, "''")}'`;
|
|
159662
|
+
}
|
|
159663
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
159664
|
+
}
|
|
159665
|
+
function renderParameterizedSql(sql, values) {
|
|
159666
|
+
if (!values || values.length === 0) {
|
|
159667
|
+
return sql;
|
|
159668
|
+
}
|
|
159669
|
+
return sql.replace(/\$(\d+)/g, (placeholder, indexText) => {
|
|
159670
|
+
const index = Number(indexText);
|
|
159671
|
+
if (!Number.isInteger(index) || index < 1 || index > values.length) {
|
|
159672
|
+
return placeholder;
|
|
159673
|
+
}
|
|
159674
|
+
return renderPgLiteral(values[index - 1]);
|
|
159675
|
+
});
|
|
159676
|
+
}
|
|
159677
|
+
function inferCommand(sql) {
|
|
159678
|
+
return getSqlVerb(sql) || undefined;
|
|
159679
|
+
}
|
|
159680
|
+
function createManagerPgClient(context, cloudBaseOptions) {
|
|
159681
|
+
return {
|
|
159682
|
+
async connect() {
|
|
159683
|
+
return undefined;
|
|
159684
|
+
},
|
|
159685
|
+
async query(sql, values) {
|
|
159686
|
+
const renderedSql = renderParameterizedSql(sql, values);
|
|
159687
|
+
const result = await executeManagerPGSql(context, renderedSql, cloudBaseOptions);
|
|
159688
|
+
const rows = parseManagerRows(result);
|
|
159689
|
+
return {
|
|
159690
|
+
rows,
|
|
159691
|
+
rowCount: result.AffectedRows ?? rows.length,
|
|
159692
|
+
command: inferCommand(renderedSql),
|
|
159693
|
+
fields: result.Columns?.map((name) => ({ name })) ?? [],
|
|
159694
|
+
};
|
|
159695
|
+
},
|
|
159696
|
+
async end() {
|
|
159697
|
+
return undefined;
|
|
159698
|
+
},
|
|
159699
|
+
};
|
|
159700
|
+
}
|
|
159701
|
+
function sleep(ms) {
|
|
159702
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
159703
|
+
}
|
|
159704
|
+
async function ensurePgReady(context, deps, options) {
|
|
159705
|
+
const maxAttempts = options?.maxAttempts ?? 20;
|
|
159706
|
+
const retryDelayMs = options?.retryDelayMs ?? 1000;
|
|
159707
|
+
let lastError;
|
|
159708
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
159709
|
+
try {
|
|
159710
|
+
await withPgClient(context, deps, async (client) => {
|
|
159711
|
+
await client.query("SELECT 1");
|
|
159712
|
+
});
|
|
159713
|
+
return;
|
|
159714
|
+
}
|
|
159715
|
+
catch (error) {
|
|
159716
|
+
lastError = error;
|
|
159717
|
+
if (attempt === maxAttempts) {
|
|
159718
|
+
break;
|
|
159719
|
+
}
|
|
159720
|
+
await sleep(retryDelayMs);
|
|
159721
|
+
}
|
|
159722
|
+
}
|
|
159723
|
+
const reason = lastError instanceof Error ? lastError.message : String(lastError);
|
|
159724
|
+
throw new Error(`PostgreSQL is not ready after ${maxAttempts} attempts. Last error: ${reason}`);
|
|
159725
|
+
}
|
|
159726
|
+
async function handleInit(args, server, deps) {
|
|
159727
|
+
const existing = await getPgContext(server);
|
|
159728
|
+
const resolvedEnvId = args.envId ??
|
|
159729
|
+
server.cloudBaseOptions?.envId ??
|
|
159730
|
+
process.env.CLOUDBASE_ENV_ID ??
|
|
159731
|
+
existing?.envId;
|
|
159732
|
+
if (!resolvedEnvId) {
|
|
159733
|
+
return buildPgToolResult({
|
|
159734
|
+
success: false,
|
|
159735
|
+
errorCode: "ENV_ID_REQUIRED",
|
|
159736
|
+
message: "CloudBase PostgreSQL only supports the built-in cloud runtime. Provide envId or bind a CloudBase environment before calling managePgDatabase(action=init).",
|
|
159737
|
+
});
|
|
159738
|
+
}
|
|
159739
|
+
const bootstrapResult = {
|
|
159740
|
+
instanceId: args.instanceId ?? "cloudbase-pg",
|
|
159741
|
+
defaultSchema: args.defaultSchema ?? "public",
|
|
159742
|
+
status: "ready",
|
|
159743
|
+
runtimeMode: "cloudbase-manager",
|
|
159744
|
+
role: args.role ?? "cloudbase_admin",
|
|
159745
|
+
};
|
|
159746
|
+
const now = new Date().toISOString();
|
|
159747
|
+
const context = {
|
|
159748
|
+
envId: resolvedEnvId,
|
|
159749
|
+
instanceId: bootstrapResult.instanceId,
|
|
159750
|
+
defaultSchema: bootstrapResult.defaultSchema,
|
|
159751
|
+
runtimeMode: "cloudbase-manager",
|
|
159752
|
+
bootstrapMode: "cloud",
|
|
159753
|
+
role: bootstrapResult.role,
|
|
159754
|
+
createdAt: existing?.createdAt ?? now,
|
|
159755
|
+
updatedAt: now,
|
|
159756
|
+
};
|
|
159757
|
+
await ensurePgReady(context, deps, deps.readyCheckOptions);
|
|
159758
|
+
await savePgContext(server, context);
|
|
159759
|
+
return buildPgToolResult({
|
|
159760
|
+
success: true,
|
|
159761
|
+
data: {
|
|
159762
|
+
context: {
|
|
159763
|
+
...sanitizePgContext(context),
|
|
159764
|
+
},
|
|
159765
|
+
status: bootstrapResult.status,
|
|
159766
|
+
},
|
|
159767
|
+
message: "CloudBase PG 云端执行上下文已就绪。先查询对象或 schema,再执行写入 SQL。",
|
|
159768
|
+
nextActions: [
|
|
159769
|
+
buildNextAction(QUERY_PG_DATABASE, "objects", "List schema-qualified database objects before drilling into a specific table.", { action: "objects", limit: 20 }),
|
|
159770
|
+
buildSchemaNextAction("Inspect a concrete schema-qualified table after you know which object matters.", `${context.defaultSchema}.your_table`),
|
|
159771
|
+
],
|
|
159772
|
+
});
|
|
159773
|
+
}
|
|
159774
|
+
async function handleQueryContext(server) {
|
|
159775
|
+
const context = await getPgContext(server);
|
|
159776
|
+
if (!context) {
|
|
159777
|
+
return buildMissingContextResult();
|
|
159778
|
+
}
|
|
159779
|
+
return buildPgToolResult({
|
|
159780
|
+
success: true,
|
|
159781
|
+
data: {
|
|
159782
|
+
context: {
|
|
159783
|
+
...sanitizePgContext(context),
|
|
159784
|
+
},
|
|
159785
|
+
},
|
|
159786
|
+
message: "Resolved current CloudBase PostgreSQL context.",
|
|
159787
|
+
nextActions: [
|
|
159788
|
+
buildNextAction(QUERY_PG_DATABASE, "objects", "List schema-qualified objects before inspecting an individual table.", { action: "objects", limit: 20 }),
|
|
159789
|
+
],
|
|
159790
|
+
});
|
|
159791
|
+
}
|
|
159792
|
+
async function handleListObjects(args, context, deps) {
|
|
159793
|
+
const limit = normalizeLimit(args.limit);
|
|
159794
|
+
const objects = await withPgClient(context, deps, (client) => listObjects(client, args.schema, limit));
|
|
159795
|
+
return buildPgToolResult({
|
|
159796
|
+
success: true,
|
|
159797
|
+
data: {
|
|
159798
|
+
objects,
|
|
159799
|
+
schemaFilter: args.schema ?? null,
|
|
159800
|
+
limit,
|
|
159801
|
+
},
|
|
159802
|
+
message: objects.length > 0
|
|
159803
|
+
? `Listed ${objects.length} schema-qualified PostgreSQL objects. Inspect one object schema before writing SQL.`
|
|
159804
|
+
: "No PostgreSQL objects matched the current filter.",
|
|
159805
|
+
nextActions: objects.length > 0
|
|
159806
|
+
? [
|
|
159807
|
+
buildSchemaNextAction("Inspect the most relevant object schema before querying data.", objects[0].schemaTable),
|
|
159808
|
+
]
|
|
159809
|
+
: [
|
|
159810
|
+
buildNextAction(QUERY_PG_DATABASE, "context", "Re-check the current PG context if no objects were expectedly returned.", { action: "context" }),
|
|
159811
|
+
],
|
|
159812
|
+
});
|
|
159813
|
+
}
|
|
159814
|
+
async function handleMetadata(args, context, deps) {
|
|
159815
|
+
const limit = normalizeLimit(args.limit);
|
|
159816
|
+
const tables = await withPgClient(context, deps, (client) => summarizeMetadata(client, args.schema, limit));
|
|
159817
|
+
return buildPgToolResult({
|
|
159818
|
+
success: true,
|
|
159819
|
+
data: {
|
|
159820
|
+
tables,
|
|
159821
|
+
schemaFilter: args.schema ?? null,
|
|
159822
|
+
limit,
|
|
159823
|
+
},
|
|
159824
|
+
message: tables.length > 0
|
|
159825
|
+
? `Summarized ${tables.length} PostgreSQL objects with row-count and RLS hints. Use schema inspection before composing joins or mutations.`
|
|
159826
|
+
: "No PostgreSQL objects matched the current metadata filter.",
|
|
159827
|
+
nextActions: tables.length > 0
|
|
159828
|
+
? [
|
|
159829
|
+
buildSchemaNextAction("Inspect the relevant table schema to confirm columns, keys, and policies.", tables[0].schemaTable),
|
|
159830
|
+
]
|
|
159831
|
+
: [
|
|
159832
|
+
buildNextAction(QUERY_PG_DATABASE, "objects", "List objects first if metadata is empty or too restrictive.", { action: "objects", schema: args.schema, limit }),
|
|
159833
|
+
],
|
|
159834
|
+
});
|
|
159835
|
+
}
|
|
159836
|
+
async function handleReadOnlySql(args, context, deps) {
|
|
159837
|
+
if (!args.sql?.trim()) {
|
|
159838
|
+
return buildPgToolResult({
|
|
159839
|
+
success: false,
|
|
159840
|
+
errorCode: "SQL_REQUIRED",
|
|
159841
|
+
message: "Provide a read-only SQL statement when action=sql.",
|
|
159842
|
+
});
|
|
159843
|
+
}
|
|
159844
|
+
if (!isReadOnlySql(args.sql)) {
|
|
159845
|
+
return buildPgToolResult({
|
|
159846
|
+
success: false,
|
|
159847
|
+
errorCode: "READ_ONLY_SQL_REQUIRED",
|
|
159848
|
+
message: "queryPgDatabase(action=sql) only accepts read-only SQL. For DDL/DML (CREATE/ALTER/INSERT/UPDATE/DELETE/...), call managePgDatabase(action=execute) with confirm=true and the same SQL.",
|
|
159849
|
+
nextActions: [
|
|
159850
|
+
buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue this statement via managePgDatabase(action=execute) with confirm=true.", { action: "execute", sql: args.sql, confirm: true }),
|
|
159851
|
+
buildSchemaNextAction("Inspect schema first if you are deciding which write operation is needed.", `${context.defaultSchema}.your_table`),
|
|
159852
|
+
],
|
|
159853
|
+
});
|
|
159854
|
+
}
|
|
159855
|
+
const limit = normalizeLimit(args.limit);
|
|
159856
|
+
const limitedSql = buildLimitedReadOnlySql(args.sql, limit);
|
|
159857
|
+
const result = await withPgClient(context, deps, (client) => client.query(limitedSql));
|
|
159858
|
+
const summary = summarizeQueryResult(result, limit);
|
|
159859
|
+
return buildPgToolResult({
|
|
159860
|
+
success: true,
|
|
159861
|
+
data: {
|
|
159862
|
+
...summary,
|
|
159863
|
+
command: result.command ?? "SELECT",
|
|
159864
|
+
rowCount: result.rowCount ?? summary.returnedRows,
|
|
159865
|
+
},
|
|
159866
|
+
message: summary.truncated
|
|
159867
|
+
? `Read-only SQL executed successfully. Showing ${summary.rows.length} of ${summary.returnedRows} rows to control token usage.`
|
|
159868
|
+
: "Read-only SQL executed successfully.",
|
|
159869
|
+
nextActions: [
|
|
159870
|
+
buildSchemaNextAction("Inspect the table schema if you need to refine joins, filters, or mutations.", parseTargetTableFromSql(args.sql, context.defaultSchema) ??
|
|
159871
|
+
`${context.defaultSchema}.your_table`),
|
|
159872
|
+
],
|
|
159873
|
+
});
|
|
159874
|
+
}
|
|
159875
|
+
async function handleExecuteSql(args, context, deps) {
|
|
159876
|
+
if (!args.sql?.trim()) {
|
|
159877
|
+
return buildPgToolResult({
|
|
159878
|
+
success: false,
|
|
159879
|
+
errorCode: "SQL_REQUIRED",
|
|
159880
|
+
message: "Provide a SQL statement when action=execute.",
|
|
159881
|
+
});
|
|
159882
|
+
}
|
|
159883
|
+
const classification = classifySqlRisk(args.sql);
|
|
159884
|
+
if (!classification.readOnly && !args.confirm) {
|
|
159885
|
+
return buildPgToolResult({
|
|
159886
|
+
success: false,
|
|
159887
|
+
errorCode: "CONFIRM_REQUIRED",
|
|
159888
|
+
message: `This SQL is classified as ${classification.risk}. Re-run managePgDatabase(action=execute) with the same sql and confirm=true to proceed.`,
|
|
159889
|
+
data: {
|
|
159890
|
+
classification,
|
|
159891
|
+
},
|
|
159892
|
+
nextActions: [
|
|
159893
|
+
buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue with confirm=true to actually run the SQL.", { action: "execute", sql: args.sql, confirm: true }),
|
|
159894
|
+
buildNextAction(QUERY_PG_DATABASE, "metadata", "Inspect table size and shape before destructive changes.", { action: "metadata", limit: 20 }),
|
|
159895
|
+
],
|
|
159896
|
+
});
|
|
159897
|
+
}
|
|
159898
|
+
const result = await withPgClient(context, deps, (client) => client.query(args.sql));
|
|
159899
|
+
const targetTable = parseTargetTableFromSql(args.sql, context.defaultSchema);
|
|
159900
|
+
return buildPgToolResult({
|
|
159901
|
+
success: true,
|
|
159902
|
+
data: {
|
|
159903
|
+
classification,
|
|
159904
|
+
command: result.command ?? getSqlVerb(args.sql),
|
|
159905
|
+
rowCount: result.rowCount ?? null,
|
|
159906
|
+
previewRows: result.rows
|
|
159907
|
+
.slice(0, 5)
|
|
159908
|
+
.map((row) => serializeValue(row)),
|
|
159909
|
+
targetTable: targetTable ?? null,
|
|
159910
|
+
},
|
|
159911
|
+
message: "Write SQL executed successfully.",
|
|
159912
|
+
nextActions: classification.risk === "schema_change" && targetTable
|
|
159913
|
+
? [
|
|
159914
|
+
buildNextAction(QUERY_PG_DATABASE, "schema", "Inspect the table schema, columns, indexes, and RLS policies after schema changes.", {
|
|
159915
|
+
action: "schema",
|
|
159916
|
+
objectName: targetTable,
|
|
159917
|
+
}),
|
|
159918
|
+
]
|
|
159919
|
+
: [
|
|
159920
|
+
buildNextAction(QUERY_PG_DATABASE, "sql", "Verify the mutation with a focused read-only SQL query.", {
|
|
159921
|
+
action: "sql",
|
|
159922
|
+
sql: targetTable
|
|
159923
|
+
? `SELECT * FROM ${targetTable} LIMIT 20`
|
|
159924
|
+
: "SELECT 1",
|
|
159925
|
+
limit: 20,
|
|
159926
|
+
}),
|
|
159927
|
+
],
|
|
159928
|
+
});
|
|
159929
|
+
}
|
|
159930
|
+
async function handleDryRun(args) {
|
|
159931
|
+
if (!args.sql?.trim()) {
|
|
159932
|
+
return buildPgToolResult({
|
|
159933
|
+
success: false,
|
|
159934
|
+
errorCode: "SQL_REQUIRED",
|
|
159935
|
+
message: "Provide a SQL statement when action=dryRun.",
|
|
159936
|
+
});
|
|
159937
|
+
}
|
|
159938
|
+
const classification = classifySqlRisk(args.sql);
|
|
159939
|
+
return buildPgToolResult({
|
|
159940
|
+
success: true,
|
|
159941
|
+
data: {
|
|
159942
|
+
classification,
|
|
159943
|
+
wouldExecute: false,
|
|
159944
|
+
sqlPreview: args.sql.trim().slice(0, 500),
|
|
159945
|
+
},
|
|
159946
|
+
message: classification.readOnly
|
|
159947
|
+
? "SQL dry run completed. This statement is read-only; use queryPgDatabase(action=sql) to execute it."
|
|
159948
|
+
: "SQL dry run completed. Write SQL requires managePgDatabase(action=execute, confirm=true).",
|
|
159949
|
+
nextActions: [
|
|
159950
|
+
buildNextAction(classification.readOnly ? QUERY_PG_DATABASE : MANAGE_PG_DATABASE, classification.readOnly ? "sql" : "execute", classification.readOnly
|
|
159951
|
+
? "Execute the read-only SQL through queryPgDatabase."
|
|
159952
|
+
: "Execute the write SQL only after explicit confirmation.", classification.readOnly
|
|
159953
|
+
? { action: "sql", sql: args.sql, limit: 20 }
|
|
159954
|
+
: { action: "execute", sql: args.sql, confirm: true }),
|
|
159955
|
+
],
|
|
159956
|
+
});
|
|
159957
|
+
}
|
|
159958
|
+
async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
|
|
159959
|
+
if (!args.sql?.trim()) {
|
|
159960
|
+
return buildPgToolResult({
|
|
159961
|
+
success: false,
|
|
159962
|
+
errorCode: "SQL_REQUIRED",
|
|
159963
|
+
message: "Provide migration SQL when action=planMigration.",
|
|
159964
|
+
});
|
|
159965
|
+
}
|
|
159966
|
+
try {
|
|
159967
|
+
const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
|
|
159968
|
+
Sql: args.sql,
|
|
159969
|
+
}, cloudBaseOptions);
|
|
159970
|
+
return buildPgToolResult({
|
|
159971
|
+
success: true,
|
|
159972
|
+
data: result,
|
|
159973
|
+
message: "Migration plan generated via PreviewPGUserMigrations.",
|
|
159974
|
+
nextActions: [
|
|
159975
|
+
buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration to execute.", { action: "applyMigration", sql: args.sql, confirm: true }),
|
|
159976
|
+
],
|
|
159977
|
+
});
|
|
159978
|
+
}
|
|
159979
|
+
catch (error) {
|
|
159980
|
+
return buildPgToolResult({
|
|
159981
|
+
success: false,
|
|
159982
|
+
errorCode: "MIGRATION_API_ERROR",
|
|
159983
|
+
message: `PreviewPGUserMigrations failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
159984
|
+
});
|
|
159985
|
+
}
|
|
159986
|
+
}
|
|
159987
|
+
async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
159988
|
+
if (!args.sql?.trim()) {
|
|
159989
|
+
return buildPgToolResult({
|
|
159990
|
+
success: false,
|
|
159991
|
+
errorCode: "SQL_REQUIRED",
|
|
159992
|
+
message: "Provide migration SQL when action=applyMigration.",
|
|
159993
|
+
});
|
|
159994
|
+
}
|
|
159995
|
+
if (args.confirm !== true) {
|
|
159996
|
+
return buildPgToolResult({
|
|
159997
|
+
success: false,
|
|
159998
|
+
errorCode: "CONFIRM_REQUIRED",
|
|
159999
|
+
message: "PushPGUserMigrations requires confirm=true. Run with confirm=true to proceed.",
|
|
160000
|
+
});
|
|
160001
|
+
}
|
|
160002
|
+
try {
|
|
160003
|
+
const result = await callPgMigrationApi(context, "PushPGUserMigrations", {
|
|
160004
|
+
Sql: args.sql,
|
|
160005
|
+
}, cloudBaseOptions);
|
|
160006
|
+
return buildPgToolResult({
|
|
160007
|
+
success: true,
|
|
160008
|
+
data: result,
|
|
160009
|
+
message: "Migrations applied via PushPGUserMigrations.",
|
|
160010
|
+
});
|
|
160011
|
+
}
|
|
160012
|
+
catch (error) {
|
|
160013
|
+
return buildPgToolResult({
|
|
160014
|
+
success: false,
|
|
160015
|
+
errorCode: "MIGRATION_API_ERROR",
|
|
160016
|
+
message: `PushPGUserMigrations failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
160017
|
+
});
|
|
160018
|
+
}
|
|
160019
|
+
}
|
|
160020
|
+
async function handleListMigrations(args, context, deps, cloudBaseOptions) {
|
|
160021
|
+
try {
|
|
160022
|
+
const result = await callPgMigrationApi(context, "ListPGUserMigrations", {}, cloudBaseOptions);
|
|
160023
|
+
return buildPgToolResult({
|
|
160024
|
+
success: true,
|
|
160025
|
+
data: result,
|
|
160026
|
+
message: "Migration list retrieved via ListPGUserMigrations.",
|
|
160027
|
+
});
|
|
160028
|
+
}
|
|
160029
|
+
catch (error) {
|
|
160030
|
+
return buildPgToolResult({
|
|
160031
|
+
success: false,
|
|
160032
|
+
errorCode: "MIGRATION_API_ERROR",
|
|
160033
|
+
message: `ListPGUserMigrations failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
160034
|
+
});
|
|
160035
|
+
}
|
|
160036
|
+
}
|
|
160037
|
+
async function handleMigrationDetail(args, context, deps, cloudBaseOptions) {
|
|
160038
|
+
if (!args.objectName?.trim()) {
|
|
160039
|
+
return buildPgToolResult({
|
|
160040
|
+
success: false,
|
|
160041
|
+
errorCode: "OBJECT_NAME_REQUIRED",
|
|
160042
|
+
message: "Provide objectName (migration ID) when action=migrationDetail.",
|
|
160043
|
+
});
|
|
160044
|
+
}
|
|
160045
|
+
try {
|
|
160046
|
+
const result = await callPgMigrationApi(context, "DescribePGUserMigration", {
|
|
160047
|
+
ObjectId: args.objectName,
|
|
160048
|
+
}, cloudBaseOptions);
|
|
160049
|
+
return buildPgToolResult({
|
|
160050
|
+
success: true,
|
|
160051
|
+
data: result,
|
|
160052
|
+
message: "Migration detail retrieved via DescribePGUserMigration.",
|
|
160053
|
+
});
|
|
160054
|
+
}
|
|
160055
|
+
catch (error) {
|
|
160056
|
+
return buildPgToolResult({
|
|
160057
|
+
success: false,
|
|
160058
|
+
errorCode: "MIGRATION_API_ERROR",
|
|
160059
|
+
message: `DescribePGUserMigration failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
160060
|
+
});
|
|
160061
|
+
}
|
|
160062
|
+
}
|
|
160063
|
+
async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
|
|
160064
|
+
if (!args.objectName?.trim()) {
|
|
160065
|
+
return buildPgToolResult({
|
|
160066
|
+
success: false,
|
|
160067
|
+
errorCode: "OBJECT_NAME_REQUIRED",
|
|
160068
|
+
message: "Provide objectName (migration ID) when action=rollbackMigration.",
|
|
160069
|
+
});
|
|
160070
|
+
}
|
|
160071
|
+
if (args.confirm !== true) {
|
|
160072
|
+
return buildPgToolResult({
|
|
160073
|
+
success: false,
|
|
160074
|
+
errorCode: "CONFIRM_REQUIRED",
|
|
160075
|
+
message: "RollbackPGUserMigrations requires confirm=true. Run with confirm=true to proceed.",
|
|
160076
|
+
});
|
|
160077
|
+
}
|
|
160078
|
+
try {
|
|
160079
|
+
const result = await callPgMigrationApi(context, "RollbackPGUserMigrations", {
|
|
160080
|
+
ObjectId: args.objectName,
|
|
160081
|
+
}, cloudBaseOptions);
|
|
160082
|
+
return buildPgToolResult({
|
|
160083
|
+
success: true,
|
|
160084
|
+
data: result,
|
|
160085
|
+
message: "Migration rolled back via RollbackPGUserMigrations.",
|
|
160086
|
+
});
|
|
160087
|
+
}
|
|
160088
|
+
catch (error) {
|
|
160089
|
+
return buildPgToolResult({
|
|
160090
|
+
success: false,
|
|
160091
|
+
errorCode: "MIGRATION_API_ERROR",
|
|
160092
|
+
message: `RollbackPGUserMigrations failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
160093
|
+
});
|
|
160094
|
+
}
|
|
160095
|
+
}
|
|
160096
|
+
async function handleGetPgSchema(args, context, deps) {
|
|
160097
|
+
const objectName = args.objectName ?? "";
|
|
160098
|
+
const parsed = parseSchemaQualifiedName(objectName);
|
|
160099
|
+
if (!parsed) {
|
|
160100
|
+
return buildPgToolResult({
|
|
160101
|
+
success: false,
|
|
160102
|
+
errorCode: "SCHEMA_QUALIFIED_NAME_REQUIRED",
|
|
160103
|
+
message: "queryPgDatabase(action=schema) requires a schema-qualified object name like public.users.",
|
|
160104
|
+
});
|
|
160105
|
+
}
|
|
160106
|
+
const schemaInfo = await withPgClient(context, deps, (client) => readSchemaInfo(client, parsed.schema, parsed.name));
|
|
160107
|
+
if (!schemaInfo) {
|
|
160108
|
+
return buildPgToolResult({
|
|
160109
|
+
success: false,
|
|
160110
|
+
errorCode: "OBJECT_NOT_FOUND",
|
|
160111
|
+
message: `PostgreSQL object ${objectName} was not found in the current CloudBase PG context.`,
|
|
160112
|
+
nextActions: [
|
|
160113
|
+
buildNextAction(QUERY_PG_DATABASE, "objects", "List schema-qualified objects to find the correct table or view name.", { action: "objects", schema: parsed.schema, limit: 20 }),
|
|
160114
|
+
],
|
|
160115
|
+
});
|
|
160116
|
+
}
|
|
160117
|
+
const security = schemaInfo.security;
|
|
160118
|
+
const rlsWithoutPolicies = security.rowLevelSecurityEnabled && security.policies.length === 0;
|
|
160119
|
+
return buildPgToolResult({
|
|
160120
|
+
success: true,
|
|
160121
|
+
data: schemaInfo,
|
|
160122
|
+
message: rlsWithoutPolicies
|
|
160123
|
+
? "Resolved PostgreSQL schema, key, index, and security details. WARNING: RLS is enabled but no policies were found; browser/client reads and writes will be denied until policies are created or RLS is disabled."
|
|
160124
|
+
: "Resolved PostgreSQL schema, key, index, and security details. Use this structure before composing multi-table SQL.",
|
|
160125
|
+
nextActions: [
|
|
160126
|
+
...(rlsWithoutPolicies
|
|
160127
|
+
? [
|
|
160128
|
+
buildNextAction(MANAGE_PG_DATABASE, "execute", "Create SELECT/INSERT/UPDATE/DELETE RLS policies or disable RLS before browser-side app.rdb() CRUD.", {
|
|
160129
|
+
action: "execute",
|
|
160130
|
+
sql: `-- Example: CREATE POLICY ... ON ${objectName} FOR SELECT USING (true);`,
|
|
160131
|
+
confirm: true,
|
|
160132
|
+
}),
|
|
160133
|
+
]
|
|
160134
|
+
: []),
|
|
160135
|
+
buildNextAction(QUERY_PG_DATABASE, "metadata", "Check row-count and RLS hints for nearby tables before more complex queries.", { action: "metadata", schema: parsed.schema, limit: 20 }),
|
|
160136
|
+
buildNextAction(QUERY_PG_DATABASE, "sql", "Run a focused read-only query now that the schema is known.", {
|
|
160137
|
+
action: "sql",
|
|
160138
|
+
sql: `SELECT * FROM ${objectName} LIMIT 20`,
|
|
160139
|
+
limit: 20,
|
|
160140
|
+
}),
|
|
160141
|
+
],
|
|
160142
|
+
});
|
|
160143
|
+
}
|
|
160144
|
+
function registerPGDatabaseTools(server, providedDeps) {
|
|
160145
|
+
const deps = {
|
|
160146
|
+
...createDefaultDependencies(server),
|
|
160147
|
+
...providedDeps,
|
|
160148
|
+
};
|
|
160149
|
+
server.registerTool?.(QUERY_PG_DATABASE, {
|
|
160150
|
+
title: "查询 PostgreSQL 上下文、对象、元数据或执行只读 SQL",
|
|
160151
|
+
description: "查询 CloudBase PostgreSQL 数据库。支持获取当前 PG 上下文、列出带 schema 的数据库对象、读取轻量元数据、检查单个对象结构,以及执行只读 SQL。",
|
|
160152
|
+
inputSchema: {
|
|
160153
|
+
action: zod_1.z
|
|
160154
|
+
.enum(QUERY_ACTIONS)
|
|
160155
|
+
.describe("操作类型:context=获取当前 PostgreSQL 上下文;objects=列出带 schema 的数据库对象;metadata=获取轻量表元数据;schema=检查单个带 schema 的对象结构;sql=执行只读 SQL"),
|
|
160156
|
+
sql: zod_1.z.string().optional().describe("action=sql 时使用的只读 SQL"),
|
|
160157
|
+
objectName: zod_1.z
|
|
160158
|
+
.string()
|
|
160159
|
+
.optional()
|
|
160160
|
+
.describe("action=schema 时使用的带 schema 的 PostgreSQL 对象名,例如 public.users"),
|
|
160161
|
+
schema: zod_1.z
|
|
160162
|
+
.string()
|
|
160163
|
+
.optional()
|
|
160164
|
+
.describe("可选的 schema 过滤条件,用于 action=objects 或 action=metadata"),
|
|
160165
|
+
limit: zod_1.z
|
|
160166
|
+
.number()
|
|
160167
|
+
.int()
|
|
160168
|
+
.min(1)
|
|
160169
|
+
.max(200)
|
|
160170
|
+
.optional()
|
|
160171
|
+
.describe("可选的摘要数量上限,用于对象、元数据或 SQL 返回行数,默认 20,最大 200。"),
|
|
160172
|
+
},
|
|
160173
|
+
annotations: {
|
|
160174
|
+
readOnlyHint: true,
|
|
160175
|
+
openWorldHint: true,
|
|
160176
|
+
category: CATEGORY,
|
|
160177
|
+
},
|
|
160178
|
+
}, async (args) => {
|
|
160179
|
+
if (args.action === "context") {
|
|
160180
|
+
return handleQueryContext(server);
|
|
160181
|
+
}
|
|
160182
|
+
const context = await getPgContext(server);
|
|
160183
|
+
if (!context) {
|
|
160184
|
+
return buildMissingContextResult();
|
|
160185
|
+
}
|
|
160186
|
+
switch (args.action) {
|
|
160187
|
+
case "objects":
|
|
160188
|
+
return handleListObjects(args, context, deps);
|
|
160189
|
+
case "metadata":
|
|
160190
|
+
return handleMetadata(args, context, deps);
|
|
160191
|
+
case "schema":
|
|
160192
|
+
return handleGetPgSchema(args, context, deps);
|
|
160193
|
+
case "sql":
|
|
160194
|
+
return handleReadOnlySql(args, context, deps);
|
|
160195
|
+
default:
|
|
160196
|
+
return buildPgToolResult({
|
|
160197
|
+
success: false,
|
|
160198
|
+
errorCode: "UNSUPPORTED_ACTION",
|
|
160199
|
+
message: `Unsupported PostgreSQL query action: ${args.action}`,
|
|
160200
|
+
});
|
|
160201
|
+
}
|
|
160202
|
+
});
|
|
160203
|
+
server.registerTool?.(MANAGE_PG_DATABASE, {
|
|
160204
|
+
title: "管理 PostgreSQL 上下文或执行写入 SQL",
|
|
160205
|
+
description: "管理 CloudBase PostgreSQL MCP 上下文。仅支持 CloudBase 内置 PostgreSQL 云端 Manager SDK 执行路径、SQL 风险预检、迁移规划,以及在显式确认后执行写入 SQL。",
|
|
160206
|
+
inputSchema: {
|
|
160207
|
+
action: zod_1.z
|
|
160208
|
+
.enum(MANAGE_ACTIONS)
|
|
160209
|
+
.describe("操作类型:init=初始化或绑定 PostgreSQL 上下文;execute=执行已确认的写入 SQL 或 DDL;dryRun=只分析 SQL 风险不执行;planMigration=通过 PreviewPGUserMigrations 预览迁移计划;applyMigration=通过 PushPGUserMigrations 应用迁移;listMigrations=查询已应用的 Migration 列表;migrationDetail=查看单条 Migration 详情;rollbackMigration=回滚指定 Migration"),
|
|
160210
|
+
sql: zod_1.z
|
|
160211
|
+
.string()
|
|
160212
|
+
.optional()
|
|
160213
|
+
.describe("action=execute、dryRun、planMigration 或 applyMigration 使用的 SQL 语句"),
|
|
160214
|
+
confirm: zod_1.z
|
|
160215
|
+
.boolean()
|
|
160216
|
+
.optional()
|
|
160217
|
+
.describe("执行任何写入 SQL 前都需要显式设置为 true。"),
|
|
160218
|
+
envId: zod_1.z
|
|
160219
|
+
.string()
|
|
160220
|
+
.optional()
|
|
160221
|
+
.describe("可选的 CloudBase 环境 ID,不传时使用当前 MCP 环境。"),
|
|
160222
|
+
instanceId: zod_1.z
|
|
160223
|
+
.string()
|
|
160224
|
+
.optional()
|
|
160225
|
+
.describe("可选的 PostgreSQL 逻辑实例标识。"),
|
|
160226
|
+
defaultSchema: zod_1.z
|
|
160227
|
+
.string()
|
|
160228
|
+
.optional()
|
|
160229
|
+
.describe("默认 schema,会保存到 PG 上下文中,默认 public。"),
|
|
160230
|
+
role: zod_1.z
|
|
160231
|
+
.string()
|
|
160232
|
+
.optional()
|
|
160233
|
+
.describe("可选的 PostgreSQL role,会传给 Manager SDK executePGSql;例如需要管理策略时可传 postgres。"),
|
|
160234
|
+
objectName: zod_1.z
|
|
160235
|
+
.string()
|
|
160236
|
+
.optional()
|
|
160237
|
+
.describe("action=migrationDetail 或 rollbackMigration 时使用的 migration ID。"),
|
|
160238
|
+
},
|
|
160239
|
+
annotations: {
|
|
160240
|
+
readOnlyHint: false,
|
|
160241
|
+
destructiveHint: true,
|
|
160242
|
+
idempotentHint: false,
|
|
160243
|
+
openWorldHint: true,
|
|
160244
|
+
category: CATEGORY,
|
|
160245
|
+
},
|
|
160246
|
+
}, async (args) => {
|
|
160247
|
+
switch (args.action) {
|
|
160248
|
+
case "init":
|
|
160249
|
+
return handleInit(args, server, deps);
|
|
160250
|
+
case "execute": {
|
|
160251
|
+
const context = await getPgContext(server);
|
|
160252
|
+
if (!context) {
|
|
160253
|
+
return buildMissingContextResult();
|
|
160254
|
+
}
|
|
160255
|
+
return handleExecuteSql(args, context, deps);
|
|
160256
|
+
}
|
|
160257
|
+
case "dryRun":
|
|
160258
|
+
return handleDryRun(args);
|
|
160259
|
+
case "planMigration":
|
|
160260
|
+
case "applyMigration":
|
|
160261
|
+
case "listMigrations":
|
|
160262
|
+
case "migrationDetail":
|
|
160263
|
+
case "rollbackMigration": {
|
|
160264
|
+
const migrationCtx = await getPgContext(server);
|
|
160265
|
+
if (!migrationCtx) {
|
|
160266
|
+
return buildMissingContextResult();
|
|
160267
|
+
}
|
|
160268
|
+
const cbOpts = server.cloudBaseOptions;
|
|
160269
|
+
switch (args.action) {
|
|
160270
|
+
case "planMigration": return handlePlanMigration(args, migrationCtx, deps, cbOpts);
|
|
160271
|
+
case "applyMigration": return handleApplyMigration(args, migrationCtx, deps, cbOpts);
|
|
160272
|
+
case "listMigrations": return handleListMigrations(args, migrationCtx, deps, cbOpts);
|
|
160273
|
+
case "migrationDetail": return handleMigrationDetail(args, migrationCtx, deps, cbOpts);
|
|
160274
|
+
case "rollbackMigration": return handleRollbackMigration(args, migrationCtx, deps, cbOpts);
|
|
160275
|
+
default: return buildPgToolResult({ success: false, errorCode: "UNSUPPORTED_ACTION", message: `Unsupported action: ${args.action}` });
|
|
160276
|
+
}
|
|
160277
|
+
}
|
|
160278
|
+
default:
|
|
160279
|
+
return buildPgToolResult({
|
|
160280
|
+
success: false,
|
|
160281
|
+
errorCode: "UNSUPPORTED_ACTION",
|
|
160282
|
+
message: `Unsupported PostgreSQL manage action: ${args.action}`,
|
|
160283
|
+
});
|
|
160284
|
+
}
|
|
160285
|
+
});
|
|
160286
|
+
}
|
|
160287
|
+
|
|
160288
|
+
|
|
158592
160289
|
/***/ }),
|
|
158593
160290
|
|
|
158594
160291
|
/***/ 56575:
|
|
@@ -158874,7 +160571,7 @@ Redirect.prototype.onResponse = function (response) {
|
|
|
158874
160571
|
return true
|
|
158875
160572
|
}
|
|
158876
160573
|
|
|
158877
|
-
exports.
|
|
160574
|
+
exports.Redirect = Redirect
|
|
158878
160575
|
|
|
158879
160576
|
|
|
158880
160577
|
/***/ }),
|
|
@@ -166441,7 +168138,7 @@ function once(callback) {
|
|
|
166441
168138
|
callback.apply(void 0, arguments);
|
|
166442
168139
|
};
|
|
166443
168140
|
}
|
|
166444
|
-
var _require$codes = (__webpack_require__(30113)
|
|
168141
|
+
var _require$codes = (__webpack_require__(30113).codes),
|
|
166445
168142
|
ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS,
|
|
166446
168143
|
ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED;
|
|
166447
168144
|
function noop(err) {
|
|
@@ -172143,7 +173840,7 @@ Querystring.prototype.rfc3986 = function (str) {
|
|
|
172143
173840
|
|
|
172144
173841
|
Querystring.prototype.unescape = querystring.unescape
|
|
172145
173842
|
|
|
172146
|
-
exports.
|
|
173843
|
+
exports.Querystring = Querystring
|
|
172147
173844
|
|
|
172148
173845
|
|
|
172149
173846
|
/***/ }),
|
|
@@ -180429,7 +182126,7 @@ async function registerRagTools(server) {
|
|
|
180429
182126
|
.map((skill) => `文档名:${path.basename(path.dirname(skill.absolutePath))} 文档介绍:${skill.description}`)
|
|
180430
182127
|
.join("\n")}
|
|
180431
182128
|
|
|
180432
|
-
OpenAPI 文档 (openapi)
|
|
182129
|
+
OpenAPI 文档 (openapi) 查询只需要传 mode="openapi" 和 apiName,不要传 action;action 仅用于 mode="docs"。当前支持 ${openapis.length} 个 API 文档,分别是:
|
|
180433
182130
|
${openapis
|
|
180434
182131
|
.map((api) => `API名:${api.name} API介绍:${api.description}`)
|
|
180435
182132
|
.join("\n")}`,
|
|
@@ -180437,7 +182134,7 @@ async function registerRagTools(server) {
|
|
|
180437
182134
|
mode: SearchKnowledgeModeEnum,
|
|
180438
182135
|
skillName: buildOptionalStringEnum(skillNames, "mode=skill 时指定。技能名称。"),
|
|
180439
182136
|
apiName: buildOptionalStringEnum(openapiNames, "mode=openapi 时指定。API 名称。"),
|
|
180440
|
-
action: CloudBaseDocsActionEnum.optional().describe("mode=docs
|
|
182137
|
+
action: CloudBaseDocsActionEnum.optional().describe("仅 mode=docs 时指定;mode=openapi 不要传 action。CloudBase 文档操作类型:listModules=列出所有文档模块,listModuleDocs=获取指定模块的目录结构,findByName=按名称/路径/URL 智能查找,readDoc=读取指定文档 Markdown,searchDocs=全文搜索官方文档。"),
|
|
180441
182138
|
moduleName: zod_1.z
|
|
180442
182139
|
.string()
|
|
180443
182140
|
.optional()
|
|
@@ -183192,14 +184889,14 @@ var isTypedArray = (__webpack_require__(89225).strict)
|
|
|
183192
184889
|
var helpers = __webpack_require__(64179)
|
|
183193
184890
|
var cookies = __webpack_require__(75997)
|
|
183194
184891
|
var getProxyFromURI = __webpack_require__(37404)
|
|
183195
|
-
var Querystring = (__webpack_require__(61171)
|
|
183196
|
-
var Har = (__webpack_require__(21849)
|
|
183197
|
-
var Auth = (__webpack_require__(94174)
|
|
183198
|
-
var OAuth = (__webpack_require__(757)
|
|
184892
|
+
var Querystring = (__webpack_require__(61171).Querystring)
|
|
184893
|
+
var Har = (__webpack_require__(21849).Har)
|
|
184894
|
+
var Auth = (__webpack_require__(94174).Auth)
|
|
184895
|
+
var OAuth = (__webpack_require__(757).OAuth)
|
|
183199
184896
|
var hawk = __webpack_require__(85618)
|
|
183200
|
-
var Multipart = (__webpack_require__(44954)
|
|
183201
|
-
var Redirect = (__webpack_require__(56696)
|
|
183202
|
-
var Tunnel = (__webpack_require__(85526)
|
|
184897
|
+
var Multipart = (__webpack_require__(44954).Multipart)
|
|
184898
|
+
var Redirect = (__webpack_require__(56696).Redirect)
|
|
184899
|
+
var Tunnel = (__webpack_require__(85526).Tunnel)
|
|
183203
184900
|
var now = __webpack_require__(13491)
|
|
183204
184901
|
var Buffer = (__webpack_require__(92861).Buffer)
|
|
183205
184902
|
|
|
@@ -184916,7 +186613,7 @@ exports.getProjectStore = getProjectStore;
|
|
|
184916
186613
|
"use strict";
|
|
184917
186614
|
|
|
184918
186615
|
|
|
184919
|
-
var ERR_INVALID_OPT_VALUE = (__webpack_require__(30113)
|
|
186616
|
+
var ERR_INVALID_OPT_VALUE = (__webpack_require__(30113).codes).ERR_INVALID_OPT_VALUE;
|
|
184920
186617
|
function highWaterMarkFrom(options, isDuplex, duplexKey) {
|
|
184921
186618
|
return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null;
|
|
184922
186619
|
}
|
|
@@ -185764,7 +187461,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
|
185764
187461
|
exports.CloudBaseContext = void 0;
|
|
185765
187462
|
const constant_1 = __webpack_require__(40762);
|
|
185766
187463
|
class CloudBaseContext {
|
|
185767
|
-
constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined }) {
|
|
187464
|
+
constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined, requestFn = undefined }) {
|
|
185768
187465
|
this.secretId = secretId;
|
|
185769
187466
|
this.secretKey = secretKey;
|
|
185770
187467
|
this.token = token;
|
|
@@ -185773,6 +187470,7 @@ class CloudBaseContext {
|
|
|
185773
187470
|
this.envType = envType;
|
|
185774
187471
|
this.useInternalEndpoint = useInternalEndpoint;
|
|
185775
187472
|
this.internalEndpointRegion = internalEndpointRegion;
|
|
187473
|
+
this.requestFn = requestFn;
|
|
185776
187474
|
}
|
|
185777
187475
|
isInternalEndpoint() {
|
|
185778
187476
|
return this.useInternalEndpoint !== undefined ? this.useInternalEndpoint : constant_1.USE_INTERNAL_ENDPOINT;
|
|
@@ -193669,7 +195367,7 @@ ${envIdSection}
|
|
|
193669
195367
|
## 环境信息
|
|
193670
195368
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
193671
195369
|
- Node.js版本: ${process.version}
|
|
193672
|
-
- MCP 版本:${process.env.npm_package_version || "2.
|
|
195370
|
+
- MCP 版本:${process.env.npm_package_version || "2.22.0-beta.0" || 0}
|
|
193673
195371
|
- 系统架构: ${os_1.default.arch()}
|
|
193674
195372
|
- 时间: ${new Date().toISOString()}
|
|
193675
195373
|
- 请求ID: ${requestId}
|
|
@@ -193750,7 +195448,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
193750
195448
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
193751
195449
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
193752
195450
|
});
|
|
193753
|
-
const mcpVersion = true ? "2.
|
|
195451
|
+
const mcpVersion = true ? "2.22.0-beta.0" : 0;
|
|
193754
195452
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
193755
195453
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
193756
195454
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -199380,9 +201078,9 @@ Promise.filter = function (promises, fn, options) {
|
|
|
199380
201078
|
const punycode = __webpack_require__(48379);
|
|
199381
201079
|
const urlParse = __webpack_require__(61160);
|
|
199382
201080
|
const pubsuffix = __webpack_require__(61177);
|
|
199383
|
-
const Store = (__webpack_require__(94836)
|
|
199384
|
-
const MemoryCookieStore = (__webpack_require__(82691)
|
|
199385
|
-
const pathMatch = (__webpack_require__(75321)
|
|
201081
|
+
const Store = (__webpack_require__(94836).Store);
|
|
201082
|
+
const MemoryCookieStore = (__webpack_require__(82691).MemoryCookieStore);
|
|
201083
|
+
const pathMatch = (__webpack_require__(75321).pathMatch);
|
|
199386
201084
|
const validators = __webpack_require__(82982);
|
|
199387
201085
|
const VERSION = __webpack_require__(63093);
|
|
199388
201086
|
const { fromCallback } = __webpack_require__(18964);
|
|
@@ -203179,6 +204877,13 @@ async function callNoSqlContentApi(options) {
|
|
|
203179
204877
|
instanceIdSource: "envId",
|
|
203180
204878
|
requestId: result?.RequestId,
|
|
203181
204879
|
});
|
|
204880
|
+
// 检查 API 层返回的业务错误(如集合不存在)并抛出
|
|
204881
|
+
if (result?.Error) {
|
|
204882
|
+
const err = new Error(`${result.Error.Code}: ${result.Error.Message}`);
|
|
204883
|
+
err.code = result.Error.Code;
|
|
204884
|
+
err.requestId = result.RequestId;
|
|
204885
|
+
throw err;
|
|
204886
|
+
}
|
|
203182
204887
|
return result;
|
|
203183
204888
|
}
|
|
203184
204889
|
catch (error) {
|
|
@@ -203192,7 +204897,7 @@ async function callNoSqlContentApi(options) {
|
|
|
203192
204897
|
throw error;
|
|
203193
204898
|
}
|
|
203194
204899
|
}
|
|
203195
|
-
async function waitForCollectionReady({ cloudbase, collectionName, logger, timeoutMs = COLLECTION_READY_TIMEOUT_MS, pollIntervalMs = COLLECTION_READY_POLL_INTERVAL_MS, }) {
|
|
204900
|
+
async function waitForCollectionReady({ cloudbase, collectionName, logger, cloudBaseOptions, timeoutMs = COLLECTION_READY_TIMEOUT_MS, pollIntervalMs = COLLECTION_READY_POLL_INTERVAL_MS, }) {
|
|
203196
204901
|
const startedAt = Date.now();
|
|
203197
204902
|
const deadline = startedAt + timeoutMs;
|
|
203198
204903
|
let lastError;
|
|
@@ -203206,17 +204911,40 @@ async function waitForCollectionReady({ cloudbase, collectionName, logger, timeo
|
|
|
203206
204911
|
});
|
|
203207
204912
|
while (Date.now() <= deadline) {
|
|
203208
204913
|
try {
|
|
203209
|
-
//
|
|
203210
|
-
|
|
203211
|
-
const
|
|
203212
|
-
|
|
204914
|
+
// 用 PutItem 探测集合是否真正就绪(DescribeTable 可能误报),插入一个空占位文档
|
|
204915
|
+
// 如果成功,立即删除该文档;如果仍 ResourceNotFound,继续等待
|
|
204916
|
+
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
204917
|
+
const probeDoc = JSON.stringify({ _readyProbe: true, _ts: Date.now() });
|
|
204918
|
+
const probeResult = await cloudbase.commonService("tcb", "2018-06-08").call({
|
|
204919
|
+
Action: "PutItem",
|
|
203213
204920
|
Param: {
|
|
203214
204921
|
EnvId: envId,
|
|
203215
204922
|
TableName: collectionName,
|
|
204923
|
+
MgoDocs: [probeDoc],
|
|
203216
204924
|
},
|
|
203217
204925
|
});
|
|
203218
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger,
|
|
203219
|
-
|
|
204926
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, probeResult);
|
|
204927
|
+
if (probeResult?.Error) {
|
|
204928
|
+
// 集合还未就绪,继续轮询
|
|
204929
|
+
throw new Error(probeResult.Error.Message);
|
|
204930
|
+
}
|
|
204931
|
+
// 集合就绪,清理探测文档
|
|
204932
|
+
if (Array.isArray(probeResult?.InsertedIds) && probeResult.InsertedIds.length > 0) {
|
|
204933
|
+
try {
|
|
204934
|
+
await cloudbase.commonService("tcb", "2018-06-08").call({
|
|
204935
|
+
Action: "DeleteItem",
|
|
204936
|
+
Param: {
|
|
204937
|
+
EnvId: envId,
|
|
204938
|
+
TableName: collectionName,
|
|
204939
|
+
MgoQuery: JSON.stringify({ _readyProbe: true }),
|
|
204940
|
+
MgoIsMulti: true,
|
|
204941
|
+
},
|
|
204942
|
+
});
|
|
204943
|
+
}
|
|
204944
|
+
catch {
|
|
204945
|
+
// 清理失败不影响主流程
|
|
204946
|
+
}
|
|
204947
|
+
}
|
|
203220
204948
|
logger?.({
|
|
203221
204949
|
type: "toolInfo",
|
|
203222
204950
|
toolName: "writeNoSqlDatabaseStructure",
|
|
@@ -203255,8 +204983,8 @@ function registerDatabaseTools(server) {
|
|
|
203255
204983
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
203256
204984
|
// readNoSqlDatabaseStructure
|
|
203257
204985
|
server.registerTool?.("readNoSqlDatabaseStructure", {
|
|
203258
|
-
title: "读取 NoSQL 数据库结构",
|
|
203259
|
-
description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。本工具为服务端管理工具,用于管理端查询数据库结构,不用于编写客户端代码。",
|
|
204986
|
+
title: "读取 CloudBase NoSQL 数据库结构",
|
|
204987
|
+
description: "读取 CloudBase NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。本工具为服务端管理工具,用于管理端查询数据库结构,不用于编写客户端代码。",
|
|
203260
204988
|
inputSchema: {
|
|
203261
204989
|
action: zod_1.z.enum([
|
|
203262
204990
|
"listCollections",
|
|
@@ -203443,8 +205171,8 @@ checkIndex: 检查指定索引是否存在`),
|
|
|
203443
205171
|
});
|
|
203444
205172
|
// writeNoSqlDatabaseStructure
|
|
203445
205173
|
server.registerTool?.("writeNoSqlDatabaseStructure", {
|
|
203446
|
-
title: "创建并管理 NoSQL 数据库集合",
|
|
203447
|
-
description: "创建、删除和管理 NoSQL 数据库集合(collection)。支持创建新集合、删除现有集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。当需要新建集合时,使用 action=createCollection。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。",
|
|
205174
|
+
title: "创建并管理 CloudBase NoSQL 数据库集合",
|
|
205175
|
+
description: "创建、删除和管理 CloudBase NoSQL 数据库集合(collection)。支持创建新集合、删除现有集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。当需要新建集合时,使用 action=createCollection。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。",
|
|
203448
205176
|
inputSchema: {
|
|
203449
205177
|
action: zod_1.z.enum([
|
|
203450
205178
|
"createCollection",
|
|
@@ -203504,8 +205232,14 @@ deleteCollection: 删除集合`),
|
|
|
203504
205232
|
Action: "DescribeTable",
|
|
203505
205233
|
Param: { EnvId: envId, TableName: collectionName },
|
|
203506
205234
|
});
|
|
203507
|
-
|
|
203508
|
-
|
|
205235
|
+
if (descResult?.Error) {
|
|
205236
|
+
// 集合不存在(ResourceNotFound)或其他错误,继续创建
|
|
205237
|
+
existsRequestId = descResult.RequestId ?? "";
|
|
205238
|
+
}
|
|
205239
|
+
else {
|
|
205240
|
+
collectionExists = true;
|
|
205241
|
+
existsRequestId = descResult?.RequestId ?? "";
|
|
205242
|
+
}
|
|
203509
205243
|
}
|
|
203510
205244
|
catch (e) {
|
|
203511
205245
|
existsRequestId = e?.requestId ?? "";
|
|
@@ -203538,6 +205272,7 @@ deleteCollection: 删除集合`),
|
|
|
203538
205272
|
cloudbase,
|
|
203539
205273
|
collectionName,
|
|
203540
205274
|
logger: server.logger,
|
|
205275
|
+
cloudBaseOptions: server.cloudBaseOptions,
|
|
203541
205276
|
});
|
|
203542
205277
|
return {
|
|
203543
205278
|
content: [
|
|
@@ -203620,8 +205355,8 @@ deleteCollection: 删除集合`),
|
|
|
203620
205355
|
});
|
|
203621
205356
|
// readNoSqlDatabaseContent
|
|
203622
205357
|
server.registerTool?.("readNoSqlDatabaseContent", {
|
|
203623
|
-
title: "查询并获取 NoSQL 数据库数据记录",
|
|
203624
|
-
description: "
|
|
205358
|
+
title: "查询并获取 CloudBase NoSQL 数据库数据记录",
|
|
205359
|
+
description: "查询 CloudBase NoSQL 数据库中的数据记录。支持按条件筛选、分页、排序,适用于管理端数据查询与运维。",
|
|
203625
205360
|
inputSchema: {
|
|
203626
205361
|
collectionName: zod_1.z.string().describe("集合名称"),
|
|
203627
205362
|
instanceId: zod_1.z
|
|
@@ -203707,8 +205442,10 @@ deleteCollection: 删除集合`),
|
|
|
203707
205442
|
});
|
|
203708
205443
|
// writeNoSqlDatabaseContent
|
|
203709
205444
|
server.registerTool?.("writeNoSqlDatabaseContent", {
|
|
203710
|
-
title: "修改 NoSQL 数据库数据记录",
|
|
203711
|
-
description: "修改 NoSQL
|
|
205445
|
+
title: "修改 CloudBase NoSQL 数据库数据记录",
|
|
205446
|
+
description: "修改 CloudBase NoSQL 数据库中的数据记录。支持插入、更新(含 $set/$inc/$push 等操作符)、删除、upsert 等操作,适用于管理端数据写入与运维。" +
|
|
205447
|
+
"⚠️ 服务端写入不含 _openid:若集合依赖客户端 SDK(@cloudbase/js-sdk 或微信小程序 wx.cloud.database())的行级安全规则(如 doc._openid == auth.openid),服务端写入时需手动补充 _openid 字段,否则客户端将无法读取到该数据。" +
|
|
205448
|
+
"⚠️ 部分更新嵌套字段须使用点号路径,如 `$set: {\"shipping.city\": \"guangzhou\"}`,直接传嵌套对象会覆盖整个字段。",
|
|
203712
205449
|
inputSchema: {
|
|
203713
205450
|
action: zod_1.z
|
|
203714
205451
|
.enum(["insert", "update", "delete"])
|
|
@@ -204669,7 +206406,7 @@ function clean(key)
|
|
|
204669
206406
|
|
|
204670
206407
|
|
|
204671
206408
|
module.exports = Transform;
|
|
204672
|
-
var _require$codes = (__webpack_require__(30113)
|
|
206409
|
+
var _require$codes = (__webpack_require__(30113).codes),
|
|
204673
206410
|
ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
|
|
204674
206411
|
ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
|
|
204675
206412
|
ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING,
|
|
@@ -205793,7 +207530,7 @@ function pathMatch(reqPath, cookiePath) {
|
|
|
205793
207530
|
return false;
|
|
205794
207531
|
}
|
|
205795
207532
|
|
|
205796
|
-
exports.
|
|
207533
|
+
exports.pathMatch = pathMatch;
|
|
205797
207534
|
|
|
205798
207535
|
|
|
205799
207536
|
/***/ }),
|
|
@@ -208754,7 +210491,7 @@ function registerPermissionTools(server) {
|
|
|
208754
210491
|
};
|
|
208755
210492
|
server.registerTool?.("queryPermissions", {
|
|
208756
210493
|
title: "查询 CloudBase 权限与用户配置",
|
|
208757
|
-
description: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`",
|
|
210494
|
+
description: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`。`resourceType=\"noSqlDatabase\"` 查询的是 CloudBase NoSQL 集合规则,与 CloudBase PostgreSQL(PG)表的行级安全(RLS)是两套独立机制——同一个 PG 环境里 NoSQL 集合若仍在使用,对那些集合查询本工具结果**仍然有效**。要查 PG 表 RLS,请改用 `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")`。本工具不涉及 MySQL 权限。",
|
|
208758
210495
|
inputSchema: {
|
|
208759
210496
|
action: zod_1.z.enum(QUERY_PERMISSION_ACTIONS),
|
|
208760
210497
|
resourceType: zod_1.z
|
|
@@ -208915,7 +210652,7 @@ function registerPermissionTools(server) {
|
|
|
208915
210652
|
}));
|
|
208916
210653
|
server.registerTool?.("managePermissions", {
|
|
208917
210654
|
title: "管理 CloudBase 权限与用户配置",
|
|
208918
|
-
description: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK
|
|
210655
|
+
description: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。直接在浏览器里用 `auth.signUp` 创建用户名密码用户取决于 SDK/provider 支持,使用前必须验证;不支持时应走后端或管理端边界,不能在浏览器暴露密钥。`securityRule` 的详细语义取决于 `resourceType`:`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则;配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`:\n- `resourceType=\"noSqlDatabase\"` 仅作用于 CloudBase NoSQL 文档数据库的集合;CloudBase PostgreSQL(PG)表的行级权限**不**受它控制——PG 表请改用 RLS:`managePgDatabase(action=\"execute\", confirm=true)` 跑 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` 与 `CREATE POLICY ...`。同一个 PG 环境里如果还有 NoSQL 集合在用,对那些**集合**继续使用 `noSqlDatabase` 规则是正确的——不是\"PG 环境就禁用本工具\"。\n- `resourceType=\"storage\"` 控制的是 NoSQL/COS 存储桶 ACL;PG 的 `pgstore` bucket 不在此 `resourceType` 覆盖范围内。\n- 本工具不涉及 MySQL;MySQL 数据库权限请走 MySQL 自身的 GRANT/REVOKE 语句(通过 `manageSqlDatabase`)。",
|
|
208919
210656
|
inputSchema: {
|
|
208920
210657
|
action: zod_1.z.enum(MANAGE_PERMISSION_ACTIONS),
|
|
208921
210658
|
resourceType: zod_1.z
|
|
@@ -209723,7 +211460,7 @@ function registerSetupTools(server) {
|
|
|
209723
211460
|
server.registerTool("downloadTemplate", {
|
|
209724
211461
|
title: "下载项目模板",
|
|
209725
211462
|
description: `自动下载并部署CloudBase项目模板。
|
|
209726
|
-
**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.
|
|
211463
|
+
**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.22.0-beta.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
209727
211464
|
inputSchema: {
|
|
209728
211465
|
template: zod_1.z
|
|
209729
211466
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -214066,7 +215803,7 @@ function normalizeEncoding(enc) {
|
|
|
214066
215803
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
214067
215804
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
214068
215805
|
// characters.
|
|
214069
|
-
exports.
|
|
215806
|
+
exports.StringDecoder = StringDecoder;
|
|
214070
215807
|
function StringDecoder(encoding) {
|
|
214071
215808
|
this.encoding = normalizeEncoding(encoding);
|
|
214072
215809
|
var nb;
|
|
@@ -219910,12 +221647,13 @@ function getDate(timestamp) {
|
|
|
219910
221647
|
}
|
|
219911
221648
|
class CloudService {
|
|
219912
221649
|
/* eslint-disable-next-line */
|
|
219913
|
-
constructor(context, service, version, baseParams) {
|
|
221650
|
+
constructor(context, service, version, baseParams, requestFn) {
|
|
219914
221651
|
this.service = service;
|
|
219915
221652
|
this.version = version;
|
|
219916
221653
|
this.timeout = 60000;
|
|
219917
221654
|
this.baseParams = baseParams || {};
|
|
219918
221655
|
this.cloudBaseContext = context;
|
|
221656
|
+
this.requestFn = requestFn !== null && requestFn !== void 0 ? requestFn : context.requestFn;
|
|
219919
221657
|
}
|
|
219920
221658
|
get baseUrl() {
|
|
219921
221659
|
const internalEndpoint = this.cloudBaseContext.isInternalEndpoint();
|
|
@@ -219947,8 +221685,31 @@ class CloudService {
|
|
|
219947
221685
|
}
|
|
219948
221686
|
}
|
|
219949
221687
|
async request(action, data = {}, method = 'POST') {
|
|
221688
|
+
var _a;
|
|
219950
221689
|
this.action = action;
|
|
219951
221690
|
this.data = deepRemoveVoid(Object.assign(Object.assign({}, data), this.baseParams));
|
|
221691
|
+
// 如果注入了 requestFn,直接委托,跳过 TC3 签名逻辑
|
|
221692
|
+
if (this.requestFn) {
|
|
221693
|
+
try {
|
|
221694
|
+
const result = await this.requestFn({
|
|
221695
|
+
service: this.service,
|
|
221696
|
+
action,
|
|
221697
|
+
version: this.version,
|
|
221698
|
+
region: (_a = this.cloudBaseContext.region) !== null && _a !== void 0 ? _a : '',
|
|
221699
|
+
payload: this.data,
|
|
221700
|
+
});
|
|
221701
|
+
return result;
|
|
221702
|
+
}
|
|
221703
|
+
catch (e) {
|
|
221704
|
+
if (e.name === 'CloudBaseError') {
|
|
221705
|
+
throw e;
|
|
221706
|
+
}
|
|
221707
|
+
throw new error_1.CloudBaseError(e.message, {
|
|
221708
|
+
action,
|
|
221709
|
+
code: e.code
|
|
221710
|
+
});
|
|
221711
|
+
}
|
|
221712
|
+
}
|
|
219952
221713
|
this.method = method;
|
|
219953
221714
|
this.url = this.baseUrl;
|
|
219954
221715
|
let { secretId, secretKey, token } = this.cloudBaseContext;
|
|
@@ -222421,7 +224182,6 @@ module.exports = scan;
|
|
|
222421
224182
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
222422
224183
|
|
|
222423
224184
|
"use strict";
|
|
222424
|
-
var __webpack_unused_export__;
|
|
222425
224185
|
/*!
|
|
222426
224186
|
* Copyright (c) 2015, Salesforce.com, Inc.
|
|
222427
224187
|
* All rights reserved.
|
|
@@ -222454,9 +224214,9 @@ var __webpack_unused_export__;
|
|
|
222454
224214
|
*/
|
|
222455
224215
|
|
|
222456
224216
|
const { fromCallback } = __webpack_require__(18964);
|
|
222457
|
-
const Store = (__webpack_require__(94836)
|
|
224217
|
+
const Store = (__webpack_require__(94836).Store);
|
|
222458
224218
|
const permuteDomain = (__webpack_require__(37791).permuteDomain);
|
|
222459
|
-
const pathMatch = (__webpack_require__(75321)
|
|
224219
|
+
const pathMatch = (__webpack_require__(75321).pathMatch);
|
|
222460
224220
|
const { getCustomInspectSymbol, getUtilInspect } = __webpack_require__(29921);
|
|
222461
224221
|
|
|
222462
224222
|
class MemoryCookieStore extends Store {
|
|
@@ -222615,7 +224375,7 @@ class MemoryCookieStore extends Store {
|
|
|
222615
224375
|
);
|
|
222616
224376
|
});
|
|
222617
224377
|
|
|
222618
|
-
exports.
|
|
224378
|
+
exports.MemoryCookieStore = MemoryCookieStore;
|
|
222619
224379
|
|
|
222620
224380
|
function inspectFallback(val) {
|
|
222621
224381
|
const domains = Object.keys(val);
|
|
@@ -222663,7 +224423,7 @@ function formatPath(pathName, pathValue) {
|
|
|
222663
224423
|
return result;
|
|
222664
224424
|
}
|
|
222665
224425
|
|
|
222666
|
-
|
|
224426
|
+
exports.inspectFallback = inspectFallback;
|
|
222667
224427
|
|
|
222668
224428
|
|
|
222669
224429
|
/***/ }),
|
|
@@ -223262,7 +225022,7 @@ function ReadableState(options, stream) {
|
|
|
223262
225022
|
this.decoder = null;
|
|
223263
225023
|
this.encoding = null;
|
|
223264
225024
|
if (options.encoding) {
|
|
223265
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(78247)
|
|
225025
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(78247).StringDecoder);
|
|
223266
225026
|
this.decoder = new StringDecoder(options.encoding);
|
|
223267
225027
|
this.encoding = options.encoding;
|
|
223268
225028
|
}
|
|
@@ -223418,7 +225178,7 @@ Readable.prototype.isPaused = function () {
|
|
|
223418
225178
|
|
|
223419
225179
|
// backwards compatibility.
|
|
223420
225180
|
Readable.prototype.setEncoding = function (enc) {
|
|
223421
|
-
if (!StringDecoder) StringDecoder = (__webpack_require__(78247)
|
|
225181
|
+
if (!StringDecoder) StringDecoder = (__webpack_require__(78247).StringDecoder);
|
|
223422
225182
|
this._readableState.decoder = new StringDecoder(enc);
|
|
223423
225183
|
this._readableState.encoding = enc;
|
|
223424
225184
|
return this;
|
|
@@ -224404,7 +226164,7 @@ function normalizeEncoding(enc) {
|
|
|
224404
226164
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
224405
226165
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
224406
226166
|
// characters.
|
|
224407
|
-
exports.
|
|
226167
|
+
exports.StringDecoder = StringDecoder;
|
|
224408
226168
|
function StringDecoder(encoding) {
|
|
224409
226169
|
this.encoding = normalizeEncoding(encoding);
|
|
224410
226170
|
var nb;
|
|
@@ -227463,7 +229223,7 @@ function normalizeEncoding(enc) {
|
|
|
227463
229223
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
227464
229224
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
227465
229225
|
// characters.
|
|
227466
|
-
exports.
|
|
229226
|
+
exports.StringDecoder = StringDecoder;
|
|
227467
229227
|
function StringDecoder(encoding) {
|
|
227468
229228
|
this.encoding = normalizeEncoding(encoding);
|
|
227469
229229
|
var nb;
|
|
@@ -229091,7 +230851,7 @@ Tunnel.prototype.setup = function (options) {
|
|
|
229091
230851
|
|
|
229092
230852
|
Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
|
|
229093
230853
|
Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
|
|
229094
|
-
exports
|
|
230854
|
+
exports.Tunnel = Tunnel
|
|
229095
230855
|
|
|
229096
230856
|
|
|
229097
230857
|
/***/ }),
|
|
@@ -230720,7 +232480,7 @@ DailyRotateFile.prototype.query = function (options, callback) {
|
|
|
230720
232480
|
|
|
230721
232481
|
|
|
230722
232482
|
|
|
230723
|
-
var ERR_STREAM_PREMATURE_CLOSE = (__webpack_require__(30113)
|
|
232483
|
+
var ERR_STREAM_PREMATURE_CLOSE = (__webpack_require__(30113).codes).ERR_STREAM_PREMATURE_CLOSE;
|
|
230724
232484
|
function once(callback) {
|
|
230725
232485
|
var called = false;
|
|
230726
232486
|
return function () {
|
|
@@ -244687,8 +246447,8 @@ function registerLogTools(server) {
|
|
|
244687
246447
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
244688
246448
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
244689
246449
|
server.registerTool?.("queryLogs", {
|
|
244690
|
-
title: "
|
|
244691
|
-
description: "日志域统一只读入口。支持检查日志服务状态并搜索 CLS 日志。" +
|
|
246450
|
+
title: "查询 CloudBase 日志服务",
|
|
246451
|
+
description: "CloudBase 日志域统一只读入口。支持检查日志服务状态并搜索 CLS 日志。" +
|
|
244692
246452
|
"\n\n**重要区分**:" +
|
|
244693
246453
|
"\n- 查询云函数日志:使用 `queryFunctions(action=\"listFunctionLogs\", functionName=\"xxx\")`" +
|
|
244694
246454
|
"\n- 查询 CLS 日志(跨服务日志聚合):使用本工具 `queryLogs(action=\"searchLogs\")`" +
|
|
@@ -248647,7 +250407,7 @@ Auth.prototype.onResponse = function (response) {
|
|
|
248647
250407
|
}
|
|
248648
250408
|
}
|
|
248649
250409
|
|
|
248650
|
-
exports.
|
|
250410
|
+
exports.Auth = Auth
|
|
248651
250411
|
|
|
248652
250412
|
|
|
248653
250413
|
/***/ }),
|
|
@@ -249437,7 +251197,7 @@ class Store {
|
|
|
249437
251197
|
}
|
|
249438
251198
|
}
|
|
249439
251199
|
|
|
249440
|
-
exports.
|
|
251200
|
+
exports.Store = Store;
|
|
249441
251201
|
|
|
249442
251202
|
|
|
249443
251203
|
/***/ }),
|
|
@@ -250765,12 +252525,13 @@ async function resolveStoragePublicAccess(params) {
|
|
|
250765
252525
|
function registerStorageTools(server) {
|
|
250766
252526
|
// 获取 cloudBaseOptions,如果没有则为 undefined
|
|
250767
252527
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
252528
|
+
const storageOverrides = server.pluginOptions?.storage;
|
|
250768
252529
|
// 创建闭包函数来获取 CloudBase Manager
|
|
250769
252530
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
250770
252531
|
// Tool 1: queryStorage - 查询存储信息(只读操作)
|
|
250771
252532
|
server.registerTool("queryStorage", {
|
|
250772
252533
|
title: "查询 CloudBase 存储信息",
|
|
250773
|
-
description: "查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain
|
|
252534
|
+
description: "查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 环境:`DescribeEnvs.Storages[]` 列出的 bucket 是旧 NoSQL 后端的,不等于 pgstore bucket。本工具用于查看常规存储;为 PG 浏览器上传准备 bucket 时,请确认目标 bucket 是 pgstore 后端可用的,否则浏览器 `app.storage.from().upload(...)` 会得到 `STORAGE_BUCKET_NOT_FOUND` 并出现 `PUT https://undefined/`。",
|
|
250774
252535
|
inputSchema: queryStorageInputSchema,
|
|
250775
252536
|
annotations: {
|
|
250776
252537
|
readOnlyHint: true,
|
|
@@ -250786,7 +252547,14 @@ function registerStorageTools(server) {
|
|
|
250786
252547
|
const storageService = manager.storage;
|
|
250787
252548
|
switch (input.action) {
|
|
250788
252549
|
case 'list': {
|
|
250789
|
-
|
|
252550
|
+
let files;
|
|
252551
|
+
if (storageOverrides?.listFiles) {
|
|
252552
|
+
files = await storageOverrides.listFiles({ cloudPath: input.cloudPath });
|
|
252553
|
+
}
|
|
252554
|
+
else {
|
|
252555
|
+
const result = await storageService.listDirectoryFiles(input.cloudPath);
|
|
252556
|
+
files = result || [];
|
|
252557
|
+
}
|
|
250790
252558
|
return {
|
|
250791
252559
|
content: [
|
|
250792
252560
|
{
|
|
@@ -250796,17 +252564,23 @@ function registerStorageTools(server) {
|
|
|
250796
252564
|
data: {
|
|
250797
252565
|
action: 'list',
|
|
250798
252566
|
cloudPath: input.cloudPath,
|
|
250799
|
-
files
|
|
250800
|
-
totalCount:
|
|
252567
|
+
files,
|
|
252568
|
+
totalCount: files.length
|
|
250801
252569
|
},
|
|
250802
|
-
message: `Successfully listed ${
|
|
252570
|
+
message: `Successfully listed ${files.length} files in directory '${input.cloudPath}'`
|
|
250803
252571
|
}, null, 2)
|
|
250804
252572
|
}
|
|
250805
252573
|
]
|
|
250806
252574
|
};
|
|
250807
252575
|
}
|
|
250808
252576
|
case 'info': {
|
|
250809
|
-
|
|
252577
|
+
let fileInfo;
|
|
252578
|
+
if (storageOverrides?.getFileInfo) {
|
|
252579
|
+
fileInfo = await storageOverrides.getFileInfo({ cloudPath: input.cloudPath });
|
|
252580
|
+
}
|
|
252581
|
+
else {
|
|
252582
|
+
fileInfo = await storageService.getFileInfo(input.cloudPath);
|
|
252583
|
+
}
|
|
250810
252584
|
return {
|
|
250811
252585
|
content: [
|
|
250812
252586
|
{
|
|
@@ -250816,7 +252590,7 @@ function registerStorageTools(server) {
|
|
|
250816
252590
|
data: {
|
|
250817
252591
|
action: 'info',
|
|
250818
252592
|
cloudPath: input.cloudPath,
|
|
250819
|
-
fileInfo
|
|
252593
|
+
fileInfo
|
|
250820
252594
|
},
|
|
250821
252595
|
message: `Successfully retrieved file info for '${input.cloudPath}'`
|
|
250822
252596
|
}, null, 2)
|
|
@@ -250825,10 +252599,24 @@ function registerStorageTools(server) {
|
|
|
250825
252599
|
};
|
|
250826
252600
|
}
|
|
250827
252601
|
case 'url': {
|
|
250828
|
-
|
|
252602
|
+
let temporaryUrl = "";
|
|
252603
|
+
let fileId = "";
|
|
252604
|
+
if (storageOverrides?.getFileUrl) {
|
|
252605
|
+
const urlResult = await storageOverrides.getFileUrl({
|
|
250829
252606
|
cloudPath: input.cloudPath,
|
|
250830
|
-
maxAge: input.maxAge || 3600
|
|
250831
|
-
}
|
|
252607
|
+
maxAge: input.maxAge || 3600,
|
|
252608
|
+
});
|
|
252609
|
+
temporaryUrl = urlResult.url;
|
|
252610
|
+
fileId = urlResult.fileId || "";
|
|
252611
|
+
}
|
|
252612
|
+
else {
|
|
252613
|
+
const result = await storageService.getTemporaryUrl([{
|
|
252614
|
+
cloudPath: input.cloudPath,
|
|
252615
|
+
maxAge: input.maxAge || 3600
|
|
252616
|
+
}]);
|
|
252617
|
+
temporaryUrl = result[0]?.url || "";
|
|
252618
|
+
fileId = result[0]?.fileId || "";
|
|
252619
|
+
}
|
|
250832
252620
|
const publicAccess = await resolveStoragePublicAccess({
|
|
250833
252621
|
cloudPath: input.cloudPath,
|
|
250834
252622
|
cloudBaseOptions,
|
|
@@ -250843,9 +252631,9 @@ function registerStorageTools(server) {
|
|
|
250843
252631
|
data: {
|
|
250844
252632
|
action: 'url',
|
|
250845
252633
|
cloudPath: input.cloudPath,
|
|
250846
|
-
temporaryUrl
|
|
252634
|
+
temporaryUrl,
|
|
250847
252635
|
expireTime: `${input.maxAge || 3600}秒`,
|
|
250848
|
-
fileId
|
|
252636
|
+
fileId,
|
|
250849
252637
|
storageCdnDomain: publicAccess.storageCdnDomain,
|
|
250850
252638
|
publicUrl: publicAccess.publicUrl,
|
|
250851
252639
|
note: "temporaryUrl 是临时签名链接,会按 expireTime 过期。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
|
|
@@ -250860,10 +252648,15 @@ function registerStorageTools(server) {
|
|
|
250860
252648
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), STORAGE_READ_TEMP_PREFIX));
|
|
250861
252649
|
const localPath = path.join(tempDir, getStorageTempFileName(input.cloudPath));
|
|
250862
252650
|
try {
|
|
250863
|
-
|
|
250864
|
-
cloudPath: input.cloudPath,
|
|
250865
|
-
|
|
250866
|
-
|
|
252651
|
+
if (storageOverrides?.downloadFile) {
|
|
252652
|
+
await storageOverrides.downloadFile({ cloudPath: input.cloudPath, localPath });
|
|
252653
|
+
}
|
|
252654
|
+
else {
|
|
252655
|
+
await storageService.downloadFile({
|
|
252656
|
+
cloudPath: input.cloudPath,
|
|
252657
|
+
localPath
|
|
252658
|
+
});
|
|
252659
|
+
}
|
|
250867
252660
|
const buffer = await fs.readFile(localPath);
|
|
250868
252661
|
const decoded = decodeInlineTextContent(buffer);
|
|
250869
252662
|
return {
|
|
@@ -250899,7 +252692,7 @@ function registerStorageTools(server) {
|
|
|
250899
252692
|
// Tool 2: manageStorage - 管理存储文件(写操作)
|
|
250900
252693
|
server.registerTool("manageStorage", {
|
|
250901
252694
|
title: "管理 CloudBase 存储文件",
|
|
250902
|
-
description: "管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\")
|
|
252695
|
+
description: "管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 桶必须先创建后使用(与 Supabase Storage 一致:upload 前 bucket 必须存在)。浏览器 SDK `app.storage.from().upload(path, file)` 不会自动建桶,且 `path` 的第一段就是 bucket 名(例如 `covers/foo.png` → bucket=`covers`);`from('covers')` 这个参数当前不会被拼到 path 里。如果上传时浏览器看到 `STORAGE_BUCKET_NOT_FOUND` 或 `PUT https://undefined/`(DevTools 表现为 `net::ERR_NAME_NOT_RESOLVED`),先用本工具或控制台确认 / 创建对应的 pgstore bucket,再让前端重试上传,不要让前端把上传失败静默吞掉。`DescribeEnvs.Storages[]` 返回的旧 NoSQL bucket(形如 `<hash>-<envId>-<appId>`)不是可用的 pgstore bucket,切勿当作默认目标使用。",
|
|
250903
252696
|
inputSchema: manageStorageInputSchema,
|
|
250904
252697
|
annotations: {
|
|
250905
252698
|
readOnlyHint: false,
|
|
@@ -250918,32 +252711,69 @@ function registerStorageTools(server) {
|
|
|
250918
252711
|
switch (input.action) {
|
|
250919
252712
|
case 'upload': {
|
|
250920
252713
|
if (input.isDirectory) {
|
|
250921
|
-
|
|
250922
|
-
localPath: input.localPath,
|
|
252714
|
+
if (storageOverrides?.uploadDirectory) {
|
|
252715
|
+
await storageOverrides.uploadDirectory({ localPath: input.localPath, cloudPath: input.cloudPath });
|
|
252716
|
+
}
|
|
252717
|
+
else {
|
|
252718
|
+
await storageService.uploadDirectory({
|
|
252719
|
+
localPath: input.localPath,
|
|
252720
|
+
cloudPath: input.cloudPath,
|
|
252721
|
+
onProgress: (progressData) => {
|
|
252722
|
+
console.log("Upload directory progress:", progressData);
|
|
252723
|
+
}
|
|
252724
|
+
});
|
|
252725
|
+
}
|
|
252726
|
+
}
|
|
252727
|
+
else {
|
|
252728
|
+
if (storageOverrides?.uploadFile) {
|
|
252729
|
+
await storageOverrides.uploadFile({ localPath: input.localPath, cloudPath: input.cloudPath });
|
|
252730
|
+
}
|
|
252731
|
+
else {
|
|
252732
|
+
await storageService.uploadFile({
|
|
252733
|
+
localPath: input.localPath,
|
|
252734
|
+
cloudPath: input.cloudPath,
|
|
252735
|
+
onProgress: (progressData) => {
|
|
252736
|
+
console.log("Upload file progress:", progressData);
|
|
252737
|
+
}
|
|
252738
|
+
});
|
|
252739
|
+
}
|
|
252740
|
+
}
|
|
252741
|
+
// 上传成功后获取临时 URL 和公网访问信息
|
|
252742
|
+
let temporaryUrl = "";
|
|
252743
|
+
let fileId = "";
|
|
252744
|
+
if (storageOverrides?.getFileUrl) {
|
|
252745
|
+
const urlResult = await storageOverrides.getFileUrl({
|
|
250923
252746
|
cloudPath: input.cloudPath,
|
|
250924
|
-
|
|
250925
|
-
console.log("Upload directory progress:", progressData);
|
|
250926
|
-
}
|
|
252747
|
+
maxAge: 3600,
|
|
250927
252748
|
});
|
|
252749
|
+
temporaryUrl = urlResult.url;
|
|
252750
|
+
fileId = urlResult.fileId || "";
|
|
250928
252751
|
}
|
|
250929
252752
|
else {
|
|
250930
|
-
|
|
250931
|
-
|
|
252753
|
+
try {
|
|
252754
|
+
const fileUrls = await storageService.getTemporaryUrl([{
|
|
252755
|
+
cloudPath: input.cloudPath,
|
|
252756
|
+
maxAge: 3600
|
|
252757
|
+
}]);
|
|
252758
|
+
temporaryUrl = fileUrls[0]?.url || "";
|
|
252759
|
+
fileId = fileUrls[0]?.fileId || "";
|
|
252760
|
+
}
|
|
252761
|
+
catch {
|
|
252762
|
+
// StorageOverrides 下 getTemporaryUrl 可能不可用
|
|
252763
|
+
}
|
|
252764
|
+
}
|
|
252765
|
+
let publicAccess = {};
|
|
252766
|
+
try {
|
|
252767
|
+
publicAccess = await resolveStoragePublicAccess({
|
|
250932
252768
|
cloudPath: input.cloudPath,
|
|
250933
|
-
|
|
250934
|
-
|
|
250935
|
-
}
|
|
252769
|
+
cloudBaseOptions,
|
|
252770
|
+
manager,
|
|
250936
252771
|
});
|
|
250937
252772
|
}
|
|
250938
|
-
|
|
250939
|
-
|
|
250940
|
-
|
|
250941
|
-
|
|
250942
|
-
const publicAccess = await resolveStoragePublicAccess({
|
|
250943
|
-
cloudPath: input.cloudPath,
|
|
250944
|
-
cloudBaseOptions,
|
|
250945
|
-
manager,
|
|
250946
|
-
});
|
|
252773
|
+
catch {
|
|
252774
|
+
// DescribeEnvs 可能不支持(IDE 模式下会绕过)
|
|
252775
|
+
publicAccess = {};
|
|
252776
|
+
}
|
|
250947
252777
|
return {
|
|
250948
252778
|
content: [
|
|
250949
252779
|
{
|
|
@@ -250955,7 +252785,7 @@ function registerStorageTools(server) {
|
|
|
250955
252785
|
localPath: input.localPath,
|
|
250956
252786
|
cloudPath: input.cloudPath,
|
|
250957
252787
|
isDirectory: input.isDirectory,
|
|
250958
|
-
temporaryUrl
|
|
252788
|
+
temporaryUrl,
|
|
250959
252789
|
expireTime: "1小时",
|
|
250960
252790
|
storageCdnDomain: publicAccess.storageCdnDomain,
|
|
250961
252791
|
publicUrl: publicAccess.publicUrl,
|
|
@@ -250969,16 +252799,26 @@ function registerStorageTools(server) {
|
|
|
250969
252799
|
}
|
|
250970
252800
|
case 'download': {
|
|
250971
252801
|
if (input.isDirectory) {
|
|
250972
|
-
|
|
250973
|
-
cloudPath: input.cloudPath,
|
|
250974
|
-
|
|
250975
|
-
|
|
252802
|
+
if (storageOverrides?.downloadDirectory) {
|
|
252803
|
+
await storageOverrides.downloadDirectory({ cloudPath: input.cloudPath, localPath: input.localPath });
|
|
252804
|
+
}
|
|
252805
|
+
else {
|
|
252806
|
+
await storageService.downloadDirectory({
|
|
252807
|
+
cloudPath: input.cloudPath,
|
|
252808
|
+
localPath: input.localPath
|
|
252809
|
+
});
|
|
252810
|
+
}
|
|
250976
252811
|
}
|
|
250977
252812
|
else {
|
|
250978
|
-
|
|
250979
|
-
cloudPath: input.cloudPath,
|
|
250980
|
-
|
|
250981
|
-
|
|
252813
|
+
if (storageOverrides?.downloadFile) {
|
|
252814
|
+
await storageOverrides.downloadFile({ cloudPath: input.cloudPath, localPath: input.localPath });
|
|
252815
|
+
}
|
|
252816
|
+
else {
|
|
252817
|
+
await storageService.downloadFile({
|
|
252818
|
+
cloudPath: input.cloudPath,
|
|
252819
|
+
localPath: input.localPath
|
|
252820
|
+
});
|
|
252821
|
+
}
|
|
250982
252822
|
}
|
|
250983
252823
|
return {
|
|
250984
252824
|
content: [
|
|
@@ -251014,10 +252854,20 @@ function registerStorageTools(server) {
|
|
|
251014
252854
|
};
|
|
251015
252855
|
}
|
|
251016
252856
|
if (input.isDirectory) {
|
|
251017
|
-
|
|
252857
|
+
if (storageOverrides?.deleteDirectory) {
|
|
252858
|
+
await storageOverrides.deleteDirectory({ cloudPath: input.cloudPath });
|
|
252859
|
+
}
|
|
252860
|
+
else {
|
|
252861
|
+
await storageService.deleteDirectory(input.cloudPath);
|
|
252862
|
+
}
|
|
251018
252863
|
}
|
|
251019
252864
|
else {
|
|
251020
|
-
|
|
252865
|
+
if (storageOverrides?.deleteFiles) {
|
|
252866
|
+
await storageOverrides.deleteFiles({ cloudPaths: [input.cloudPath] });
|
|
252867
|
+
}
|
|
252868
|
+
else {
|
|
252869
|
+
await storageService.deleteFile([input.cloudPath]);
|
|
252870
|
+
}
|
|
251021
252871
|
}
|
|
251022
252872
|
return {
|
|
251023
252873
|
content: [
|
|
@@ -259302,9 +261152,9 @@ class CloudBase {
|
|
|
259302
261152
|
}
|
|
259303
261153
|
constructor(config = {}) {
|
|
259304
261154
|
this.cloudBaseConfig = {};
|
|
259305
|
-
let { secretId, secretKey, token, envId, proxy, region, envType, useInternalEndpoint, internalEndpointRegion } = config;
|
|
259306
|
-
// config 中传入的 secretId secretkey
|
|
259307
|
-
if ((secretId && !secretKey) || (!secretId && secretKey)) {
|
|
261155
|
+
let { secretId, secretKey, token, envId, proxy, region, envType, useInternalEndpoint, internalEndpointRegion, requestFn } = config;
|
|
261156
|
+
// config 中传入的 secretId secretkey 必须同时存在(有 requestFn 时可以不传)
|
|
261157
|
+
if (!requestFn && ((secretId && !secretKey) || (!secretId && secretKey))) {
|
|
259308
261158
|
throw new Error('secretId and secretKey must be a pair');
|
|
259309
261159
|
}
|
|
259310
261160
|
this.cloudBaseConfig = {
|
|
@@ -259317,6 +261167,7 @@ class CloudBase {
|
|
|
259317
261167
|
region,
|
|
259318
261168
|
useInternalEndpoint,
|
|
259319
261169
|
internalEndpointRegion,
|
|
261170
|
+
requestFn,
|
|
259320
261171
|
};
|
|
259321
261172
|
// 初始化 context
|
|
259322
261173
|
this.context = new context_1.CloudBaseContext(this.cloudBaseConfig);
|
|
@@ -260344,7 +262195,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
260344
262195
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
260345
262196
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
260346
262197
|
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
260347
|
-
var ERR_INVALID_ARG_TYPE = (__webpack_require__(30113)
|
|
262198
|
+
var ERR_INVALID_ARG_TYPE = (__webpack_require__(30113).codes).ERR_INVALID_ARG_TYPE;
|
|
260348
262199
|
function from(Readable, iterable, opts) {
|
|
260349
262200
|
var iterator;
|
|
260350
262201
|
if (iterable && typeof iterable.next === 'function') {
|
|
@@ -265806,7 +267657,7 @@ function normalizeEncoding(enc) {
|
|
|
265806
267657
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
265807
267658
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
265808
267659
|
// characters.
|
|
265809
|
-
exports.
|
|
267660
|
+
exports.StringDecoder = StringDecoder;
|
|
265810
267661
|
function StringDecoder(encoding) {
|
|
265811
267662
|
this.encoding = normalizeEncoding(encoding);
|
|
265812
267663
|
var nb;
|