@cloudbase/cloudbase-mcp 2.16.1-beta.1 → 2.17.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +1471 -288
- package/dist/index.js +1316 -273
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1865,6 +1865,40 @@ function buildEnvCandidatePayload(envCandidates, limit = DEFAULT_ENV_CANDIDATE_L
|
|
|
1865
1865
|
},
|
|
1866
1866
|
};
|
|
1867
1867
|
}
|
|
1868
|
+
function buildLocalDevDomainHint() {
|
|
1869
|
+
return {
|
|
1870
|
+
format: "host:port",
|
|
1871
|
+
useActualOrigin: true,
|
|
1872
|
+
requiredValue: "当前浏览器实际访问 origin 对应的 host:port",
|
|
1873
|
+
deriveFrom: ["浏览器地址栏中的当前 origin", "本地 dev server 实际启动输出"],
|
|
1874
|
+
note: "如果你的前端运行在自定义域名或本地开发端口上,请把当前浏览器实际访问地址对应的 host:port 加入安全域名。不要依赖一组固定默认端口,也不要假设已有 localhost/127.0.0.1 条目已经覆盖当前运行端口。",
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
function summarizeConfiguredLocalDevEntries(domains) {
|
|
1878
|
+
const localEntries = domains
|
|
1879
|
+
.map((domain) => String(domain?.Domain ?? "").trim())
|
|
1880
|
+
.filter((domain) => domain.startsWith("127.0.0.1:") || domain.startsWith("localhost:"));
|
|
1881
|
+
return {
|
|
1882
|
+
hasAnyConfiguredLocalEntry: localEntries.length > 0,
|
|
1883
|
+
configuredEntries: localEntries,
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
function simplifyEnvDomains(domains) {
|
|
1887
|
+
if (!Array.isArray(domains)) {
|
|
1888
|
+
return domains;
|
|
1889
|
+
}
|
|
1890
|
+
return domains.map((domain) => {
|
|
1891
|
+
if (!domain || typeof domain !== "object") {
|
|
1892
|
+
return domain;
|
|
1893
|
+
}
|
|
1894
|
+
const source = domain;
|
|
1895
|
+
return {
|
|
1896
|
+
...(source.Domain !== undefined ? { Domain: source.Domain } : {}),
|
|
1897
|
+
...(source.Status !== undefined ? { Status: source.Status } : {}),
|
|
1898
|
+
...(source.Type !== undefined ? { Type: source.Type } : {}),
|
|
1899
|
+
};
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1868
1902
|
function formatDeviceAuthHint(deviceAuthInfo) {
|
|
1869
1903
|
if (!deviceAuthInfo) {
|
|
1870
1904
|
return "";
|
|
@@ -2807,6 +2841,35 @@ function registerEnvTools(server) {
|
|
|
2807
2841
|
const cloudbaseDomains = await getManager();
|
|
2808
2842
|
result = await cloudbaseDomains.env.getEnvAuthDomains();
|
|
2809
2843
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
2844
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
2845
|
+
const domainsResult = result;
|
|
2846
|
+
const localDevHint = buildLocalDevDomainHint();
|
|
2847
|
+
const simplifiedDomains = simplifyEnvDomains(domainsResult.Domains);
|
|
2848
|
+
const localDevSummary = summarizeConfiguredLocalDevEntries(Array.isArray(simplifiedDomains)
|
|
2849
|
+
? simplifiedDomains
|
|
2850
|
+
: []);
|
|
2851
|
+
result = {
|
|
2852
|
+
...domainsResult,
|
|
2853
|
+
Domains: simplifiedDomains,
|
|
2854
|
+
localDevHint,
|
|
2855
|
+
localDevStatus: {
|
|
2856
|
+
requiresExactCurrentOrigin: true,
|
|
2857
|
+
browserUploadReady: false,
|
|
2858
|
+
coverageConfirmed: false,
|
|
2859
|
+
doNotAssumeConfiguredEntriesAreSufficient: true,
|
|
2860
|
+
canAutoDetermineCurrentOrigin: false,
|
|
2861
|
+
hasAnyConfiguredLocalEntry: localDevSummary.hasAnyConfiguredLocalEntry,
|
|
2862
|
+
configuredEntries: localDevSummary.configuredEntries,
|
|
2863
|
+
note: "此查询不会自动知道你当前浏览器实际使用的自定义域名或本地端口。即使已经存在一些 localhost/127.0.0.1 条目,也不能据此认定浏览器上传已就绪。若浏览器 Web 应用需要直接上传文件到 CloudBase,请先确认并添加当前访问地址对应的 host:port,再依赖 app.uploadFile()。",
|
|
2864
|
+
},
|
|
2865
|
+
next_step_template: {
|
|
2866
|
+
tool: "envDomainManagement",
|
|
2867
|
+
action: "create",
|
|
2868
|
+
domains: ["<actual-browser-host>:<actual-browser-port>"],
|
|
2869
|
+
note: "请把占位符替换为当前浏览器实际访问 origin 对应的 host:port,再执行添加。",
|
|
2870
|
+
},
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2810
2873
|
break;
|
|
2811
2874
|
case "hosting": {
|
|
2812
2875
|
const cloudbaseHosting = await getManager();
|
|
@@ -2878,7 +2941,7 @@ function registerEnvTools(server) {
|
|
|
2878
2941
|
// envDomainManagement - 环境域名管理(合并 createEnvDomain + deleteEnvDomain)
|
|
2879
2942
|
server.registerTool?.("envDomainManagement", {
|
|
2880
2943
|
title: "环境域名管理",
|
|
2881
|
-
description: "管理云开发环境的安全域名,支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI
|
|
2944
|
+
description: "管理云开发环境的安全域名,支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 或自定义域名直接访问 CloudBase 资源时,应先用 envQuery(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。",
|
|
2882
2945
|
inputSchema: {
|
|
2883
2946
|
action: zod_1.z
|
|
2884
2947
|
.enum(["create", "delete"])
|
|
@@ -24471,6 +24534,9 @@ exports.listAvailableEnvCandidates = listAvailableEnvCandidates;
|
|
|
24471
24534
|
exports.getEnvId = getEnvId;
|
|
24472
24535
|
exports.resetCloudBaseManagerCache = resetCloudBaseManagerCache;
|
|
24473
24536
|
exports.getCachedEnvId = getCachedEnvId;
|
|
24537
|
+
exports.resetDatabaseInstanceIdCache = resetDatabaseInstanceIdCache;
|
|
24538
|
+
exports.invalidateDatabaseInstanceIdCache = invalidateDatabaseInstanceIdCache;
|
|
24539
|
+
exports.getDatabaseInstanceId = getDatabaseInstanceId;
|
|
24474
24540
|
exports.getCloudBaseManager = getCloudBaseManager;
|
|
24475
24541
|
exports.createCloudBaseManagerWithOptions = createCloudBaseManagerWithOptions;
|
|
24476
24542
|
exports.extractRequestId = extractRequestId;
|
|
@@ -24503,7 +24569,7 @@ function createManagerFromLoginState(loginState, region) {
|
|
|
24503
24569
|
envId: loginState.envId,
|
|
24504
24570
|
token: loginState.token,
|
|
24505
24571
|
proxy: process.env.http_proxy,
|
|
24506
|
-
region,
|
|
24572
|
+
region: region ?? process.env.TCB_REGION ?? 'ap-shanghai',
|
|
24507
24573
|
});
|
|
24508
24574
|
}
|
|
24509
24575
|
async function listAvailableEnvCandidates(options) {
|
|
@@ -24522,7 +24588,7 @@ async function listAvailableEnvCandidates(options) {
|
|
|
24522
24588
|
if (!loginState?.secretId || !loginState?.secretKey) {
|
|
24523
24589
|
return [];
|
|
24524
24590
|
}
|
|
24525
|
-
const region = cloudBaseOptions?.region ?? process.env.TCB_REGION ??
|
|
24591
|
+
const region = cloudBaseOptions?.region ?? process.env.TCB_REGION ?? 'ap-shanghai';
|
|
24526
24592
|
cloudbase = createManagerFromLoginState(loginState, region);
|
|
24527
24593
|
}
|
|
24528
24594
|
try {
|
|
@@ -24726,6 +24792,74 @@ function resetCloudBaseManagerCache() {
|
|
|
24726
24792
|
function getCachedEnvId() {
|
|
24727
24793
|
return envManager.getCachedEnvId();
|
|
24728
24794
|
}
|
|
24795
|
+
const databaseInstanceIdCache = new Map();
|
|
24796
|
+
function buildDatabaseInstanceIdCacheKey(options) {
|
|
24797
|
+
const envId = options?.envId ?? options?.cloudBaseOptions?.envId ?? process.env.CLOUDBASE_ENV_ID ?? 'unknown';
|
|
24798
|
+
const region = options?.region ?? options?.cloudBaseOptions?.region ?? process.env.TCB_REGION ?? 'ap-shanghai';
|
|
24799
|
+
return `${region}:${envId}`;
|
|
24800
|
+
}
|
|
24801
|
+
function resetDatabaseInstanceIdCache() {
|
|
24802
|
+
databaseInstanceIdCache.clear();
|
|
24803
|
+
}
|
|
24804
|
+
function invalidateDatabaseInstanceIdCache(options) {
|
|
24805
|
+
const cacheKey = options?.cacheKey ?? buildDatabaseInstanceIdCacheKey(options);
|
|
24806
|
+
databaseInstanceIdCache.delete(cacheKey);
|
|
24807
|
+
}
|
|
24808
|
+
async function getDatabaseInstanceId(options) {
|
|
24809
|
+
if (options?.instanceId) {
|
|
24810
|
+
return {
|
|
24811
|
+
instanceId: options.instanceId,
|
|
24812
|
+
source: 'input',
|
|
24813
|
+
};
|
|
24814
|
+
}
|
|
24815
|
+
const envId = await getEnvId(options?.cloudBaseOptions);
|
|
24816
|
+
const cacheKey = buildDatabaseInstanceIdCacheKey({
|
|
24817
|
+
envId,
|
|
24818
|
+
cloudBaseOptions: options?.cloudBaseOptions,
|
|
24819
|
+
});
|
|
24820
|
+
const cachedEntry = databaseInstanceIdCache.get(cacheKey);
|
|
24821
|
+
if (cachedEntry?.instanceId) {
|
|
24822
|
+
return {
|
|
24823
|
+
instanceId: cachedEntry.instanceId,
|
|
24824
|
+
source: 'cache',
|
|
24825
|
+
cacheKey,
|
|
24826
|
+
};
|
|
24827
|
+
}
|
|
24828
|
+
if (cachedEntry?.inflightPromise) {
|
|
24829
|
+
const instanceId = await cachedEntry.inflightPromise;
|
|
24830
|
+
return {
|
|
24831
|
+
instanceId,
|
|
24832
|
+
source: 'cache',
|
|
24833
|
+
cacheKey,
|
|
24834
|
+
};
|
|
24835
|
+
}
|
|
24836
|
+
const inflightPromise = (async () => {
|
|
24837
|
+
const cloudbase = options?.cloudbase ?? (await getCloudBaseManager({ cloudBaseOptions: options?.cloudBaseOptions }));
|
|
24838
|
+
const { EnvInfo } = await cloudbase.env.getEnvInfo();
|
|
24839
|
+
if (!EnvInfo?.Databases?.[0]?.InstanceId) {
|
|
24840
|
+
throw new Error("无法获取数据库实例ID");
|
|
24841
|
+
}
|
|
24842
|
+
return EnvInfo.Databases[0].InstanceId;
|
|
24843
|
+
})();
|
|
24844
|
+
databaseInstanceIdCache.set(cacheKey, {
|
|
24845
|
+
inflightPromise,
|
|
24846
|
+
});
|
|
24847
|
+
try {
|
|
24848
|
+
const instanceId = await inflightPromise;
|
|
24849
|
+
databaseInstanceIdCache.set(cacheKey, {
|
|
24850
|
+
instanceId,
|
|
24851
|
+
});
|
|
24852
|
+
return {
|
|
24853
|
+
instanceId,
|
|
24854
|
+
source: 'envInfo',
|
|
24855
|
+
cacheKey,
|
|
24856
|
+
};
|
|
24857
|
+
}
|
|
24858
|
+
catch (error) {
|
|
24859
|
+
databaseInstanceIdCache.delete(cacheKey);
|
|
24860
|
+
throw error;
|
|
24861
|
+
}
|
|
24862
|
+
}
|
|
24729
24863
|
/**
|
|
24730
24864
|
* 每次都实时获取最新的 token/secretId/secretKey
|
|
24731
24865
|
*/
|
|
@@ -24766,8 +24900,8 @@ async function getCloudBaseManager(options = {}) {
|
|
|
24766
24900
|
});
|
|
24767
24901
|
}
|
|
24768
24902
|
try {
|
|
24769
|
-
//
|
|
24770
|
-
const fallbackRegion = cloudBaseOptions?.region ?? process.env.TCB_REGION;
|
|
24903
|
+
// Region priority: explicit option > env var > ap-shanghai default
|
|
24904
|
+
const fallbackRegion = cloudBaseOptions?.region ?? process.env.TCB_REGION ?? 'ap-shanghai';
|
|
24771
24905
|
const loginState = authStrategy === 'ensure'
|
|
24772
24906
|
? await (0, auth_js_1.getLoginState)({ region: fallbackRegion })
|
|
24773
24907
|
: await (0, auth_js_1.peekLoginState)();
|
|
@@ -24815,24 +24949,19 @@ async function getCloudBaseManager(options = {}) {
|
|
|
24815
24949
|
}
|
|
24816
24950
|
}
|
|
24817
24951
|
}
|
|
24818
|
-
// envId priority:
|
|
24819
|
-
// Note: envManager.cachedEnvId has highest priority as it reflects user's latest environment switch
|
|
24820
|
-
// Region priority: envCandidates[].region (actual env region) > process.env.TCB_REGION > undefined
|
|
24821
|
-
// Resolving the environment's own region avoids INVALID_REGION errors when TCB_REGION
|
|
24822
|
-
// is set to a different region than the target environment.
|
|
24952
|
+
// envId priority: explicit option > envManager cache > loginState.envId
|
|
24823
24953
|
const resolvedEnvId = finalEnvId || loginEnvId;
|
|
24824
24954
|
let region = fallbackRegion;
|
|
24825
24955
|
if (resolvedEnvId && !cloudBaseOptions?.region) {
|
|
24826
24956
|
try {
|
|
24827
24957
|
const envCandidates = await listAvailableEnvCandidates({ loginState });
|
|
24828
|
-
const matchedEnv = envCandidates.find(
|
|
24958
|
+
const matchedEnv = envCandidates.find((candidate) => candidate.envId === resolvedEnvId);
|
|
24829
24959
|
if (matchedEnv?.region) {
|
|
24830
24960
|
region = matchedEnv.region;
|
|
24831
24961
|
(0, logger_js_1.debug)('使用环境实际 region:', { envId: resolvedEnvId, region });
|
|
24832
24962
|
}
|
|
24833
24963
|
}
|
|
24834
24964
|
catch {
|
|
24835
|
-
// If env listing fails, fall back to TCB_REGION / undefined
|
|
24836
24965
|
(0, logger_js_1.debug)('无法获取环境列表,使用 fallback region');
|
|
24837
24966
|
}
|
|
24838
24967
|
}
|
|
@@ -24865,8 +24994,8 @@ function createCloudBaseManagerWithOptions(cloudBaseOptions) {
|
|
|
24865
24994
|
hasSecretKey: !!cloudBaseOptions.secretKey,
|
|
24866
24995
|
hasToken: !!cloudBaseOptions.token,
|
|
24867
24996
|
});
|
|
24868
|
-
// Region priority:
|
|
24869
|
-
const region = cloudBaseOptions.region ?? process.env.TCB_REGION ??
|
|
24997
|
+
// Region priority: explicit option > env var > ap-shanghai default
|
|
24998
|
+
const region = cloudBaseOptions.region ?? process.env.TCB_REGION ?? 'ap-shanghai';
|
|
24870
24999
|
const manager = new manager_node_1.default({
|
|
24871
25000
|
...cloudBaseOptions,
|
|
24872
25001
|
proxy: cloudBaseOptions.proxy || process.env.http_proxy,
|
|
@@ -28034,28 +28163,36 @@ exports.registerAppAuthTools = registerAppAuthTools;
|
|
|
28034
28163
|
const zod_1 = __webpack_require__(21614);
|
|
28035
28164
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
28036
28165
|
const json_content_js_1 = __webpack_require__(4365);
|
|
28166
|
+
const tool_result_js_1 = __webpack_require__(9835);
|
|
28037
28167
|
const QUERY_APP_AUTH_ACTIONS = [
|
|
28038
28168
|
"getLoginConfig",
|
|
28039
28169
|
"listProviders",
|
|
28040
28170
|
"getProvider",
|
|
28041
28171
|
"getClientConfig",
|
|
28042
|
-
"
|
|
28172
|
+
"getPublishableKey",
|
|
28043
28173
|
"getStaticDomain",
|
|
28174
|
+
"listApiKeys",
|
|
28044
28175
|
];
|
|
28045
28176
|
const MANAGE_APP_AUTH_ACTIONS = [
|
|
28046
|
-
"
|
|
28177
|
+
"patchLoginStrategy",
|
|
28178
|
+
"addProvider",
|
|
28047
28179
|
"updateProvider",
|
|
28180
|
+
"deleteProvider",
|
|
28048
28181
|
"updateClientConfig",
|
|
28049
|
-
"
|
|
28182
|
+
"ensurePublishableKey",
|
|
28183
|
+
"createApiKey",
|
|
28184
|
+
"deleteApiKey",
|
|
28050
28185
|
"createCustomLoginKeys",
|
|
28051
28186
|
];
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
|
|
28057
|
-
}
|
|
28058
|
-
}
|
|
28187
|
+
const APP_AUTH_KEY_TYPES = ["publish_key", "api_key"];
|
|
28188
|
+
const SUPABASE_LIKE_SDK_HINTS = {
|
|
28189
|
+
phoneOtp: "auth.signInWithOtp({ phone })",
|
|
28190
|
+
emailOtp: "auth.signInWithOtp({ email })",
|
|
28191
|
+
password: "auth.signInWithPassword({ username|email|phone, password })",
|
|
28192
|
+
signup: "auth.signUp({ phone|email, ... })",
|
|
28193
|
+
verifyOtp: "verifyOtp({ token })",
|
|
28194
|
+
anonymous: "auth.signInAnonymously()",
|
|
28195
|
+
};
|
|
28059
28196
|
function buildErrorEnvelope(error) {
|
|
28060
28197
|
return {
|
|
28061
28198
|
success: false,
|
|
@@ -28063,6 +28200,19 @@ function buildErrorEnvelope(error) {
|
|
|
28063
28200
|
message: error instanceof Error ? error.message : String(error),
|
|
28064
28201
|
};
|
|
28065
28202
|
}
|
|
28203
|
+
function buildShortError(error) {
|
|
28204
|
+
return {
|
|
28205
|
+
success: false,
|
|
28206
|
+
error,
|
|
28207
|
+
};
|
|
28208
|
+
}
|
|
28209
|
+
function buildShortErrorWithCode(error, code) {
|
|
28210
|
+
return {
|
|
28211
|
+
success: false,
|
|
28212
|
+
error,
|
|
28213
|
+
code,
|
|
28214
|
+
};
|
|
28215
|
+
}
|
|
28066
28216
|
function normalizePlainObject(value, label) {
|
|
28067
28217
|
if (value === undefined) {
|
|
28068
28218
|
return undefined;
|
|
@@ -28072,6 +28222,25 @@ function normalizePlainObject(value, label) {
|
|
|
28072
28222
|
}
|
|
28073
28223
|
return value;
|
|
28074
28224
|
}
|
|
28225
|
+
function normalizeLocalizedMessage(value, label, fallback) {
|
|
28226
|
+
if (typeof value === "string") {
|
|
28227
|
+
return { Message: value };
|
|
28228
|
+
}
|
|
28229
|
+
const normalized = normalizePlainObject(value, label);
|
|
28230
|
+
if (normalized) {
|
|
28231
|
+
return normalized;
|
|
28232
|
+
}
|
|
28233
|
+
if (fallback) {
|
|
28234
|
+
return { Message: fallback };
|
|
28235
|
+
}
|
|
28236
|
+
throw new Error(`${label} 必须是字符串或对象`);
|
|
28237
|
+
}
|
|
28238
|
+
function omitKeys(value, keys) {
|
|
28239
|
+
if (!value) {
|
|
28240
|
+
return undefined;
|
|
28241
|
+
}
|
|
28242
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !keys.includes(key)));
|
|
28243
|
+
}
|
|
28075
28244
|
function extractLoginStrategyState(value) {
|
|
28076
28245
|
const source = normalizePlainObject(value, "loginStrategy") ?? {};
|
|
28077
28246
|
const data = normalizePlainObject(source.Data, "loginStrategy.Data") ?? source;
|
|
@@ -28087,13 +28256,154 @@ function extractLoginStrategyState(value) {
|
|
|
28087
28256
|
};
|
|
28088
28257
|
}
|
|
28089
28258
|
function normalizeLoginConfigPatch(value) {
|
|
28090
|
-
const { PhoneLogin, UsernameLogin, ...rest } = value;
|
|
28259
|
+
const { PhoneLogin, UsernameLogin, usernamePassword, email, anonymous, phone, ...rest } = value;
|
|
28091
28260
|
return {
|
|
28092
28261
|
...rest,
|
|
28093
28262
|
...(typeof PhoneLogin === "boolean" ? { PhoneNumberLogin: PhoneLogin } : {}),
|
|
28094
28263
|
...(typeof UsernameLogin === "boolean" ? { UserNameLogin: UsernameLogin } : {}),
|
|
28264
|
+
...(typeof usernamePassword === "boolean" ? { UserNameLogin: usernamePassword } : {}),
|
|
28265
|
+
...(typeof email === "boolean" ? { EmailLogin: email } : {}),
|
|
28266
|
+
...(typeof anonymous === "boolean" ? { AnonymousLogin: anonymous } : {}),
|
|
28267
|
+
...(typeof phone === "boolean" ? { PhoneNumberLogin: phone } : {}),
|
|
28268
|
+
};
|
|
28269
|
+
}
|
|
28270
|
+
function buildLoginMethods(value) {
|
|
28271
|
+
const state = extractLoginStrategyState(value);
|
|
28272
|
+
return {
|
|
28273
|
+
usernamePassword: Boolean(state.UserNameLogin),
|
|
28274
|
+
email: Boolean(state.EmailLogin),
|
|
28275
|
+
anonymous: Boolean(state.AnonymousLogin),
|
|
28276
|
+
phone: Boolean(state.PhoneNumberLogin),
|
|
28277
|
+
};
|
|
28278
|
+
}
|
|
28279
|
+
function buildWebSdkHint(loginMethods) {
|
|
28280
|
+
if (!loginMethods.usernamePassword) {
|
|
28281
|
+
return {
|
|
28282
|
+
blocked: true,
|
|
28283
|
+
reason: "plain username-style identifiers require usernamePassword auth",
|
|
28284
|
+
nextStep: 'manageAppAuth({ action: "patchLoginStrategy", patch: { usernamePassword: true } })',
|
|
28285
|
+
accountInputType: "text",
|
|
28286
|
+
avoidEmailHelpers: true,
|
|
28287
|
+
};
|
|
28288
|
+
}
|
|
28289
|
+
return {
|
|
28290
|
+
blocked: false,
|
|
28291
|
+
register: "auth.signUp({ username, password })",
|
|
28292
|
+
login: "auth.signInWithPassword({ username, password })",
|
|
28293
|
+
accountInputType: "text",
|
|
28294
|
+
avoidEmailHelpers: true,
|
|
28295
|
+
};
|
|
28296
|
+
}
|
|
28297
|
+
function buildLoginConfigNextStep(loginMethods) {
|
|
28298
|
+
if (loginMethods.usernamePassword) {
|
|
28299
|
+
return undefined;
|
|
28300
|
+
}
|
|
28301
|
+
return {
|
|
28302
|
+
tool: "manageAppAuth",
|
|
28303
|
+
action: "patchLoginStrategy",
|
|
28304
|
+
patch: {
|
|
28305
|
+
usernamePassword: true,
|
|
28306
|
+
},
|
|
28307
|
+
};
|
|
28308
|
+
}
|
|
28309
|
+
function extractProviders(value) {
|
|
28310
|
+
const payload = normalizePlainObject(value, "providersResult");
|
|
28311
|
+
const providers = payload?.Providers ?? payload?.ProviderList ?? payload?.Data ?? [];
|
|
28312
|
+
return Array.isArray(providers) ? providers : [];
|
|
28313
|
+
}
|
|
28314
|
+
function extractStaticStores(value) {
|
|
28315
|
+
const payload = normalizePlainObject(value, "staticStoreResult");
|
|
28316
|
+
const stores = payload?.Data ?? [];
|
|
28317
|
+
return Array.isArray(stores) ? stores : [];
|
|
28318
|
+
}
|
|
28319
|
+
function getNonEmptyString(value) {
|
|
28320
|
+
if (typeof value !== "string") {
|
|
28321
|
+
return undefined;
|
|
28322
|
+
}
|
|
28323
|
+
const trimmed = value.trim();
|
|
28324
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
28325
|
+
}
|
|
28326
|
+
function resolveStaticStoreDomain(store) {
|
|
28327
|
+
return getNonEmptyString(store?.CdnDomain) ?? getNonEmptyString(store?.StaticDomain) ?? null;
|
|
28328
|
+
}
|
|
28329
|
+
function extractApiKeyList(value) {
|
|
28330
|
+
const payload = normalizePlainObject(value, "apiKeyResult");
|
|
28331
|
+
const items = payload?.ApiKeyList ?? payload?.Data ?? [];
|
|
28332
|
+
return Array.isArray(items) ? items : [];
|
|
28333
|
+
}
|
|
28334
|
+
function findPublishableKey(value) {
|
|
28335
|
+
const apiKeys = extractApiKeyList(value);
|
|
28336
|
+
return (apiKeys.find((item) => item.Name === "publish_key" ||
|
|
28337
|
+
item.KeyName === "publish_key" ||
|
|
28338
|
+
item.KeyType === "publish_key") ?? null);
|
|
28339
|
+
}
|
|
28340
|
+
function buildPublishableKeyResponse(envId, record, options) {
|
|
28341
|
+
return {
|
|
28342
|
+
success: true,
|
|
28343
|
+
envId,
|
|
28344
|
+
sdkStyle: "supabase-like",
|
|
28345
|
+
sdkHints: SUPABASE_LIKE_SDK_HINTS,
|
|
28346
|
+
publishableKey: typeof record?.ApiKey === "string" ? record.ApiKey : null,
|
|
28347
|
+
keyId: typeof record?.KeyId === "string" ? record.KeyId : null,
|
|
28348
|
+
keyName: typeof record?.Name === "string"
|
|
28349
|
+
? record.Name
|
|
28350
|
+
: typeof record?.KeyName === "string"
|
|
28351
|
+
? record.KeyName
|
|
28352
|
+
: record
|
|
28353
|
+
? "publish_key"
|
|
28354
|
+
: null,
|
|
28355
|
+
expireAt: typeof record?.ExpireAt === "string" ? record.ExpireAt : null,
|
|
28356
|
+
createdAt: typeof record?.CreateAt === "string" ? record.CreateAt : null,
|
|
28357
|
+
...(typeof options?.created === "boolean" ? { created: options.created } : {}),
|
|
28095
28358
|
};
|
|
28096
28359
|
}
|
|
28360
|
+
function buildSupabaseLikeAuthResponse(payload) {
|
|
28361
|
+
return {
|
|
28362
|
+
...payload,
|
|
28363
|
+
sdkStyle: "supabase-like",
|
|
28364
|
+
sdkHints: SUPABASE_LIKE_SDK_HINTS,
|
|
28365
|
+
};
|
|
28366
|
+
}
|
|
28367
|
+
function buildClientConfigResponse(envId, clientId, clientConfig) {
|
|
28368
|
+
return {
|
|
28369
|
+
success: true,
|
|
28370
|
+
envId,
|
|
28371
|
+
clientId,
|
|
28372
|
+
clientConfig,
|
|
28373
|
+
};
|
|
28374
|
+
}
|
|
28375
|
+
async function getActiveEnvId(cloudBaseOptions) {
|
|
28376
|
+
try {
|
|
28377
|
+
return await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
28378
|
+
}
|
|
28379
|
+
catch (error) {
|
|
28380
|
+
const payload = (0, tool_result_js_1.isToolPayloadError)(error)
|
|
28381
|
+
? error.payload
|
|
28382
|
+
: normalizePlainObject(error?.payload, "error.payload");
|
|
28383
|
+
if (payload?.code === "ENV_REQUIRED") {
|
|
28384
|
+
const nextError = new Error("no active environment selected");
|
|
28385
|
+
nextError.code = "ENV_REQUIRED";
|
|
28386
|
+
throw nextError;
|
|
28387
|
+
}
|
|
28388
|
+
if (payload?.code === "AUTH_REQUIRED") {
|
|
28389
|
+
const nextError = new Error("authentication required");
|
|
28390
|
+
nextError.code = "AUTH_REQUIRED";
|
|
28391
|
+
throw nextError;
|
|
28392
|
+
}
|
|
28393
|
+
throw error;
|
|
28394
|
+
}
|
|
28395
|
+
}
|
|
28396
|
+
function isResourceInUseError(error) {
|
|
28397
|
+
if (!error || typeof error !== "object") {
|
|
28398
|
+
return false;
|
|
28399
|
+
}
|
|
28400
|
+
const candidate = error;
|
|
28401
|
+
const code = typeof candidate.code === "string" ? candidate.code : "";
|
|
28402
|
+
const name = typeof candidate.name === "string" ? candidate.name : "";
|
|
28403
|
+
const message = typeof candidate.message === "string" ? candidate.message : "";
|
|
28404
|
+
const haystack = `${code} ${name} ${message}`;
|
|
28405
|
+
return /ResourceInUse/i.test(haystack);
|
|
28406
|
+
}
|
|
28097
28407
|
function registerAppAuthTools(server) {
|
|
28098
28408
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
28099
28409
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
@@ -28102,117 +28412,161 @@ function registerAppAuthTools(server) {
|
|
|
28102
28412
|
return (0, json_content_js_1.jsonContent)(await handler());
|
|
28103
28413
|
}
|
|
28104
28414
|
catch (error) {
|
|
28415
|
+
if (error instanceof Error && (error.message === "no active environment selected" || error.message === "authentication required")) {
|
|
28416
|
+
const code = error.code;
|
|
28417
|
+
if (code) {
|
|
28418
|
+
return (0, json_content_js_1.jsonContent)(buildShortErrorWithCode(error.message, code));
|
|
28419
|
+
}
|
|
28420
|
+
return (0, json_content_js_1.jsonContent)(buildShortError(error.message));
|
|
28421
|
+
}
|
|
28105
28422
|
return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
|
|
28106
28423
|
}
|
|
28107
28424
|
};
|
|
28108
|
-
const
|
|
28109
|
-
const
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28113
|
-
|
|
28114
|
-
});
|
|
28115
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28116
|
-
return result;
|
|
28425
|
+
const describePublishableKey = async (cloudbase, envId) => {
|
|
28426
|
+
const result = await cloudbase.env.describeApiKeyList({ KeyType: "publish_key", PageNumber: 1, PageSize: 10 });
|
|
28427
|
+
return {
|
|
28428
|
+
result,
|
|
28429
|
+
record: findPublishableKey(result),
|
|
28430
|
+
};
|
|
28117
28431
|
};
|
|
28118
28432
|
server.registerTool?.("queryAppAuth", {
|
|
28119
28433
|
title: "查询应用认证配置",
|
|
28120
|
-
description: "应用侧认证配置只读入口。用于查询登录方式、provider、publishable key、client
|
|
28434
|
+
description: "应用侧认证配置只读入口。用于查询登录方式、provider、publishable key、API key、client 配置和静态域名等认证准备状态。若业务要接受普通用户名样式标识符,先查询 action=getLoginConfig;若 usernamePassword=false,下一步应立即调用 manageAppAuth(action=patchLoginStrategy, patch={ usernamePassword: true }),不要直接写 email 登录 API。",
|
|
28121
28435
|
inputSchema: {
|
|
28122
28436
|
action: zod_1.z.enum(QUERY_APP_AUTH_ACTIONS),
|
|
28123
28437
|
providerId: zod_1.z.string().optional().describe("provider 标识,如 email、google"),
|
|
28438
|
+
clientId: zod_1.z
|
|
28439
|
+
.string()
|
|
28440
|
+
.optional()
|
|
28441
|
+
.describe("OAuth client_id / DescribeClient 的 Id;省略时默认使用当前环境 ID(默认客户端)"),
|
|
28442
|
+
keyType: zod_1.z
|
|
28443
|
+
.enum(APP_AUTH_KEY_TYPES)
|
|
28444
|
+
.optional()
|
|
28445
|
+
.describe("API key 类型过滤,可选 publish_key 或 api_key"),
|
|
28446
|
+
pageNumber: zod_1.z.number().int().positive().optional().describe("API key 列表页码,从 1 开始"),
|
|
28447
|
+
pageSize: zod_1.z.number().int().positive().optional().describe("API key 列表每页条数"),
|
|
28124
28448
|
},
|
|
28125
28449
|
annotations: {
|
|
28126
28450
|
readOnlyHint: true,
|
|
28127
28451
|
openWorldHint: true,
|
|
28128
28452
|
category: "auth",
|
|
28129
28453
|
},
|
|
28130
|
-
}, async ({ action, providerId, }) => withEnvelope(async () => {
|
|
28131
|
-
const envId = await (
|
|
28454
|
+
}, async ({ action, providerId, clientId, keyType, pageNumber, pageSize, }) => withEnvelope(async () => {
|
|
28455
|
+
const envId = await getActiveEnvId(cloudBaseOptions);
|
|
28132
28456
|
const cloudbase = await getManager();
|
|
28133
28457
|
switch (action) {
|
|
28134
28458
|
case "getLoginConfig": {
|
|
28135
|
-
|
|
28136
|
-
const result = await cloudbase.env.getLoginConfigListV2();
|
|
28459
|
+
const result = await cloudbase.env.getLoginConfig();
|
|
28137
28460
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28138
|
-
|
|
28139
|
-
|
|
28461
|
+
const loginMethods = buildLoginMethods(result);
|
|
28462
|
+
const nextStep = buildLoginConfigNextStep(loginMethods);
|
|
28463
|
+
const webSdkHint = buildWebSdkHint(loginMethods);
|
|
28464
|
+
return buildSupabaseLikeAuthResponse({
|
|
28465
|
+
success: true,
|
|
28140
28466
|
envId,
|
|
28141
|
-
|
|
28142
|
-
|
|
28467
|
+
loginMethods,
|
|
28468
|
+
...(nextStep ? { next_step: nextStep } : {}),
|
|
28469
|
+
...(webSdkHint ? { webSdkHint } : {}),
|
|
28470
|
+
});
|
|
28143
28471
|
}
|
|
28144
28472
|
case "listProviders": {
|
|
28145
|
-
const result = await
|
|
28146
|
-
|
|
28147
|
-
|
|
28148
|
-
return buildEnvelope({
|
|
28149
|
-
action,
|
|
28473
|
+
const result = await cloudbase.env.getProviders();
|
|
28474
|
+
return {
|
|
28475
|
+
success: true,
|
|
28150
28476
|
envId,
|
|
28151
|
-
providers: result
|
|
28152
|
-
|
|
28153
|
-
}, "应用 provider 列表查询成功");
|
|
28477
|
+
providers: extractProviders(result),
|
|
28478
|
+
};
|
|
28154
28479
|
}
|
|
28155
28480
|
case "getProvider": {
|
|
28156
28481
|
if (!providerId) {
|
|
28157
28482
|
throw new Error("action=getProvider 时必须提供 providerId");
|
|
28158
28483
|
}
|
|
28159
|
-
const result = await
|
|
28160
|
-
|
|
28161
|
-
|
|
28162
|
-
|
|
28163
|
-
result.ProviderList ??
|
|
28164
|
-
result.Data ??
|
|
28165
|
-
[]);
|
|
28166
|
-
const provider = providers.find((item) => item.Id === providerId || item.id === providerId) ?? null;
|
|
28167
|
-
return buildEnvelope({
|
|
28168
|
-
action,
|
|
28484
|
+
const result = await cloudbase.env.getProviders();
|
|
28485
|
+
const provider = extractProviders(result).find((item) => item.Id === providerId || item.id === providerId) ?? null;
|
|
28486
|
+
return {
|
|
28487
|
+
success: true,
|
|
28169
28488
|
envId,
|
|
28170
28489
|
providerId,
|
|
28171
28490
|
provider,
|
|
28172
|
-
|
|
28173
|
-
}, `应用 provider ${providerId} 查询成功`);
|
|
28491
|
+
};
|
|
28174
28492
|
}
|
|
28175
28493
|
case "getClientConfig": {
|
|
28176
|
-
const
|
|
28177
|
-
|
|
28178
|
-
|
|
28179
|
-
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
|
|
28183
|
-
}, "应用 client 配置查询成功");
|
|
28494
|
+
const clientRecordId = clientId ?? envId;
|
|
28495
|
+
const result = await cloudbase.env.describeClient(clientRecordId);
|
|
28496
|
+
return buildClientConfigResponse(envId, clientRecordId, result);
|
|
28497
|
+
}
|
|
28498
|
+
case "getPublishableKey": {
|
|
28499
|
+
const { record } = await describePublishableKey(cloudbase, envId);
|
|
28500
|
+
return buildPublishableKeyResponse(envId, record);
|
|
28184
28501
|
}
|
|
28185
|
-
case "
|
|
28186
|
-
const
|
|
28187
|
-
|
|
28502
|
+
case "getStaticDomain": {
|
|
28503
|
+
const service = cloudbase.commonService("tcb", "2018-06-08");
|
|
28504
|
+
const result = await service.call({
|
|
28505
|
+
Action: "DescribeStaticStore",
|
|
28506
|
+
Param: { EnvId: envId },
|
|
28188
28507
|
});
|
|
28189
|
-
|
|
28190
|
-
|
|
28508
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28509
|
+
const stores = extractStaticStores(result);
|
|
28510
|
+
const primaryDomain = resolveStaticStoreDomain(stores[0]);
|
|
28511
|
+
return {
|
|
28512
|
+
success: true,
|
|
28191
28513
|
envId,
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28514
|
+
cdnDomain: primaryDomain,
|
|
28515
|
+
staticDomain: primaryDomain,
|
|
28516
|
+
staticStores: stores,
|
|
28517
|
+
};
|
|
28195
28518
|
}
|
|
28196
|
-
case "
|
|
28197
|
-
const
|
|
28198
|
-
|
|
28519
|
+
case "listApiKeys": {
|
|
28520
|
+
const currentPageNumber = pageNumber ?? 1;
|
|
28521
|
+
const currentPageSize = pageSize ?? 20;
|
|
28522
|
+
const result = await cloudbase.env.describeApiKeyList({
|
|
28523
|
+
...(keyType ? { KeyType: keyType } : {}),
|
|
28524
|
+
PageNumber: currentPageNumber,
|
|
28525
|
+
PageSize: currentPageSize,
|
|
28199
28526
|
});
|
|
28200
|
-
|
|
28201
|
-
|
|
28527
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28528
|
+
const apiKeys = extractApiKeyList(result);
|
|
28529
|
+
return {
|
|
28530
|
+
success: true,
|
|
28202
28531
|
envId,
|
|
28203
|
-
|
|
28204
|
-
|
|
28532
|
+
...(keyType ? { keyType } : {}),
|
|
28533
|
+
apiKeys,
|
|
28534
|
+
total: typeof result.Total === "number"
|
|
28535
|
+
? result.Total
|
|
28536
|
+
: apiKeys.length,
|
|
28537
|
+
pageNumber: currentPageNumber,
|
|
28538
|
+
pageSize: currentPageSize,
|
|
28539
|
+
};
|
|
28205
28540
|
}
|
|
28206
28541
|
}
|
|
28207
28542
|
}));
|
|
28208
28543
|
server.registerTool?.("manageAppAuth", {
|
|
28209
28544
|
title: "管理应用认证配置",
|
|
28210
|
-
description: "应用侧认证配置写入口。用于修改登录方式、provider、client
|
|
28545
|
+
description: "应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key,以及创建或删除 API key、自定义登录密钥。若前端要接受普通用户名样式标识符,应先执行 action=patchLoginStrategy 并传入 patch={ usernamePassword: true },再实现对应前端登录逻辑。",
|
|
28211
28546
|
inputSchema: {
|
|
28212
28547
|
action: zod_1.z.enum(MANAGE_APP_AUTH_ACTIONS),
|
|
28213
|
-
|
|
28214
|
-
|
|
28215
|
-
|
|
28548
|
+
patch: zod_1.z
|
|
28549
|
+
.record(zod_1.z.any())
|
|
28550
|
+
.optional()
|
|
28551
|
+
.describe("patchLoginStrategy 使用的简化登录策略 patch,如 { usernamePassword: true }"),
|
|
28552
|
+
providerId: zod_1.z.string().optional().describe("provider 标识,如 email、google;addProvider 时也可作为自定义 provider Id"),
|
|
28553
|
+
providerType: zod_1.z.string().optional().describe("addProvider 时的 provider 协议类型,如 OAUTH、OIDC、EMAIL"),
|
|
28554
|
+
displayName: zod_1.z
|
|
28555
|
+
.union([zod_1.z.string(), zod_1.z.record(zod_1.z.any())])
|
|
28556
|
+
.optional()
|
|
28557
|
+
.describe("addProvider 时的展示名称,可传字符串或多语言对象"),
|
|
28558
|
+
clientId: zod_1.z
|
|
28559
|
+
.string()
|
|
28560
|
+
.optional()
|
|
28561
|
+
.describe("updateClientConfig 时的客户端 Id;省略时默认使用当前环境 ID"),
|
|
28562
|
+
config: zod_1.z.record(zod_1.z.any()).optional().describe("provider / client 的配置对象"),
|
|
28563
|
+
keyType: zod_1.z
|
|
28564
|
+
.enum(APP_AUTH_KEY_TYPES)
|
|
28565
|
+
.optional()
|
|
28566
|
+
.describe("createApiKey 时的 API key 类型,默认 publish_key"),
|
|
28567
|
+
keyName: zod_1.z.string().optional().describe("createApiKey 时的 API key 名称"),
|
|
28568
|
+
expireIn: zod_1.z.number().int().min(0).optional().describe("createApiKey 时的有效期,单位秒;0 表示不过期"),
|
|
28569
|
+
keyId: zod_1.z.string().optional().describe("deleteApiKey 时的 API key 唯一标识"),
|
|
28216
28570
|
},
|
|
28217
28571
|
annotations: {
|
|
28218
28572
|
readOnlyHint: false,
|
|
@@ -28221,86 +28575,165 @@ function registerAppAuthTools(server) {
|
|
|
28221
28575
|
openWorldHint: true,
|
|
28222
28576
|
category: "auth",
|
|
28223
28577
|
},
|
|
28224
|
-
}, async ({ action,
|
|
28225
|
-
const envId = await (
|
|
28578
|
+
}, async ({ action, patch, providerId, providerType, displayName, clientId, config, keyType, keyName, expireIn, keyId, }) => withEnvelope(async () => {
|
|
28579
|
+
const envId = await getActiveEnvId(cloudBaseOptions);
|
|
28226
28580
|
const cloudbase = await getManager();
|
|
28227
28581
|
switch (action) {
|
|
28228
|
-
case "
|
|
28229
|
-
const
|
|
28582
|
+
case "patchLoginStrategy": {
|
|
28583
|
+
const input = normalizePlainObject(patch, "patch");
|
|
28584
|
+
const normalized = input ? normalizeLoginConfigPatch(input) : undefined;
|
|
28230
28585
|
if (!normalized) {
|
|
28231
|
-
throw new Error("action=
|
|
28586
|
+
throw new Error("action=patchLoginStrategy 时必须提供 patch");
|
|
28232
28587
|
}
|
|
28233
|
-
const current = await cloudbase.env.
|
|
28588
|
+
const current = await cloudbase.env.getLoginConfig();
|
|
28234
28589
|
const merged = {
|
|
28235
|
-
EnvId: envId,
|
|
28236
28590
|
...extractLoginStrategyState(current),
|
|
28237
|
-
...
|
|
28591
|
+
...normalized,
|
|
28238
28592
|
};
|
|
28239
|
-
|
|
28240
|
-
const result = await cloudbase.env.updateLoginConfigV2(merged);
|
|
28593
|
+
const result = await cloudbase.env.modifyLoginConfig(merged);
|
|
28241
28594
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28242
|
-
|
|
28243
|
-
|
|
28595
|
+
const confirmed = await cloudbase.env.getLoginConfig();
|
|
28596
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, confirmed);
|
|
28597
|
+
const loginMethods = buildLoginMethods(confirmed);
|
|
28598
|
+
const nextStep = buildLoginConfigNextStep(loginMethods);
|
|
28599
|
+
const webSdkHint = buildWebSdkHint(loginMethods);
|
|
28600
|
+
return buildSupabaseLikeAuthResponse({
|
|
28601
|
+
success: true,
|
|
28244
28602
|
envId,
|
|
28245
|
-
|
|
28246
|
-
|
|
28247
|
-
|
|
28603
|
+
loginMethods,
|
|
28604
|
+
...(nextStep ? { next_step: nextStep } : {}),
|
|
28605
|
+
...(webSdkHint ? { webSdkHint } : {}),
|
|
28606
|
+
});
|
|
28607
|
+
}
|
|
28608
|
+
case "addProvider": {
|
|
28609
|
+
const normalized = omitKeys(normalizePlainObject(config, "config"), ["EnvId"]);
|
|
28610
|
+
if (!providerType) {
|
|
28611
|
+
throw new Error("action=addProvider 时必须提供 providerType");
|
|
28612
|
+
}
|
|
28613
|
+
const localizedDisplayName = normalizeLocalizedMessage(displayName, "displayName", providerId ?? providerType);
|
|
28614
|
+
const result = await cloudbase.env.addProvider({
|
|
28615
|
+
...(providerId ? { Id: providerId } : {}),
|
|
28616
|
+
Name: localizedDisplayName,
|
|
28617
|
+
ProviderType: providerType,
|
|
28618
|
+
...(normalized ? { Config: normalized } : {}),
|
|
28619
|
+
});
|
|
28620
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28621
|
+
const addProviderResult = normalizePlainObject(result, "addProviderResult") ?? {};
|
|
28622
|
+
return {
|
|
28623
|
+
success: true,
|
|
28624
|
+
envId,
|
|
28625
|
+
providerId: providerId ??
|
|
28626
|
+
(typeof addProviderResult.Id === "string" ? addProviderResult.Id : null),
|
|
28627
|
+
providerType,
|
|
28628
|
+
};
|
|
28248
28629
|
}
|
|
28249
28630
|
case "updateProvider": {
|
|
28250
|
-
const normalized = normalizePlainObject(config, "config");
|
|
28631
|
+
const normalized = omitKeys(normalizePlainObject(config, "config"), ["EnvId", "Id"]);
|
|
28251
28632
|
if (!providerId) {
|
|
28252
28633
|
throw new Error("action=updateProvider 时必须提供 providerId");
|
|
28253
28634
|
}
|
|
28254
28635
|
if (!normalized) {
|
|
28255
28636
|
throw new Error("action=updateProvider 时必须提供 config");
|
|
28256
28637
|
}
|
|
28257
|
-
|
|
28258
|
-
|
|
28259
|
-
|
|
28260
|
-
...normalized,
|
|
28261
|
-
});
|
|
28262
|
-
return buildEnvelope({
|
|
28263
|
-
action,
|
|
28638
|
+
await cloudbase.env.modifyProvider({ Id: providerId, ...normalized });
|
|
28639
|
+
return {
|
|
28640
|
+
success: true,
|
|
28264
28641
|
envId,
|
|
28265
28642
|
providerId,
|
|
28266
|
-
|
|
28267
|
-
|
|
28643
|
+
};
|
|
28644
|
+
}
|
|
28645
|
+
case "deleteProvider": {
|
|
28646
|
+
if (!providerId) {
|
|
28647
|
+
throw new Error("action=deleteProvider 时必须提供 providerId");
|
|
28648
|
+
}
|
|
28649
|
+
const result = await cloudbase.env.deleteProvider(providerId);
|
|
28650
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28651
|
+
return {
|
|
28652
|
+
success: true,
|
|
28653
|
+
envId,
|
|
28654
|
+
providerId,
|
|
28655
|
+
deleted: true,
|
|
28656
|
+
};
|
|
28268
28657
|
}
|
|
28269
28658
|
case "updateClientConfig": {
|
|
28270
|
-
const normalized = normalizePlainObject(config, "config");
|
|
28659
|
+
const normalized = omitKeys(normalizePlainObject(config, "config"), ["EnvId", "Id"]);
|
|
28271
28660
|
if (!normalized) {
|
|
28272
28661
|
throw new Error("action=updateClientConfig 时必须提供 config");
|
|
28273
28662
|
}
|
|
28274
|
-
const
|
|
28275
|
-
|
|
28276
|
-
|
|
28663
|
+
const clientRecordId = clientId ?? envId;
|
|
28664
|
+
await cloudbase.env.modifyClient({ Id: clientRecordId, ...normalized });
|
|
28665
|
+
const confirmed = await cloudbase.env.describeClient(clientRecordId);
|
|
28666
|
+
return buildClientConfigResponse(envId, clientRecordId, confirmed);
|
|
28667
|
+
}
|
|
28668
|
+
case "ensurePublishableKey": {
|
|
28669
|
+
const existing = await describePublishableKey(cloudbase, envId);
|
|
28670
|
+
if (existing.record) {
|
|
28671
|
+
return buildPublishableKeyResponse(envId, existing.record, { created: false });
|
|
28672
|
+
}
|
|
28673
|
+
let created;
|
|
28674
|
+
try {
|
|
28675
|
+
created = await cloudbase.env.createApiKey({ KeyType: "publish_key" });
|
|
28676
|
+
}
|
|
28677
|
+
catch (error) {
|
|
28678
|
+
if (!isResourceInUseError(error)) {
|
|
28679
|
+
throw error;
|
|
28680
|
+
}
|
|
28681
|
+
const reread = await describePublishableKey(cloudbase, envId);
|
|
28682
|
+
if (!reread.record) {
|
|
28683
|
+
throw error;
|
|
28684
|
+
}
|
|
28685
|
+
return buildPublishableKeyResponse(envId, reread.record, { created: false });
|
|
28686
|
+
}
|
|
28687
|
+
return buildPublishableKeyResponse(envId, normalizePlainObject(created, "createdApiKey") ?? null, {
|
|
28688
|
+
created: true,
|
|
28277
28689
|
});
|
|
28278
|
-
return buildEnvelope({
|
|
28279
|
-
action,
|
|
28280
|
-
envId,
|
|
28281
|
-
raw: result,
|
|
28282
|
-
}, "应用 client 配置更新成功");
|
|
28283
28690
|
}
|
|
28284
|
-
case "
|
|
28285
|
-
const
|
|
28286
|
-
const result = await
|
|
28287
|
-
|
|
28288
|
-
...(
|
|
28691
|
+
case "createApiKey": {
|
|
28692
|
+
const effectiveKeyType = keyType ?? "publish_key";
|
|
28693
|
+
const result = await cloudbase.env.createApiKey({
|
|
28694
|
+
KeyType: effectiveKeyType,
|
|
28695
|
+
...(keyName ? { KeyName: keyName } : {}),
|
|
28696
|
+
...(typeof expireIn === "number" ? { ExpireIn: expireIn } : {}),
|
|
28289
28697
|
});
|
|
28290
|
-
|
|
28291
|
-
|
|
28698
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28699
|
+
const createApiKeyResult = normalizePlainObject(result, "createApiKeyResult") ?? {};
|
|
28700
|
+
return {
|
|
28701
|
+
success: true,
|
|
28292
28702
|
envId,
|
|
28293
|
-
|
|
28294
|
-
|
|
28703
|
+
keyType: effectiveKeyType,
|
|
28704
|
+
keyId: typeof result.KeyId === "string" ? result.KeyId : null,
|
|
28705
|
+
keyName: typeof result.Name === "string"
|
|
28706
|
+
? result.Name
|
|
28707
|
+
: typeof createApiKeyResult.KeyName === "string"
|
|
28708
|
+
? createApiKeyResult.KeyName
|
|
28709
|
+
: keyName ?? null,
|
|
28710
|
+
apiKey: typeof result.ApiKey === "string" ? result.ApiKey : null,
|
|
28711
|
+
expireAt: typeof result.ExpireAt === "string" ? result.ExpireAt : null,
|
|
28712
|
+
createdAt: typeof result.CreateAt === "string" ? result.CreateAt : null,
|
|
28713
|
+
};
|
|
28714
|
+
}
|
|
28715
|
+
case "deleteApiKey": {
|
|
28716
|
+
if (!keyId) {
|
|
28717
|
+
throw new Error("action=deleteApiKey 时必须提供 keyId");
|
|
28718
|
+
}
|
|
28719
|
+
const result = await cloudbase.env.deleteApiKey(keyId);
|
|
28720
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28721
|
+
return {
|
|
28722
|
+
success: true,
|
|
28723
|
+
envId,
|
|
28724
|
+
keyId,
|
|
28725
|
+
deleted: true,
|
|
28726
|
+
};
|
|
28295
28727
|
}
|
|
28296
28728
|
case "createCustomLoginKeys": {
|
|
28297
28729
|
const result = await cloudbase.env.createCustomLoginKeys();
|
|
28298
28730
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
28299
|
-
return
|
|
28300
|
-
|
|
28731
|
+
return {
|
|
28732
|
+
success: true,
|
|
28301
28733
|
envId,
|
|
28302
|
-
|
|
28303
|
-
|
|
28734
|
+
privateKey: result.PrivateKey,
|
|
28735
|
+
keyId: result.KeyID,
|
|
28736
|
+
};
|
|
28304
28737
|
}
|
|
28305
28738
|
}
|
|
28306
28739
|
}));
|
|
@@ -39846,17 +40279,34 @@ async function handleRunQuery(args, context) {
|
|
|
39846
40279
|
}
|
|
39847
40280
|
const cloudbase = await context.getManager();
|
|
39848
40281
|
const dbContext = await resolveSqlDbContext(context.getManager, context.cloudBaseOptions, args.dbInstance);
|
|
39849
|
-
|
|
39850
|
-
|
|
39851
|
-
|
|
39852
|
-
ReadOnly: true,
|
|
39853
|
-
DbInstance: {
|
|
40282
|
+
let result;
|
|
40283
|
+
try {
|
|
40284
|
+
result = await callSqlControlPlane(cloudbase, "RunSql", {
|
|
39854
40285
|
EnvId: dbContext.envId,
|
|
39855
|
-
|
|
39856
|
-
|
|
39857
|
-
|
|
39858
|
-
|
|
39859
|
-
|
|
40286
|
+
Sql: args.sql,
|
|
40287
|
+
ReadOnly: true,
|
|
40288
|
+
DbInstance: {
|
|
40289
|
+
EnvId: dbContext.envId,
|
|
40290
|
+
InstanceId: dbContext.instanceId,
|
|
40291
|
+
Schema: dbContext.schema,
|
|
40292
|
+
},
|
|
40293
|
+
});
|
|
40294
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(context.server.logger, result);
|
|
40295
|
+
}
|
|
40296
|
+
catch (error) {
|
|
40297
|
+
const errorCode = typeof error === "object" && error && "code" in error ? error.code : "";
|
|
40298
|
+
if (errorCode === "FailedOperation.DataSourceNotExist" || error.message?.includes("Database instance not found")) {
|
|
40299
|
+
return buildSqlToolResult({
|
|
40300
|
+
success: false,
|
|
40301
|
+
errorCode: "MYSQL_NOT_CREATED",
|
|
40302
|
+
message: "MySQL is not provisioned yet or not found. Please provision MySQL before running queries.",
|
|
40303
|
+
nextActions: [
|
|
40304
|
+
buildNextAction(MANAGE_SQL_DATABASE, "provisionMySQL", "Provision MySQL before querying data.", { action: "provisionMySQL", confirm: true }),
|
|
40305
|
+
],
|
|
40306
|
+
});
|
|
40307
|
+
}
|
|
40308
|
+
throw error;
|
|
40309
|
+
}
|
|
39860
40310
|
const normalized = normalizeRunSqlResult(result);
|
|
39861
40311
|
return buildSqlToolResult({
|
|
39862
40312
|
success: true,
|
|
@@ -40107,16 +40557,33 @@ async function handleRunStatement(args, context) {
|
|
|
40107
40557
|
}
|
|
40108
40558
|
const cloudbase = await context.getManager();
|
|
40109
40559
|
const dbContext = await resolveSqlDbContext(context.getManager, context.cloudBaseOptions, args.dbInstance);
|
|
40110
|
-
|
|
40111
|
-
|
|
40112
|
-
|
|
40113
|
-
DbInstance: {
|
|
40560
|
+
let result;
|
|
40561
|
+
try {
|
|
40562
|
+
result = await callSqlControlPlane(cloudbase, "RunSql", {
|
|
40114
40563
|
EnvId: dbContext.envId,
|
|
40115
|
-
|
|
40116
|
-
|
|
40117
|
-
|
|
40118
|
-
|
|
40119
|
-
|
|
40564
|
+
Sql: args.sql,
|
|
40565
|
+
DbInstance: {
|
|
40566
|
+
EnvId: dbContext.envId,
|
|
40567
|
+
InstanceId: dbContext.instanceId,
|
|
40568
|
+
Schema: dbContext.schema,
|
|
40569
|
+
},
|
|
40570
|
+
});
|
|
40571
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(context.server.logger, result);
|
|
40572
|
+
}
|
|
40573
|
+
catch (error) {
|
|
40574
|
+
const errorCode = typeof error === "object" && error && "code" in error ? error.code : "";
|
|
40575
|
+
if (errorCode === "FailedOperation.DataSourceNotExist" || error.message?.includes("Database instance not found")) {
|
|
40576
|
+
return buildSqlToolResult({
|
|
40577
|
+
success: false,
|
|
40578
|
+
errorCode: "MYSQL_NOT_CREATED",
|
|
40579
|
+
message: "MySQL is not provisioned yet or not found. Please provision MySQL before running statements.",
|
|
40580
|
+
nextActions: [
|
|
40581
|
+
buildNextAction(MANAGE_SQL_DATABASE, "provisionMySQL", "Provision MySQL before executing statements.", { action: "provisionMySQL", confirm: true }),
|
|
40582
|
+
],
|
|
40583
|
+
});
|
|
40584
|
+
}
|
|
40585
|
+
throw error;
|
|
40586
|
+
}
|
|
40120
40587
|
const statementType = getSqlVerb(args.sql) || "UNKNOWN";
|
|
40121
40588
|
const normalized = normalizeRunSqlResult(result);
|
|
40122
40589
|
return buildSqlToolResult({
|
|
@@ -40216,16 +40683,33 @@ async function handleInitializeSchema(args, context) {
|
|
|
40216
40683
|
const requestIdList = [];
|
|
40217
40684
|
for (const statement of args.statements) {
|
|
40218
40685
|
try {
|
|
40219
|
-
|
|
40220
|
-
|
|
40221
|
-
|
|
40222
|
-
DbInstance: {
|
|
40686
|
+
let result;
|
|
40687
|
+
try {
|
|
40688
|
+
result = await callSqlControlPlane(cloudbase, "RunSql", {
|
|
40223
40689
|
EnvId: dbContext.envId,
|
|
40224
|
-
|
|
40225
|
-
|
|
40226
|
-
|
|
40227
|
-
|
|
40228
|
-
|
|
40690
|
+
Sql: statement,
|
|
40691
|
+
DbInstance: {
|
|
40692
|
+
EnvId: dbContext.envId,
|
|
40693
|
+
InstanceId: dbContext.instanceId,
|
|
40694
|
+
Schema: dbContext.schema,
|
|
40695
|
+
},
|
|
40696
|
+
});
|
|
40697
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(context.server.logger, result);
|
|
40698
|
+
}
|
|
40699
|
+
catch (error) {
|
|
40700
|
+
const errorCode = typeof error === "object" && error && "code" in error ? error.code : "";
|
|
40701
|
+
if (errorCode === "FailedOperation.DataSourceNotExist" || error.message?.includes("Database instance not found")) {
|
|
40702
|
+
return buildSqlToolResult({
|
|
40703
|
+
success: false,
|
|
40704
|
+
errorCode: "MYSQL_NOT_CREATED",
|
|
40705
|
+
message: "MySQL is not provisioned yet or not found. Please provision MySQL before initializing schema.",
|
|
40706
|
+
nextActions: [
|
|
40707
|
+
buildNextAction(MANAGE_SQL_DATABASE, "provisionMySQL", "Provision MySQL before executing statements.", { action: "provisionMySQL", confirm: true }),
|
|
40708
|
+
],
|
|
40709
|
+
});
|
|
40710
|
+
}
|
|
40711
|
+
throw error;
|
|
40712
|
+
}
|
|
40229
40713
|
const normalized = normalizeRunSqlResult(result);
|
|
40230
40714
|
if (typeof normalized.requestId === "string") {
|
|
40231
40715
|
requestIdList.push(normalized.requestId);
|
|
@@ -88887,11 +89371,11 @@ function registerFunctionTools(server) {
|
|
|
88887
89371
|
}, async (input) => withEnvelope(() => handleQueryFunctions(input)));
|
|
88888
89372
|
server.registerTool?.("manageFunctions", {
|
|
88889
89373
|
title: "管理云函数域资源",
|
|
88890
|
-
description: "函数域统一写入口。通过 action
|
|
89374
|
+
description: "函数域统一写入口。通过 action 管理函数创建、代码更新、配置更新、调用函数、触发器和层绑定。危险操作需要显式 confirm=true。",
|
|
88891
89375
|
inputSchema: {
|
|
88892
89376
|
action: zod_1.z
|
|
88893
89377
|
.enum(exports.MANAGE_FUNCTION_ACTIONS)
|
|
88894
|
-
.describe("写操作类型,例如 createFunction、
|
|
89378
|
+
.describe("写操作类型,例如 createFunction、invokeFunction、attachLayer"),
|
|
88895
89379
|
func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
|
|
88896
89380
|
functionRootPath: zod_1.z.string().optional().describe("函数根目录(父目录绝对路径)"),
|
|
88897
89381
|
force: zod_1.z.boolean().optional().describe("createFunction 时是否覆盖"),
|
|
@@ -91048,6 +91532,7 @@ class EnvService {
|
|
|
91048
91532
|
this.envId = environment.getEnvId();
|
|
91049
91533
|
this.envType = environment.getEnvType();
|
|
91050
91534
|
this.cloudService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
|
|
91535
|
+
this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
|
|
91051
91536
|
this.commonService = environment.getCommonService('lowcode', '2021-01-08');
|
|
91052
91537
|
this.billingService = new billing_1.BillingService(environment.cloudBaseContext);
|
|
91053
91538
|
}
|
|
@@ -91193,7 +91678,7 @@ class EnvService {
|
|
|
91193
91678
|
}
|
|
91194
91679
|
/**
|
|
91195
91680
|
* 拉取登录配置列表
|
|
91196
|
-
* @deprecated 请使用
|
|
91681
|
+
* @deprecated 请使用 getLoginConfig 代替
|
|
91197
91682
|
* @returns {Promise<IEnvLoginConfigRes>}
|
|
91198
91683
|
*/
|
|
91199
91684
|
async getLoginConfigList() {
|
|
@@ -91203,6 +91688,7 @@ class EnvService {
|
|
|
91203
91688
|
}
|
|
91204
91689
|
/**
|
|
91205
91690
|
* 拉取登录配置列表
|
|
91691
|
+
* @deprecated 该方法已废弃,请使用 getLoginConfig 代替
|
|
91206
91692
|
*/
|
|
91207
91693
|
async getLoginConfigListV2() {
|
|
91208
91694
|
return this.commonService.call({
|
|
@@ -91212,8 +91698,21 @@ class EnvService {
|
|
|
91212
91698
|
}
|
|
91213
91699
|
});
|
|
91214
91700
|
}
|
|
91701
|
+
/**
|
|
91702
|
+
* 获取登录策略配置
|
|
91703
|
+
* 查询指定云开发环境的登录策略配置,包括手机号短信登录、邮箱登录、
|
|
91704
|
+
* 用户名密码登录和匿名登录方式的开启状态,同时包含短信验证码发送通道、
|
|
91705
|
+
* MFA 多因子认证和密码的更新策略。
|
|
91706
|
+
* @returns {Promise<IDescribeLoginConfigRes>}
|
|
91707
|
+
*/
|
|
91708
|
+
async getLoginConfig() {
|
|
91709
|
+
return this.tcbService.request('DescribeLoginConfig', {
|
|
91710
|
+
EnvId: this.envId
|
|
91711
|
+
});
|
|
91712
|
+
}
|
|
91215
91713
|
/**
|
|
91216
91714
|
* 创建登录方式
|
|
91715
|
+
* @deprecated 该方法已废弃
|
|
91217
91716
|
* 'WECHAT-OPEN':微信开放平台
|
|
91218
91717
|
* 'WECHAT-PUBLIC':微信公众平台
|
|
91219
91718
|
* @param {('WECHAT-OPEN' | 'WECHAT-PUBLIC')} platform 'WECHAT-OPEN' | 'WECHAT-PUBLIC'
|
|
@@ -91237,6 +91736,7 @@ class EnvService {
|
|
|
91237
91736
|
}
|
|
91238
91737
|
/**
|
|
91239
91738
|
* 更新登录方式配置
|
|
91739
|
+
* @deprecated 该方法已废弃,请使用 modifyLoginConfig 代替
|
|
91240
91740
|
* @param {string} configId 配置 Id,从配置列表中获取
|
|
91241
91741
|
* @param {string} [status='ENABLE'] 是否启用 'ENABLE', 'DISABLE' ,可选
|
|
91242
91742
|
* @param {string} [appId=''] 微信 appId,可选
|
|
@@ -91265,6 +91765,7 @@ class EnvService {
|
|
|
91265
91765
|
}
|
|
91266
91766
|
/**
|
|
91267
91767
|
* 更新登录方式配置
|
|
91768
|
+
* @deprecated 该方法已废弃,请使用 modifyLoginConfig 代替
|
|
91268
91769
|
* @param {IModifyLoginStrategy} options
|
|
91269
91770
|
* @returns {Promise<boolean>}
|
|
91270
91771
|
*/
|
|
@@ -91274,6 +91775,123 @@ class EnvService {
|
|
|
91274
91775
|
Param: options
|
|
91275
91776
|
});
|
|
91276
91777
|
}
|
|
91778
|
+
/**
|
|
91779
|
+
* 修改登录策略配置
|
|
91780
|
+
* 修改指定云开发环境的登录策略配置。支持开启或关闭手机号短信登录、邮箱登录、
|
|
91781
|
+
* 用户名密码登录和匿名登录,同时可配置短信验证码发送通道、MFA 多因子认证和密码更新策略。
|
|
91782
|
+
* 修改后立即生效,影响该环境下所有终端用户的登录行为。
|
|
91783
|
+
* @param {IModifyLoginConfigParams} options 修改参数
|
|
91784
|
+
* @returns {Promise<IModifyLoginConfigRes>}
|
|
91785
|
+
*/
|
|
91786
|
+
async modifyLoginConfig(options) {
|
|
91787
|
+
return this.tcbService.request('ModifyLoginConfig', Object.assign({ EnvId: this.envId }, options));
|
|
91788
|
+
}
|
|
91789
|
+
/**
|
|
91790
|
+
* 查询应用客户端详情
|
|
91791
|
+
* 获取指定云开发环境下某个客户端的配置信息,包括 OAuth 凭证、Token 有效期、会话控制策略等。
|
|
91792
|
+
* 当客户端 ID 等于环境 ID 时,返回该环境的默认客户端配置。
|
|
91793
|
+
* @param {string} Id 客户端唯一标识符(Client ID),一般使用环境 ID
|
|
91794
|
+
* @returns {Promise<IDescribeClientRes>}
|
|
91795
|
+
*/
|
|
91796
|
+
async describeClient(Id) {
|
|
91797
|
+
return this.tcbService.request('DescribeClient', {
|
|
91798
|
+
EnvId: this.envId,
|
|
91799
|
+
Id
|
|
91800
|
+
});
|
|
91801
|
+
}
|
|
91802
|
+
/**
|
|
91803
|
+
* 修改应用客户端配置
|
|
91804
|
+
* 采用增量更新策略,仅更新请求中传入的非空字段,未传入的字段保持原值不变。
|
|
91805
|
+
* 支持修改 Token 有效期、会话控制策略等配置。
|
|
91806
|
+
* 当客户端 ID 等于环境 ID 且客户端尚未创建时,将自动创建默认客户端配置。
|
|
91807
|
+
* @param {IModifyClientParams} options 修改参数
|
|
91808
|
+
* @returns {Promise<IModifyClientRes>}
|
|
91809
|
+
*/
|
|
91810
|
+
async modifyClient(options) {
|
|
91811
|
+
return this.tcbService.request('ModifyClient', Object.assign({ EnvId: this.envId }, options));
|
|
91812
|
+
}
|
|
91813
|
+
/**
|
|
91814
|
+
* 获取三方认证源列表
|
|
91815
|
+
* 查询指定云开发环境下的身份认证源列表,包括第三方登录(OAuth、OIDC、SAML)、
|
|
91816
|
+
* 微信小程序登录、自定义登录和邮箱登录等。
|
|
91817
|
+
* 若自定义登录或邮箱登录的身份源尚未创建,接口会自动追加一个默认关闭状态的记录。
|
|
91818
|
+
* @returns {Promise<IGetProvidersRes>}
|
|
91819
|
+
*/
|
|
91820
|
+
async getProviders() {
|
|
91821
|
+
return this.tcbService.request('GetProviders', {
|
|
91822
|
+
EnvId: this.envId
|
|
91823
|
+
});
|
|
91824
|
+
}
|
|
91825
|
+
/**
|
|
91826
|
+
* 修改身份认证源
|
|
91827
|
+
* 更新已有身份认证源的配置信息,支持修改基本信息、协议连接配置、登录行为控制及启用状态。
|
|
91828
|
+
* 采用增量更新策略,仅更新传入的字段,未传入的字段保持原值。
|
|
91829
|
+
* OIDC 类型修改 Issuer 后将自动通过 Discovery 重新获取端点配置。
|
|
91830
|
+
* 若 CUSTOM 或 EMAIL 身份源尚不存在,调用时将自动创建。
|
|
91831
|
+
* @param {IModifyProviderParams} options 修改参数
|
|
91832
|
+
* @returns {Promise<IModifyProviderRes>}
|
|
91833
|
+
*/
|
|
91834
|
+
async modifyProvider(options) {
|
|
91835
|
+
return this.tcbService.request('ModifyProvider', Object.assign({ EnvId: this.envId }, options));
|
|
91836
|
+
}
|
|
91837
|
+
/**
|
|
91838
|
+
* 删除第三方认证源
|
|
91839
|
+
* @param {string} Id 认证源 ID,2~32 位小写英文字母或数字,如 'github'
|
|
91840
|
+
* @returns {Promise<IDeleteProviderRes>}
|
|
91841
|
+
*/
|
|
91842
|
+
async deleteProvider(Id) {
|
|
91843
|
+
return this.tcbService.request('DeleteProvider', {
|
|
91844
|
+
EnvId: this.envId,
|
|
91845
|
+
Id
|
|
91846
|
+
});
|
|
91847
|
+
}
|
|
91848
|
+
/**
|
|
91849
|
+
* 添加身份认证源
|
|
91850
|
+
* 在指定云开发环境下创建新的身份认证源,支持 OAuth 2.0、OIDC、SAML 2.0 等标准协议,
|
|
91851
|
+
* 以及自定义登录和邮箱登录等多种认证方式。若身份源 ID 已存在将返回错误。
|
|
91852
|
+
* 限制:一个环境最大可允许加入 20 个认证源。
|
|
91853
|
+
* @param {IAddProviderParams} options 添加参数
|
|
91854
|
+
* @returns {Promise<IAddProviderRes>}
|
|
91855
|
+
*/
|
|
91856
|
+
async addProvider(options) {
|
|
91857
|
+
return this.tcbService.request('AddProvider', Object.assign({ EnvId: this.envId }, options));
|
|
91858
|
+
}
|
|
91859
|
+
/**
|
|
91860
|
+
* 创建 API Key
|
|
91861
|
+
* 在指定云开发环境下创建 API Key 访问凭证。支持两种类型:
|
|
91862
|
+
* - api_key:服务端管理员访问凭证,可设置有效期
|
|
91863
|
+
* - publish_key:前端匿名访问凭证,固定有效期
|
|
91864
|
+
* 创建成功后返回 API Key 明文 Token,该值仅在创建时返回一次,请妥善保存。
|
|
91865
|
+
* @param {ICreateApiKeyParams} options 创建参数
|
|
91866
|
+
* @returns {Promise<ICreateApiKeyRes>}
|
|
91867
|
+
*/
|
|
91868
|
+
async createApiKey(options) {
|
|
91869
|
+
return this.tcbService.request('CreateApiKey', Object.assign({ EnvId: this.envId }, options));
|
|
91870
|
+
}
|
|
91871
|
+
/**
|
|
91872
|
+
* 删除 API Key
|
|
91873
|
+
* 删除指定云开发环境下的某个 API Key 服务端访问凭证。
|
|
91874
|
+
* 删除后该 Key 对应的 Token 将被吊销,已使用该 Key 发起的请求将失败。
|
|
91875
|
+
* 该操作具有幂等性,若指定的 API Key 不存在则直接返回成功。
|
|
91876
|
+
* @param {string} KeyId 密钥唯一标识符,可通过查询密钥列表接口获取
|
|
91877
|
+
* @returns {Promise<IDeleteApiKeyRes>}
|
|
91878
|
+
*/
|
|
91879
|
+
async deleteApiKey(KeyId) {
|
|
91880
|
+
return this.tcbService.request('DeleteApiKey', {
|
|
91881
|
+
EnvId: this.envId,
|
|
91882
|
+
KeyId
|
|
91883
|
+
});
|
|
91884
|
+
}
|
|
91885
|
+
/**
|
|
91886
|
+
* 查询 API Key 列表
|
|
91887
|
+
* 分页查询指定云开发环境下的 API Key 访问凭证列表,支持按类型过滤。
|
|
91888
|
+
* api_key 类型的令牌值脱敏处理,publish_key 类型返回完整明文。
|
|
91889
|
+
* @param {IDescribeApiKeyListParams} options 查询参数
|
|
91890
|
+
* @returns {Promise<IDescribeApiKeyListRes>}
|
|
91891
|
+
*/
|
|
91892
|
+
async describeApiKeyList(options) {
|
|
91893
|
+
return this.tcbService.request('DescribeApiKeyList', Object.assign({ EnvId: this.envId }, options));
|
|
91894
|
+
}
|
|
91277
91895
|
// 创建自定义登录私钥
|
|
91278
91896
|
async createCustomLoginKeys() {
|
|
91279
91897
|
return this.cloudService.request('CreateCustomLoginKeys', {
|
|
@@ -116004,7 +116622,7 @@ function registerHostingTools(server) {
|
|
|
116004
116622
|
// uploadFiles - 上传文件到静态网站托管 (cloud-incompatible)
|
|
116005
116623
|
server.registerTool("uploadFiles", {
|
|
116006
116624
|
title: "上传静态文件",
|
|
116007
|
-
description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage
|
|
116625
|
+
description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。",
|
|
116008
116626
|
inputSchema: {
|
|
116009
116627
|
localPath: zod_1.z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"),
|
|
116010
116628
|
cloudPath: zod_1.z.string().optional().describe("静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。"),
|
|
@@ -132946,7 +133564,7 @@ class TelemetryReporter {
|
|
|
132946
133564
|
const nodeVersion = process.version; // Node.js版本
|
|
132947
133565
|
const arch = os_1.default.arch(); // 系统架构
|
|
132948
133566
|
// 从构建时注入的版本号获取MCP版本信息
|
|
132949
|
-
const mcpVersion = process.env.npm_package_version || "2.
|
|
133567
|
+
const mcpVersion = process.env.npm_package_version || "2.17.0-beta.2" || 0;
|
|
132950
133568
|
return {
|
|
132951
133569
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
132952
133570
|
deviceId: this.deviceId,
|
|
@@ -133145,7 +133763,7 @@ const reportToolCall = async (params) => {
|
|
|
133145
133763
|
toolName: params.toolName,
|
|
133146
133764
|
success: params.success ? 'true' : 'false',
|
|
133147
133765
|
requestId: params.requestId,
|
|
133148
|
-
duration: params.duration,
|
|
133766
|
+
duration: params.duration !== undefined ? String(params.duration) : undefined,
|
|
133149
133767
|
error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
|
|
133150
133768
|
envId: envId || 'unknown',
|
|
133151
133769
|
nodeVersion,
|
|
@@ -133200,8 +133818,8 @@ const reportToolkitLifecycle = async (params) => {
|
|
|
133200
133818
|
// 报告 Toolkit 生命周期事件
|
|
133201
133819
|
const eventData = {
|
|
133202
133820
|
event: params.event,
|
|
133203
|
-
duration: params.duration,
|
|
133204
|
-
exitCode: params.exitCode,
|
|
133821
|
+
duration: params.duration !== undefined ? String(params.duration) : undefined,
|
|
133822
|
+
exitCode: params.exitCode !== undefined ? String(params.exitCode) : undefined,
|
|
133205
133823
|
error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
|
|
133206
133824
|
envId: envId || 'unknown',
|
|
133207
133825
|
nodeVersion,
|
|
@@ -177197,6 +177815,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
177197
177815
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
177198
177816
|
};
|
|
177199
177817
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
177818
|
+
exports.resolveSkillSearchRoots = resolveSkillSearchRoots;
|
|
177200
177819
|
exports.getClaudePrompt = getClaudePrompt;
|
|
177201
177820
|
exports.registerRagTools = registerRagTools;
|
|
177202
177821
|
const adm_zip_1 = __importDefault(__webpack_require__(30283));
|
|
@@ -177205,10 +177824,20 @@ const lockfile_1 = __importDefault(__webpack_require__(80127));
|
|
|
177205
177824
|
const os = __importStar(__webpack_require__(21820));
|
|
177206
177825
|
const path = __importStar(__webpack_require__(39902));
|
|
177207
177826
|
const zod_1 = __webpack_require__(21614);
|
|
177827
|
+
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
177208
177828
|
const claude_prompt_js_1 = __webpack_require__(68359);
|
|
177829
|
+
const json_content_js_1 = __webpack_require__(4365);
|
|
177209
177830
|
const logger_js_1 = __webpack_require__(13039);
|
|
177210
177831
|
// 1. 枚举定义
|
|
177211
177832
|
const KnowledgeBaseEnum = zod_1.z.enum(["cloudbase", "scf", "miniprogram"]);
|
|
177833
|
+
const SearchKnowledgeModeEnum = zod_1.z.enum(["vector", "skill", "openapi", "docs"]);
|
|
177834
|
+
const CloudBaseDocsActionEnum = zod_1.z.enum([
|
|
177835
|
+
"listModules",
|
|
177836
|
+
"listModuleDocs",
|
|
177837
|
+
"findByName",
|
|
177838
|
+
"readDoc",
|
|
177839
|
+
"searchDocs",
|
|
177840
|
+
]);
|
|
177212
177841
|
// 2. 枚举到后端 id 的映射
|
|
177213
177842
|
const KnowledgeBaseIdMap = {
|
|
177214
177843
|
cloudbase: "ykfzskv4_ad28",
|
|
@@ -177268,6 +177897,39 @@ else {
|
|
|
177268
177897
|
}
|
|
177269
177898
|
// 共享的下载 Promise,防止并发重复下载
|
|
177270
177899
|
let resourceDownloadPromise = null;
|
|
177900
|
+
async function filterExistingDirs(pathsToCheck) {
|
|
177901
|
+
const resolved = [];
|
|
177902
|
+
for (const candidate of pathsToCheck) {
|
|
177903
|
+
if (!candidate || resolved.includes(candidate)) {
|
|
177904
|
+
continue;
|
|
177905
|
+
}
|
|
177906
|
+
try {
|
|
177907
|
+
const stat = await fs.stat(candidate);
|
|
177908
|
+
if (stat.isDirectory()) {
|
|
177909
|
+
resolved.push(candidate);
|
|
177910
|
+
}
|
|
177911
|
+
}
|
|
177912
|
+
catch {
|
|
177913
|
+
// Ignore missing candidates and keep checking fallbacks.
|
|
177914
|
+
}
|
|
177915
|
+
}
|
|
177916
|
+
return resolved;
|
|
177917
|
+
}
|
|
177918
|
+
async function resolveSkillSearchRoots(options = {}) {
|
|
177919
|
+
const cliEntryPath = options.cliEntryPath ?? process.argv[1];
|
|
177920
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
177921
|
+
const repoRoot = cliEntryPath
|
|
177922
|
+
? path.resolve(path.dirname(cliEntryPath), "..", "..")
|
|
177923
|
+
: undefined;
|
|
177924
|
+
return filterExistingDirs([
|
|
177925
|
+
repoRoot
|
|
177926
|
+
? path.join(repoRoot, ".generated", "compat-config", ".codebuddy", "skills")
|
|
177927
|
+
: undefined,
|
|
177928
|
+
repoRoot ? path.join(repoRoot, "config", "source", "skills") : undefined,
|
|
177929
|
+
options.fallbackSkillsRoot,
|
|
177930
|
+
path.join(homeDir, ".cloudbase-mcp", "web-template", ".claude", "skills"),
|
|
177931
|
+
]);
|
|
177932
|
+
}
|
|
177271
177933
|
// 检查缓存是否可用(未过期)
|
|
177272
177934
|
async function canUseCache() {
|
|
177273
177935
|
try {
|
|
@@ -177325,6 +177987,29 @@ function safeStringify(obj) {
|
|
|
177325
177987
|
return "";
|
|
177326
177988
|
}
|
|
177327
177989
|
}
|
|
177990
|
+
function buildDocsEnvelope(action, data, message) {
|
|
177991
|
+
return {
|
|
177992
|
+
success: true,
|
|
177993
|
+
data: {
|
|
177994
|
+
action,
|
|
177995
|
+
...data,
|
|
177996
|
+
},
|
|
177997
|
+
message,
|
|
177998
|
+
};
|
|
177999
|
+
}
|
|
178000
|
+
function buildDocsErrorEnvelope(error) {
|
|
178001
|
+
return {
|
|
178002
|
+
success: false,
|
|
178003
|
+
data: {},
|
|
178004
|
+
message: error instanceof Error ? error.message : String(error),
|
|
178005
|
+
};
|
|
178006
|
+
}
|
|
178007
|
+
function requireStringParam(value, fieldName, action) {
|
|
178008
|
+
if (!value?.trim()) {
|
|
178009
|
+
throw new Error(`action=${action} 时必须提供 ${fieldName}`);
|
|
178010
|
+
}
|
|
178011
|
+
return value.trim();
|
|
178012
|
+
}
|
|
177328
178013
|
// OpenAPI 文档 URL 列表
|
|
177329
178014
|
const OPENAPI_SOURCES = [
|
|
177330
178015
|
{
|
|
@@ -177630,21 +178315,38 @@ async function registerRagTools(server) {
|
|
|
177630
178315
|
});
|
|
177631
178316
|
let openapis = [];
|
|
177632
178317
|
let skills = [];
|
|
178318
|
+
let fallbackSkillsRoot;
|
|
177633
178319
|
try {
|
|
177634
178320
|
const { webTemplateDir, openAPIDocs } = await downloadResources();
|
|
177635
178321
|
openapis = openAPIDocs;
|
|
177636
|
-
|
|
178322
|
+
fallbackSkillsRoot = path.join(webTemplateDir, ".claude", "skills");
|
|
177637
178323
|
}
|
|
177638
178324
|
catch (error) {
|
|
177639
178325
|
(0, logger_js_1.warn)("[downloadResources] Failed to download resources", {
|
|
177640
178326
|
error,
|
|
177641
178327
|
});
|
|
177642
178328
|
}
|
|
178329
|
+
try {
|
|
178330
|
+
const skillRoots = await resolveSkillSearchRoots({
|
|
178331
|
+
fallbackSkillsRoot,
|
|
178332
|
+
});
|
|
178333
|
+
const preferredSkillRoot = skillRoots[0];
|
|
178334
|
+
if (preferredSkillRoot) {
|
|
178335
|
+
skills = await collectSkillDescriptions(preferredSkillRoot);
|
|
178336
|
+
}
|
|
178337
|
+
}
|
|
178338
|
+
catch (error) {
|
|
178339
|
+
(0, logger_js_1.warn)("[registerRagTools] Failed to resolve local skill roots", {
|
|
178340
|
+
error,
|
|
178341
|
+
});
|
|
178342
|
+
}
|
|
178343
|
+
const getDocsManager = () => (0, cloudbase_manager_js_1.createCloudBaseManagerWithOptions)(server.cloudBaseOptions ?? {})
|
|
178344
|
+
?.docs;
|
|
177643
178345
|
server.registerTool?.("searchKnowledgeBase", {
|
|
177644
178346
|
title: "云开发知识库检索",
|
|
177645
|
-
description: `云开发知识库智能检索工具,支持向量查询 (vector)、固定技能文档 (skill)
|
|
178347
|
+
description: `云开发知识库智能检索工具,支持向量查询 (vector)、固定技能文档 (skill)、OpenAPI 文档 (openapi) 和 CloudBase 官方文档 (docs) 查询。
|
|
177646
178348
|
|
|
177647
|
-
强烈推荐始终优先使用固定技能文档 (skill)
|
|
178349
|
+
强烈推荐始终优先使用固定技能文档 (skill)、OpenAPI 文档 (openapi) 或 CloudBase 官方文档 (docs) 模式进行检索,仅当固定文档无法覆盖你的问题时,再使用向量查询 (vector) 模式。
|
|
177648
178350
|
|
|
177649
178351
|
固定技能文档 (skill) 查询当前支持 ${skills.length} 个固定文档,分别是:
|
|
177650
178352
|
${skills
|
|
@@ -177656,7 +178358,7 @@ async function registerRagTools(server) {
|
|
|
177656
178358
|
.map((api) => `API名:${api.name} API介绍:${api.description}`)
|
|
177657
178359
|
.join("\n")}`,
|
|
177658
178360
|
inputSchema: {
|
|
177659
|
-
mode:
|
|
178361
|
+
mode: SearchKnowledgeModeEnum,
|
|
177660
178362
|
skillName: zod_1.z
|
|
177661
178363
|
.enum(skills.map((skill) => path.basename(path.dirname(skill.absolutePath))))
|
|
177662
178364
|
.optional()
|
|
@@ -177665,6 +178367,23 @@ async function registerRagTools(server) {
|
|
|
177665
178367
|
.enum(openapis.map((api) => api.name))
|
|
177666
178368
|
.optional()
|
|
177667
178369
|
.describe("mode=openapi 时指定。API 名称。"),
|
|
178370
|
+
action: CloudBaseDocsActionEnum.optional().describe("mode=docs 时指定。CloudBase 文档操作类型:listModules=列出所有文档模块,listModuleDocs=获取指定模块的目录结构,findByName=按名称/路径/URL 智能查找,readDoc=读取指定文档 Markdown,searchDocs=全文搜索官方文档。"),
|
|
178371
|
+
moduleName: zod_1.z
|
|
178372
|
+
.string()
|
|
178373
|
+
.optional()
|
|
178374
|
+
.describe("mode=docs 且 action=listModuleDocs 时指定。模块名称。"),
|
|
178375
|
+
input: zod_1.z
|
|
178376
|
+
.string()
|
|
178377
|
+
.optional()
|
|
178378
|
+
.describe("mode=docs 且 action=findByName 时指定。支持模块名、文档标题、层级路径或 URL。"),
|
|
178379
|
+
docPath: zod_1.z
|
|
178380
|
+
.string()
|
|
178381
|
+
.optional()
|
|
178382
|
+
.describe("mode=docs 且 action=readDoc 时指定。文档相对路径或完整 URL。"),
|
|
178383
|
+
query: zod_1.z
|
|
178384
|
+
.string()
|
|
178385
|
+
.optional()
|
|
178386
|
+
.describe("mode=docs 且 action=searchDocs 时指定。全文检索关键词。"),
|
|
177668
178387
|
threshold: zod_1.z
|
|
177669
178388
|
.number()
|
|
177670
178389
|
.default(0.5)
|
|
@@ -177694,14 +178413,62 @@ async function registerRagTools(server) {
|
|
|
177694
178413
|
openWorldHint: true,
|
|
177695
178414
|
category: "rag",
|
|
177696
178415
|
},
|
|
177697
|
-
}, async ({ id, content, options
|
|
178416
|
+
}, async ({ id, content, options, limit = 5, threshold = 0.5, mode, skillName, apiName, action, moduleName, input, docPath, query, }) => {
|
|
178417
|
+
const chunkExpand = options?.chunkExpand ?? [3, 3];
|
|
178418
|
+
if (mode === "docs") {
|
|
178419
|
+
try {
|
|
178420
|
+
const resolvedAction = action;
|
|
178421
|
+
if (!resolvedAction) {
|
|
178422
|
+
throw new Error("mode=docs 时必须提供 action");
|
|
178423
|
+
}
|
|
178424
|
+
const docsManager = getDocsManager();
|
|
178425
|
+
if (!docsManager) {
|
|
178426
|
+
throw new Error("当前 @cloudbase/manager-node 实例不支持 app.docs,请确认版本 >= 5.0.0。");
|
|
178427
|
+
}
|
|
178428
|
+
if (resolvedAction === "listModules") {
|
|
178429
|
+
const modules = await docsManager.listModules();
|
|
178430
|
+
return (0, json_content_js_1.jsonContent)(buildDocsEnvelope(resolvedAction, { modules }, "CloudBase 文档模块列表获取成功"));
|
|
178431
|
+
}
|
|
178432
|
+
if (resolvedAction === "listModuleDocs") {
|
|
178433
|
+
const resolvedModuleName = requireStringParam(moduleName, "moduleName", resolvedAction);
|
|
178434
|
+
const docs = await docsManager.listModuleDocs(resolvedModuleName);
|
|
178435
|
+
return (0, json_content_js_1.jsonContent)(buildDocsEnvelope(resolvedAction, { moduleName: resolvedModuleName, docs }, "CloudBase 模块文档目录获取成功"));
|
|
178436
|
+
}
|
|
178437
|
+
if (resolvedAction === "findByName") {
|
|
178438
|
+
const resolvedInput = requireStringParam(input, "input", resolvedAction);
|
|
178439
|
+
const result = await docsManager.findByName(resolvedInput);
|
|
178440
|
+
return (0, json_content_js_1.jsonContent)(buildDocsEnvelope(resolvedAction, { input: resolvedInput, result }, "CloudBase 文档查找成功"));
|
|
178441
|
+
}
|
|
178442
|
+
if (resolvedAction === "readDoc") {
|
|
178443
|
+
const resolvedDocPath = requireStringParam(docPath, "docPath", resolvedAction);
|
|
178444
|
+
const markdown = await docsManager.readDoc(resolvedDocPath);
|
|
178445
|
+
return (0, json_content_js_1.jsonContent)(buildDocsEnvelope(resolvedAction, { docPath: resolvedDocPath, content: markdown }, "CloudBase 文档读取成功"));
|
|
178446
|
+
}
|
|
178447
|
+
const resolvedQuery = requireStringParam(query, "query", resolvedAction);
|
|
178448
|
+
const results = await docsManager.searchDocs(resolvedQuery);
|
|
178449
|
+
return (0, json_content_js_1.jsonContent)(buildDocsEnvelope(resolvedAction, { query: resolvedQuery, results }, "CloudBase 文档搜索成功"));
|
|
178450
|
+
}
|
|
178451
|
+
catch (error) {
|
|
178452
|
+
return (0, json_content_js_1.jsonContent)(buildDocsErrorEnvelope(error));
|
|
178453
|
+
}
|
|
178454
|
+
}
|
|
177698
178455
|
if (mode === "skill") {
|
|
177699
|
-
const
|
|
178456
|
+
const skill = skills.find((item) => item.absolutePath.includes(skillName));
|
|
178457
|
+
if (!skill) {
|
|
178458
|
+
return {
|
|
178459
|
+
content: [
|
|
178460
|
+
{
|
|
178461
|
+
type: "text",
|
|
178462
|
+
text: `Skill document "${skillName}" not found. Available skill docs: ${skills.map((item) => path.basename(path.dirname(item.absolutePath))).join(", ") || "none"}`,
|
|
178463
|
+
},
|
|
178464
|
+
],
|
|
178465
|
+
};
|
|
178466
|
+
}
|
|
177700
178467
|
return {
|
|
177701
178468
|
content: [
|
|
177702
178469
|
{
|
|
177703
178470
|
type: "text",
|
|
177704
|
-
text: `The skill doc's absolute path is: ${absolutePath}. ${(await fs.readFile(absolutePath)).toString()}`,
|
|
178471
|
+
text: `The skill doc's absolute path is: ${skill.absolutePath}. ${(await fs.readFile(skill.absolutePath)).toString()}`,
|
|
177705
178472
|
},
|
|
177706
178473
|
],
|
|
177707
178474
|
};
|
|
@@ -177713,7 +178480,7 @@ async function registerRagTools(server) {
|
|
|
177713
178480
|
content: [
|
|
177714
178481
|
{
|
|
177715
178482
|
type: "text",
|
|
177716
|
-
text: `OpenAPI document "${apiName}" not found. Available APIs: ${openapis.map((a) => a.name).join(", ")}`,
|
|
178483
|
+
text: `OpenAPI document "${apiName}" not found. Available APIs: ${openapis.map((a) => a.name).join(", ") || "none"}`,
|
|
177717
178484
|
},
|
|
177718
178485
|
],
|
|
177719
178486
|
};
|
|
@@ -186997,24 +187764,30 @@ alwaysApply: true
|
|
|
186997
187764
|
**⚠️ CRITICAL: Read this section first based on your project type**
|
|
186998
187765
|
|
|
186999
187766
|
### When Developing a Web Project:
|
|
187000
|
-
1. **Environment Check**:
|
|
187001
|
-
|
|
187767
|
+
1. **Environment Check**: Query environment info only when the task actually needs environment metadata, deployment, or an env ID. For existing applications with active TODOs or unfinished handlers, do not spend turns on hosting or domain queries unless the user explicitly asks for deployment.
|
|
187768
|
+
1. **Execution Mode for Existing-Code Tasks**: If the task is to complete an existing application, wire TODO logic, or repair a broken flow, do **not** enter design-only, brainstorming-only, or spec-only flows first. Gather only the minimum context needed, then implement directly. Do not stop after “project exploration complete”.
|
|
187769
|
+
1. **Existing File Focus (MANDATORY)**: For an existing Web app with active pages or TODO-bearing files, do not broad-scan the whole repo before coding. First inspect only the task-critical files: \`src/lib/backend.*\`, \`src/lib/auth.*\`, \`src/lib/cms-service.*\`, \`src/lib/storage-service.*\`, and the page/components that already consume those APIs. Then implement those TODOs before doing any wide repo exploration.
|
|
187770
|
+
1. **Task Tool Discipline**: For targeted repair tasks, do not spend many turns in \`TaskCreate\`, \`TaskUpdate\`, or project-management loops. At most keep one short checklist. After that, write code. Do not let task-management consume the turn budget.
|
|
187771
|
+
1. **No README / UI-Design Detour For Existing Apps**: If the repo already contains the target pages and only backend TODOs are missing, skip README-driven discovery and skip UI-design workflows unless the task explicitly asks for a visual redesign. Backend wiring is the priority.
|
|
187772
|
+
1. **No Broad Grep / Glob Sweep**: In targeted repair tasks, do not recursively Grep / Glob the whole repo for long periods. Open the known TODO-bearing files directly and start editing.
|
|
187002
187773
|
3. **⚠️ UI Design (CRITICAL)**: **MUST read \`rules/ui-design/rule.md\` FIRST before generating any page, interface, component, or style** - This is NOT optional. You MUST explicitly read this file and output the design specification before writing any UI code.
|
|
187003
187774
|
4. **Core Capabilities**: Read Core Capabilities section below (especially UI Design and Database + Authentication for Web)
|
|
187004
187775
|
5. **Platform Rules**: Read \`rules/web-development/rule.md\` for platform-specific rules (SDK integration, static hosting, build configuration)
|
|
187005
187776
|
6. **Authentication**: Read \`rules/auth-web/rule.md\` - **MUST use Web SDK built-in authentication**
|
|
187777
|
+
- \`accessKey\` can only be a real Publishable Key obtained from CloudBase auth/app config. Never use \`envId\`, a username, or any placeholder string as \`accessKey\`.
|
|
187778
|
+
- If you do not have a real Publishable Key yet, do not fabricate one. First query or configure auth readiness, or initialize without \`accessKey\` if the login flow itself is what will establish end-user identity.
|
|
187779
|
+
7. **Cloud Storage (when files are uploaded from browser code)**: Read \`rules/cloud-storage-web/rule.md\` before implementing upload / preview / temp URL flows. For local dev origins (for example Vite on \`127.0.0.1\` / \`localhost\`), query current security domains first and compare against the returned whitelist entry format (usually \`host:port\`, such as \`127.0.0.1:4173\` or \`localhost:5173\`). If \`envQuery(action="domains")\` reports missing local entries or returns a \`next_step\`, execute that \`envDomainManagement\` step before relying on \`app.uploadFile()\`. After upload, do not handcraft a storage URL from \`envId\`, bucket domain, or \`cloudPath\`; resolve an actual browser-usable URL through \`app.getTempFileURL()\` using the returned \`fileID\`.
|
|
187006
187780
|
7. **Database**:
|
|
187007
187781
|
- NoSQL: \`rules/no-sql-web-sdk/rule.md\`
|
|
187008
187782
|
- MySQL: \`rules/relational-database-web/rule.md\` + \`rules/relational-database-mcp/rule.md\`
|
|
187009
187783
|
|
|
187010
187784
|
### When Developing a Mini Program Project:
|
|
187011
187785
|
1. **Environment Check**: Call \`envQuery\` tool first (applies to all interactions)
|
|
187012
|
-
2. **⚠️
|
|
187013
|
-
3.
|
|
187014
|
-
4. **
|
|
187015
|
-
5. **
|
|
187016
|
-
6. **
|
|
187017
|
-
7. **Database**:
|
|
187786
|
+
2. **⚠️ UI Design (CRITICAL)**: **MUST read \`rules/ui-design/rule.md\` FIRST before generating any page, interface, component, or style** - This is NOT optional. You MUST explicitly read this file and output the design specification before writing any UI code.
|
|
187787
|
+
3. **Core Capabilities**: Read Core Capabilities section below (especially UI Design and Database + Authentication for Mini Program)
|
|
187788
|
+
4. **Platform Rules**: Read \`rules/miniprogram-development/rule.md\` for platform-specific rules (project structure, WeChat Developer Tools, wx.cloud usage)
|
|
187789
|
+
5. **Authentication**: Read \`rules/auth-wechat/rule.md\` - **Naturally login-free, get OPENID in cloud functions**
|
|
187790
|
+
6. **Database**:
|
|
187018
187791
|
- NoSQL: \`rules/no-sql-wx-mp-sdk/rule.md\`
|
|
187019
187792
|
- MySQL: \`rules/relational-database-mcp/rule.md\` (via MCP tools)
|
|
187020
187793
|
|
|
@@ -187055,6 +187828,10 @@ As the most important part of application development, the following four core c
|
|
|
187055
187828
|
**Authentication**:
|
|
187056
187829
|
- **Web Projects**:
|
|
187057
187830
|
- Must use CloudBase Web SDK built-in authentication, refer to \`rules/auth-web/rule.md\`
|
|
187831
|
+
- For username-style accounts, use \`auth.signUp({ username, password })\` and \`auth.signInWithPassword({ username, password })\`
|
|
187832
|
+
- Do not use email-only helpers or email-only input semantics when the account identifier is a plain username string
|
|
187833
|
+
- When an existing schema or fixture already provides role/profile metadata in \`users\`, \`profiles\`, or \`user_roles\`, reuse that collection instead of inventing yet another one. In role-based content-management flows, a \`user_roles\` collection keyed by \`uid\` is an acceptable and validated pattern.
|
|
187834
|
+
- For browser-side file upload / preview flows, read \`rules/cloud-storage-web/rule.md\`; on local dev origins, verify the required local host entries (for example \`127.0.0.1:4173\`, \`localhost:5173\`) are already in the environment security-domain whitelist before depending on \`app.uploadFile()\`. If \`envQuery(action="domains")\` returns missing local entries or a \`next_step\`, execute that \`envDomainManagement\` action first. After upload, use \`app.getTempFileURL()\` to resolve the preview/download URL instead of constructing a URL string manually.
|
|
187058
187835
|
- Platform development rules: Refer to \`rules/web-development/rule.md\` for Web SDK integration, static hosting deployment, and build configuration
|
|
187059
187836
|
- **Mini Program Projects**:
|
|
187060
187837
|
- Naturally login-free, get \`wxContext.OPENID\` in cloud functions, refer to \`rules/auth-wechat/rule.md\`
|
|
@@ -187064,6 +187841,9 @@ As the most important part of application development, the following four core c
|
|
|
187064
187841
|
**Database Operations**:
|
|
187065
187842
|
- **Web Projects**:
|
|
187066
187843
|
- NoSQL Database: Refer to \`rules/no-sql-web-sdk/rule.md\`
|
|
187844
|
+
- For content collections that only need owner-only writes, simple permissions such as \`READONLY\` can be a good fit. But for CMS-style collections that require **app-level admin users** to edit/delete any record while editors can only edit/delete their own records, a \`CUSTOM\` rule is expected.
|
|
187845
|
+
- A validated CMS article rule shape is: \`{"read":"auth.uid != null","create":"auth.uid != null","update":"auth.uid != null && (get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid)","delete":"auth.uid != null && (get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid)"}\`.
|
|
187846
|
+
- For that CMS pattern, frontend writes can stay on \`.doc(id).update()\` / \`.doc(id).remove()\`. Do not force a \`where(...)\` rewrite unless the task explicitly requires a different rule design.
|
|
187067
187847
|
- MySQL Relational Database: Refer to \`rules/relational-database-web/rule.md\` (Web application development) and \`rules/relational-database-mcp/rule.md\` (Management via MCP tools)
|
|
187068
187848
|
- Platform development rules: Refer to \`rules/web-development/rule.md\` for Web SDK database integration patterns
|
|
187069
187849
|
- **Mini Program Projects**:
|
|
@@ -187077,6 +187857,7 @@ As the most important part of application development, the following four core c
|
|
|
187077
187857
|
- Use CloudBase static hosting after build completion
|
|
187078
187858
|
- Deploy using \`uploadFiles\` tool
|
|
187079
187859
|
- \`uploadFiles\` is only for static hosting deployment; do not use it for COS / cloud storage uploads. For storage objects, use \`manageStorage(action="upload")\`
|
|
187860
|
+
- If the task is local verification or an unfinished application repair with a dev server workflow, skip static hosting deployment unless the user explicitly asks for a deployed URL
|
|
187080
187861
|
- Remind users that CDN has a few minutes of cache after deployment
|
|
187081
187862
|
- Generate markdown format access links with random queryString
|
|
187082
187863
|
|
|
@@ -187095,7 +187876,7 @@ As the most important part of application development, the following four core c
|
|
|
187095
187876
|
### 0. Environment Check (First Step)
|
|
187096
187877
|
After user inputs any content, first check CloudBase environment status:
|
|
187097
187878
|
- Ensure current CloudBase environment ID is known
|
|
187098
|
-
- If not present in conversation history,
|
|
187879
|
+
- If not present in conversation history and the task needs env metadata or deployment, call \`envQuery\` tool with parameter \`action=info\` to query current environment information and environment ID
|
|
187099
187880
|
- **Important**: When environment ID configuration is involved in code later, automatically use the queried environment ID, no need for manual user input
|
|
187100
187881
|
|
|
187101
187882
|
### 1. Scenario Identification
|
|
@@ -187135,31 +187916,33 @@ Before starting work, suggest confirming with user:
|
|
|
187135
187916
|
3. "Please confirm if my understanding is correct"
|
|
187136
187917
|
|
|
187137
187918
|
## Core Behavior Rules
|
|
187919
|
+
0. **Existing-Code Completion Bias**: For existing applications with TODOs or broken flows, implementation is the goal. Do not hand off to a design-only phase, do not stop at exploration, and do not treat a summary of findings as task completion. After minimal context gathering, write code and wire the real backend flow.
|
|
187920
|
+
0. **No Broad Exploration For TODO Apps**: When the repo already exposes TODO markers in backend integration files, do not keep globbing, summarizing, or reading unrelated files. Open the TODO-bearing files and implement them.
|
|
187921
|
+
0. **No Task-Loop Derailment**: In targeted repair tasks, avoid repeated \`TaskCreate\` / \`TaskUpdate\` cycles. One minimal checklist is enough; the rest of the budget should go to file edits, tool calls, and verification.
|
|
187922
|
+
0. **Do Not Delegate Or Defer**: In targeted repair tasks, do not invoke design copilots, brainstorming helpers, or delegated agent flows before the core TODO files are implemented. Finish the direct file edits first.
|
|
187138
187923
|
1. **Tool Priority**: For Tencent CloudBase operations, must prioritize using CloudBase MCP tools
|
|
187139
|
-
2.
|
|
187140
|
-
3. **Project Understanding**: First read current project's README.md, follow project instructions for development
|
|
187924
|
+
2. **Project Understanding**: First read current project's README.md, follow project instructions for development. Exception: for existing applications where the missing work is clearly in known backend integration files, skip README-first and go straight to those files.
|
|
187141
187925
|
4. **Directory Standards**: Before outputting project code in current directory, first check current directory files
|
|
187142
187926
|
5. **Development Order**: When developing, prioritize frontend first, then backend, ensuring frontend interface and interaction logic are completed first, then implement backend business logic
|
|
187143
|
-
6. **⚠️ UI Design Rules Mandatory Application**: When tasks involve generating pages, interfaces, components, styles, or any frontend visual elements, **MUST FIRST explicitly read the file \`rules/ui-design/rule.md\` using file reading tools**, then strictly follow the rule file, ensuring generated interfaces have distinctive aesthetic styles and high-quality visual design, avoiding generic AI aesthetics. **You MUST output the design specification before writing any UI code.**
|
|
187927
|
+
6. **⚠️ UI Design Rules Mandatory Application**: When tasks involve generating pages, interfaces, components, styles, or any frontend visual elements, **MUST FIRST explicitly read the file \`rules/ui-design/rule.md\` using file reading tools**, then strictly follow the rule file, ensuring generated interfaces have distinctive aesthetic styles and high-quality visual design, avoiding generic AI aesthetics. **You MUST output the design specification before writing any UI code.** Exception: for existing applications where page structure is already fixed and only backend integration is missing, do not trigger the UI-design workflow.
|
|
187144
187928
|
7. **Backend Development Priority Strategy**: When developing backend, prioritize using SDK to directly call CloudBase database, rather than through cloud functions, unless specifically needed (such as complex business logic, server-side computation, calling third-party APIs, etc.)
|
|
187145
187929
|
8. **Deployment Order**: When there are backend dependencies, prioritize deploying backend before previewing frontend
|
|
187146
187930
|
9. **Interactive Confirmation**: Clarify requirements when needed, and confirm before executing high-risk operations
|
|
187147
187931
|
10. **Real-time Communication**: Use CloudBase real-time database watch capability
|
|
187148
187932
|
11. **⚠️ Authentication Rules**: When users develop projects, if user login authentication is needed, must use built-in authentication functions, must strictly distinguish authentication methods by platform
|
|
187149
187933
|
- **Web Projects**: **MUST use CloudBase Web SDK built-in authentication** (e.g., \`auth.toDefaultLoginPage()\`), refer to \`rules/auth-web/rule.md\`
|
|
187934
|
+
- **Username-style Web accounts**: Use \`auth.signUp({ username, password })\` for registration and \`auth.signInWithPassword({ username, password })\` for login. Do not use \`signInWithEmailAndPassword\` / \`signUpWithEmailAndPassword\` when the identifier is not an email address.
|
|
187935
|
+
- **Web initialization**: Only set \`accessKey\` when you have a real Publishable Key. Never set \`accessKey: envId\` or any made-up string.
|
|
187150
187936
|
- **Mini Program Projects**: **Naturally login-free**, get \`wxContext.OPENID\` in cloud functions, refer to \`rules/auth-wechat/rule.md\`
|
|
187151
187937
|
|
|
187152
187938
|
## Development Workflow
|
|
187153
187939
|
|
|
187154
187940
|
### Development
|
|
187155
187941
|
|
|
187156
|
-
1.
|
|
187157
|
-
-
|
|
187158
|
-
- For
|
|
187159
|
-
-
|
|
187160
|
-
- For UniApp projects: Use \`downloadTemplate\` with \`template="uniapp"\`
|
|
187161
|
-
- **Only proceed with manual file creation if template download fails or user explicitly requests manual creation**
|
|
187162
|
-
- If unable to download to current directory, can use scripts to copy, note that hidden files also need to be copied
|
|
187942
|
+
1. **Project Bootstrap Discipline**:
|
|
187943
|
+
- Prefer the existing project structure and scripts when they already exist
|
|
187944
|
+
- For new projects, keep the initial structure consistent and avoid mixing unrelated frameworks in one workspace
|
|
187945
|
+
- If you bootstrap or copy an existing starter manually, hidden files and project config must be carried over as well
|
|
187163
187946
|
|
|
187164
187947
|
2. **⚠️ UI Design Document Reading (MANDATORY)**:
|
|
187165
187948
|
- **Before generating ANY page, interface, component, or style, MUST FIRST explicitly read the file \`rules/ui-design/rule.md\` using file reading tools**
|
|
@@ -187187,7 +187970,7 @@ If remote links are needed in the application, can continue to call uploadFile t
|
|
|
187187
187970
|
|
|
187188
187971
|
2. **CloudRun Deployment Process**: For non-cloud function backend services (Java, Go, PHP, Python, Node.js, etc.), use manageCloudRun tool for deployment. Ensure backend code supports CORS, prepare Dockerfile, then call manageCloudRun for containerized deployment. For details, refer to \`rules/cloudrun-development/rule.md\`
|
|
187189
187972
|
|
|
187190
|
-
3. **Static Hosting Deployment Process**: Deploy using uploadFiles tool for static hosting only. If the task needs a COS object that must be queried or polled with the storage SDK, use \`manageStorage\` / \`queryStorage\` instead. After deployment, remind users that CDN has a few minutes of cache. Can generate markdown format access links with random queryString. For details, refer to \`rules/web-development/rule.md\`
|
|
187973
|
+
3. **Static Hosting Deployment Process**: Deploy using uploadFiles tool for static hosting only. If the task needs a COS object that must be queried or polled with the storage SDK, use \`manageStorage\` / \`queryStorage\` instead. For local verification or unfinished application repairs, do not deploy hosting unless the user explicitly asks for deployment. After deployment, remind users that CDN has a few minutes of cache. Can generate markdown format access links with random queryString. For details, refer to \`rules/web-development/rule.md\`
|
|
187191
187974
|
|
|
187192
187975
|
### Documentation Generation Rules
|
|
187193
187976
|
|
|
@@ -187246,8 +188029,7 @@ To ensure development quality, recommend completing the following checks before
|
|
|
187246
188029
|
|
|
187247
188030
|
### Recommended Steps
|
|
187248
188031
|
0. **[ ] Environment Check**: Call \`envQuery\` tool to check CloudBase environment status (applies to all interactions)
|
|
187249
|
-
1. **[ ]
|
|
187250
|
-
2. **[ ] Scenario Identification**: Clearly identify what type of project this is (Web/Mini Program/Database/UI)
|
|
188032
|
+
1. **[ ] Scenario Identification**: Clearly identify what type of project this is (Web/Mini Program/Database/UI)
|
|
187251
188033
|
3. **[ ] Core Capability Confirmation**: Confirm all four core capabilities have been considered
|
|
187252
188034
|
- UI Design: Have you explicitly read the file \`rules/ui-design/rule.md\` using file reading tools?
|
|
187253
188035
|
- Database + Authentication: Have you referred to corresponding authentication and database skills?
|
|
@@ -187261,7 +188043,6 @@ To ensure development quality, recommend completing the following checks before
|
|
|
187261
188043
|
6. **[ ] Rule Execution**: Strictly follow core capability requirements and relevant rule files for development
|
|
187262
188044
|
|
|
187263
188045
|
### ⚠️ Common Issues to Avoid
|
|
187264
|
-
- **❌ DO NOT manually create project files** - Always use \`downloadTemplate\` tool first for new projects
|
|
187265
188046
|
- **❌ DO NOT skip reading UI design document** - Must explicitly read \`rules/ui-design/rule.md\` file before generating any UI code
|
|
187266
188047
|
- Avoid skipping core capabilities and starting development directly
|
|
187267
188048
|
- Avoid mixing APIs and authentication methods from different platforms
|
|
@@ -190997,7 +191778,7 @@ ${envIdSection}
|
|
|
190997
191778
|
## 环境信息
|
|
190998
191779
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
190999
191780
|
- Node.js版本: ${process.version}
|
|
191000
|
-
- MCP 版本:${process.env.npm_package_version || "2.
|
|
191781
|
+
- MCP 版本:${process.env.npm_package_version || "2.17.0-beta.2" || 0}
|
|
191001
191782
|
- 系统架构: ${os_1.default.arch()}
|
|
191002
191783
|
- 时间: ${new Date().toISOString()}
|
|
191003
191784
|
- 请求ID: ${requestId}
|
|
@@ -191043,6 +191824,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
191043
191824
|
// 执行原始处理函数
|
|
191044
191825
|
const result = await handler(args);
|
|
191045
191826
|
success = true;
|
|
191827
|
+
requestId = extractRequestIdFromToolResult(result);
|
|
191046
191828
|
const duration = Date.now() - startTime;
|
|
191047
191829
|
(0, logger_js_1.debug)(`工具执行成功: ${name}`, { duration });
|
|
191048
191830
|
if (!isTestEnvironment) {
|
|
@@ -191106,6 +191888,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
191106
191888
|
(0, telemetry_js_1.reportToolCall)({
|
|
191107
191889
|
toolName: name,
|
|
191108
191890
|
success,
|
|
191891
|
+
requestId,
|
|
191109
191892
|
duration,
|
|
191110
191893
|
error: errorMessage,
|
|
191111
191894
|
inputParams: sanitizeArgs(args), // 添加入参上报
|
|
@@ -191119,6 +191902,38 @@ function createWrappedHandler(name, handler, server) {
|
|
|
191119
191902
|
}
|
|
191120
191903
|
};
|
|
191121
191904
|
}
|
|
191905
|
+
function extractRequestIdFromToolResult(result) {
|
|
191906
|
+
if (!result || typeof result !== 'object') {
|
|
191907
|
+
return undefined;
|
|
191908
|
+
}
|
|
191909
|
+
if (typeof result.requestId === 'string' && result.requestId.length > 0) {
|
|
191910
|
+
return result.requestId;
|
|
191911
|
+
}
|
|
191912
|
+
if (typeof result.RequestId === 'string' && result.RequestId.length > 0) {
|
|
191913
|
+
return result.RequestId;
|
|
191914
|
+
}
|
|
191915
|
+
const content = Array.isArray(result.content) ? result.content : [];
|
|
191916
|
+
for (const item of content) {
|
|
191917
|
+
if (!item || item.type !== 'text' || typeof item.text !== 'string') {
|
|
191918
|
+
continue;
|
|
191919
|
+
}
|
|
191920
|
+
try {
|
|
191921
|
+
const parsed = JSON.parse(item.text);
|
|
191922
|
+
if (parsed && typeof parsed === 'object') {
|
|
191923
|
+
if (typeof parsed.requestId === 'string' && parsed.requestId.length > 0) {
|
|
191924
|
+
return parsed.requestId;
|
|
191925
|
+
}
|
|
191926
|
+
if (typeof parsed.RequestId === 'string' && parsed.RequestId.length > 0) {
|
|
191927
|
+
return parsed.RequestId;
|
|
191928
|
+
}
|
|
191929
|
+
}
|
|
191930
|
+
}
|
|
191931
|
+
catch {
|
|
191932
|
+
// Ignore non-JSON text payloads.
|
|
191933
|
+
}
|
|
191934
|
+
}
|
|
191935
|
+
return undefined;
|
|
191936
|
+
}
|
|
191122
191937
|
/**
|
|
191123
191938
|
* 包装 MCP Server 的 registerTool 方法,添加数据上报功能和条件注册
|
|
191124
191939
|
* @param server MCP Server 实例
|
|
@@ -198966,20 +199781,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
|
198966
199781
|
exports.registerDatabaseTools = registerDatabaseTools;
|
|
198967
199782
|
const zod_1 = __webpack_require__(21614);
|
|
198968
199783
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
199784
|
+
const logger_js_1 = __webpack_require__(13039);
|
|
198969
199785
|
const CATEGORY = "NoSQL database";
|
|
198970
199786
|
const COLLECTION_READY_TIMEOUT_MS = 10000;
|
|
198971
199787
|
const COLLECTION_READY_POLL_INTERVAL_MS = 500;
|
|
198972
199788
|
/** Convert object values to JSON strings for API calls */
|
|
198973
199789
|
const toJSONString = (v) => typeof v === "object" && v !== null ? JSON.stringify(v) : v;
|
|
198974
|
-
// 获取数据库实例ID
|
|
198975
|
-
async function getDatabaseInstanceId(getManager) {
|
|
198976
|
-
const cloudbase = await getManager();
|
|
198977
|
-
const { EnvInfo } = await cloudbase.env.getEnvInfo();
|
|
198978
|
-
if (!EnvInfo?.Databases?.[0]?.InstanceId) {
|
|
198979
|
-
throw new Error("无法获取数据库实例ID");
|
|
198980
|
-
}
|
|
198981
|
-
return EnvInfo.Databases[0].InstanceId;
|
|
198982
|
-
}
|
|
198983
199790
|
function delay(ms) {
|
|
198984
199791
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
198985
199792
|
}
|
|
@@ -199055,6 +199862,83 @@ function withCollectionName(collectionName, payload) {
|
|
|
199055
199862
|
collectionName,
|
|
199056
199863
|
};
|
|
199057
199864
|
}
|
|
199865
|
+
function logNoSqlLatency(toolName, phase, details) {
|
|
199866
|
+
(0, logger_js_1.debug)("[nosql-latency]", {
|
|
199867
|
+
toolName,
|
|
199868
|
+
phase,
|
|
199869
|
+
...details,
|
|
199870
|
+
});
|
|
199871
|
+
}
|
|
199872
|
+
function isInvalidInstanceIdError(error) {
|
|
199873
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
199874
|
+
return /(instance.?id|tag).*(invalid|错误|不存在|not exist|not found)|invalid.*(instance.?id|tag)/i.test(message);
|
|
199875
|
+
}
|
|
199876
|
+
async function resolveNoSqlInstanceId(options) {
|
|
199877
|
+
const startedAt = Date.now();
|
|
199878
|
+
const resolved = await (0, cloudbase_manager_js_1.getDatabaseInstanceId)({
|
|
199879
|
+
instanceId: options.instanceIdOverride,
|
|
199880
|
+
cloudBaseOptions: options.cloudBaseOptions,
|
|
199881
|
+
cloudbase: options.cloudbase,
|
|
199882
|
+
});
|
|
199883
|
+
logNoSqlLatency(options.toolName, "resolveInstanceId", {
|
|
199884
|
+
collectionName: options.collectionName,
|
|
199885
|
+
durationMs: Date.now() - startedAt,
|
|
199886
|
+
instanceIdSource: resolved.source,
|
|
199887
|
+
cacheKey: resolved.cacheKey,
|
|
199888
|
+
});
|
|
199889
|
+
return resolved;
|
|
199890
|
+
}
|
|
199891
|
+
async function callNoSqlContentApi(options) {
|
|
199892
|
+
const resolvedInstance = await resolveNoSqlInstanceId({
|
|
199893
|
+
toolName: options.toolName,
|
|
199894
|
+
cloudbase: options.cloudbase,
|
|
199895
|
+
cloudBaseOptions: options.cloudBaseOptions,
|
|
199896
|
+
instanceIdOverride: options.instanceIdOverride,
|
|
199897
|
+
collectionName: options.collectionName,
|
|
199898
|
+
});
|
|
199899
|
+
const startedAt = Date.now();
|
|
199900
|
+
try {
|
|
199901
|
+
const result = await options.cloudbase
|
|
199902
|
+
.commonService("tcb", "2018-06-08")
|
|
199903
|
+
.call({
|
|
199904
|
+
Action: options.action,
|
|
199905
|
+
Param: {
|
|
199906
|
+
...options.param,
|
|
199907
|
+
TableName: options.collectionName,
|
|
199908
|
+
Tag: resolvedInstance.instanceId,
|
|
199909
|
+
},
|
|
199910
|
+
});
|
|
199911
|
+
logNoSqlLatency(options.toolName, "cloudApiCall", {
|
|
199912
|
+
collectionName: options.collectionName,
|
|
199913
|
+
action: options.action,
|
|
199914
|
+
durationMs: Date.now() - startedAt,
|
|
199915
|
+
instanceIdSource: resolvedInstance.source,
|
|
199916
|
+
requestId: result?.RequestId,
|
|
199917
|
+
});
|
|
199918
|
+
return result;
|
|
199919
|
+
}
|
|
199920
|
+
catch (error) {
|
|
199921
|
+
logNoSqlLatency(options.toolName, "cloudApiError", {
|
|
199922
|
+
collectionName: options.collectionName,
|
|
199923
|
+
action: options.action,
|
|
199924
|
+
durationMs: Date.now() - startedAt,
|
|
199925
|
+
instanceIdSource: resolvedInstance.source,
|
|
199926
|
+
message: error instanceof Error ? error.message : String(error),
|
|
199927
|
+
});
|
|
199928
|
+
if (!options.instanceIdOverride &&
|
|
199929
|
+
resolvedInstance.cacheKey &&
|
|
199930
|
+
isInvalidInstanceIdError(error)) {
|
|
199931
|
+
(0, cloudbase_manager_js_1.invalidateDatabaseInstanceIdCache)({
|
|
199932
|
+
cacheKey: resolvedInstance.cacheKey,
|
|
199933
|
+
});
|
|
199934
|
+
logNoSqlLatency(options.toolName, "invalidateInstanceIdCache", {
|
|
199935
|
+
collectionName: options.collectionName,
|
|
199936
|
+
cacheKey: resolvedInstance.cacheKey,
|
|
199937
|
+
});
|
|
199938
|
+
}
|
|
199939
|
+
throw error;
|
|
199940
|
+
}
|
|
199941
|
+
}
|
|
199058
199942
|
async function waitForCollectionReady({ cloudbase, collectionName, logger, timeoutMs = COLLECTION_READY_TIMEOUT_MS, pollIntervalMs = COLLECTION_READY_POLL_INTERVAL_MS, }) {
|
|
199059
199943
|
const startedAt = Date.now();
|
|
199060
199944
|
const deadline = startedAt + timeoutMs;
|
|
@@ -199112,7 +199996,7 @@ function registerDatabaseTools(server) {
|
|
|
199112
199996
|
// readNoSqlDatabaseStructure
|
|
199113
199997
|
server.registerTool?.("readNoSqlDatabaseStructure", {
|
|
199114
199998
|
title: "读取 NoSQL 数据库结构",
|
|
199115
|
-
description: "读取 NoSQL
|
|
199999
|
+
description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。",
|
|
199116
200000
|
inputSchema: {
|
|
199117
200001
|
action: zod_1.z.enum([
|
|
199118
200002
|
"listCollections",
|
|
@@ -199121,10 +200005,10 @@ function registerDatabaseTools(server) {
|
|
|
199121
200005
|
"listIndexes",
|
|
199122
200006
|
"checkIndex",
|
|
199123
200007
|
]).describe(`listCollections: 列出集合列表
|
|
199124
|
-
describeCollection:
|
|
200008
|
+
describeCollection: 描述集合详情(会返回索引摘要)
|
|
199125
200009
|
checkCollection: 检查集合是否存在
|
|
199126
|
-
listIndexes:
|
|
199127
|
-
checkIndex:
|
|
200010
|
+
listIndexes: 列出指定集合的索引列表
|
|
200011
|
+
checkIndex: 检查指定索引是否存在`),
|
|
199128
200012
|
limit: zod_1.z
|
|
199129
200013
|
.number()
|
|
199130
200014
|
.optional()
|
|
@@ -199260,38 +200144,42 @@ checkIndex: 检查索引是否存在`),
|
|
|
199260
200144
|
// writeNoSqlDatabaseStructure
|
|
199261
200145
|
server.registerTool?.("writeNoSqlDatabaseStructure", {
|
|
199262
200146
|
title: "修改 NoSQL 数据库结构",
|
|
199263
|
-
description: "修改 NoSQL
|
|
200147
|
+
description: "修改 NoSQL 数据库结构,支持创建/删除集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。",
|
|
199264
200148
|
inputSchema: {
|
|
199265
200149
|
action: zod_1.z.enum([
|
|
199266
200150
|
"createCollection",
|
|
199267
200151
|
"updateCollection",
|
|
199268
200152
|
"deleteCollection",
|
|
199269
200153
|
]).describe(`createCollection: 创建集合
|
|
199270
|
-
updateCollection:
|
|
200154
|
+
updateCollection: 更新集合配置;添加索引请传 updateOptions.CreateIndexes,删除索引请传 updateOptions.DropIndexes
|
|
199271
200155
|
deleteCollection: 删除集合`),
|
|
199272
200156
|
collectionName: zod_1.z.string().describe("集合名称"),
|
|
199273
200157
|
updateOptions: zod_1.z
|
|
199274
200158
|
.object({
|
|
199275
200159
|
CreateIndexes: zod_1.z
|
|
199276
200160
|
.array(zod_1.z.object({
|
|
199277
|
-
IndexName: zod_1.z.string(),
|
|
200161
|
+
IndexName: zod_1.z.string().describe("要创建的索引名称"),
|
|
199278
200162
|
MgoKeySchema: zod_1.z.object({
|
|
199279
|
-
MgoIsUnique: zod_1.z.boolean(),
|
|
200163
|
+
MgoIsUnique: zod_1.z.boolean().describe("是否唯一索引"),
|
|
199280
200164
|
MgoIndexKeys: zod_1.z.array(zod_1.z.object({
|
|
199281
|
-
Name: zod_1.z.string(),
|
|
199282
|
-
Direction: zod_1.z
|
|
199283
|
-
|
|
199284
|
-
|
|
200165
|
+
Name: zod_1.z.string().describe("索引字段名"),
|
|
200166
|
+
Direction: zod_1.z
|
|
200167
|
+
.string()
|
|
200168
|
+
.describe("索引方向,通常 1 表示升序,-1 表示降序"),
|
|
200169
|
+
})).describe("索引字段列表,支持单字段或复合索引"),
|
|
200170
|
+
}).describe("待创建索引的字段与约束配置"),
|
|
199285
200171
|
}))
|
|
199286
|
-
.optional()
|
|
200172
|
+
.optional()
|
|
200173
|
+
.describe("要添加的索引列表"),
|
|
199287
200174
|
DropIndexes: zod_1.z
|
|
199288
200175
|
.array(zod_1.z.object({
|
|
199289
|
-
IndexName: zod_1.z.string(),
|
|
200176
|
+
IndexName: zod_1.z.string().describe("要删除的索引名称"),
|
|
199290
200177
|
}))
|
|
199291
|
-
.optional()
|
|
200178
|
+
.optional()
|
|
200179
|
+
.describe("要删除的索引列表"),
|
|
199292
200180
|
})
|
|
199293
200181
|
.optional()
|
|
199294
|
-
.describe("更新选项(updateCollection 时使用)"),
|
|
200182
|
+
.describe("更新选项(updateCollection 时使用)。CreateIndexes 用于添加索引,DropIndexes 用于删除索引。"),
|
|
199295
200183
|
},
|
|
199296
200184
|
annotations: {
|
|
199297
200185
|
readOnlyHint: false,
|
|
@@ -199303,6 +200191,24 @@ deleteCollection: 删除集合`),
|
|
|
199303
200191
|
}, async ({ action, collectionName, updateOptions }) => {
|
|
199304
200192
|
const cloudbase = await getManager();
|
|
199305
200193
|
if (action === "createCollection") {
|
|
200194
|
+
const existsResult = await cloudbase.database.checkCollectionExists(collectionName);
|
|
200195
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, existsResult);
|
|
200196
|
+
if (existsResult.Exists) {
|
|
200197
|
+
return {
|
|
200198
|
+
content: [
|
|
200199
|
+
{
|
|
200200
|
+
type: "text",
|
|
200201
|
+
text: JSON.stringify(withCollectionName(collectionName, {
|
|
200202
|
+
success: true,
|
|
200203
|
+
action,
|
|
200204
|
+
requestId: existsResult.RequestId,
|
|
200205
|
+
message: "集合已存在,无需重复创建",
|
|
200206
|
+
exists: true,
|
|
200207
|
+
}), null, 2),
|
|
200208
|
+
},
|
|
200209
|
+
],
|
|
200210
|
+
};
|
|
200211
|
+
}
|
|
199306
200212
|
const result = await cloudbase.database.createCollection(collectionName);
|
|
199307
200213
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
199308
200214
|
await waitForCollectionReady({
|
|
@@ -199373,6 +200279,10 @@ deleteCollection: 删除集合`),
|
|
|
199373
200279
|
description: "查询并获取 NoSQL 数据库数据记录",
|
|
199374
200280
|
inputSchema: {
|
|
199375
200281
|
collectionName: zod_1.z.string().describe("集合名称"),
|
|
200282
|
+
instanceId: zod_1.z
|
|
200283
|
+
.string()
|
|
200284
|
+
.optional()
|
|
200285
|
+
.describe("可选:显式指定数据库实例ID;未传时会自动解析并缓存"),
|
|
199376
200286
|
query: zod_1.z
|
|
199377
200287
|
.union([zod_1.z.object({}).passthrough(), zod_1.z.string()])
|
|
199378
200288
|
.optional()
|
|
@@ -199401,24 +200311,37 @@ deleteCollection: 删除集合`),
|
|
|
199401
200311
|
openWorldHint: true,
|
|
199402
200312
|
category: CATEGORY,
|
|
199403
200313
|
},
|
|
199404
|
-
}, async ({ collectionName, query, projection, sort, limit, offset }) => {
|
|
200314
|
+
}, async ({ collectionName, instanceId, query, projection, sort, limit, offset }) => {
|
|
200315
|
+
const managerStartedAt = Date.now();
|
|
199405
200316
|
const cloudbase = await getManager();
|
|
199406
|
-
|
|
200317
|
+
logNoSqlLatency("readNoSqlDatabaseContent", "getManager", {
|
|
200318
|
+
collectionName,
|
|
200319
|
+
durationMs: Date.now() - managerStartedAt,
|
|
200320
|
+
});
|
|
199407
200321
|
const normalizedSort = normalizeSortInput(sort);
|
|
199408
|
-
const result = await
|
|
199409
|
-
|
|
199410
|
-
|
|
199411
|
-
|
|
200322
|
+
const result = await callNoSqlContentApi({
|
|
200323
|
+
toolName: "readNoSqlDatabaseContent",
|
|
200324
|
+
action: "QueryRecords",
|
|
200325
|
+
collectionName,
|
|
200326
|
+
cloudbase,
|
|
200327
|
+
cloudBaseOptions,
|
|
200328
|
+
instanceIdOverride: instanceId,
|
|
200329
|
+
param: {
|
|
199412
200330
|
MgoQuery: toJSONString(query),
|
|
199413
200331
|
MgoProjection: toJSONString(projection),
|
|
199414
200332
|
MgoSort: normalizedSort,
|
|
199415
200333
|
MgoLimit: limit ?? 100,
|
|
199416
200334
|
MgoOffset: offset,
|
|
199417
|
-
Tag: instanceId,
|
|
199418
200335
|
},
|
|
199419
200336
|
});
|
|
199420
200337
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
200338
|
+
const parseStartedAt = Date.now();
|
|
199421
200339
|
const documents = normalizeNoSqlDocuments(result.Data);
|
|
200340
|
+
logNoSqlLatency("readNoSqlDatabaseContent", "parseResult", {
|
|
200341
|
+
collectionName,
|
|
200342
|
+
durationMs: Date.now() - parseStartedAt,
|
|
200343
|
+
documentCount: documents.length,
|
|
200344
|
+
});
|
|
199422
200345
|
return {
|
|
199423
200346
|
content: [
|
|
199424
200347
|
{
|
|
@@ -199440,12 +200363,16 @@ deleteCollection: 删除集合`),
|
|
|
199440
200363
|
// writeNoSqlDatabaseContent
|
|
199441
200364
|
server.registerTool?.("writeNoSqlDatabaseContent", {
|
|
199442
200365
|
title: "修改 NoSQL 数据库数据记录",
|
|
199443
|
-
description: "修改 NoSQL 数据库数据记录。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 $set/$inc/$push 等更新操作符;如果直接传 { field: value } 这类普通对象,底层会把它当作替换内容,存在覆盖整条文档的风险。更新嵌套对象中的某个字段时必须使用点号路径(如 { \"$set\": { \"address.city\": \"shenzhen\" } }),若写成 { \"$set\": { \"address\": { \"city\": \"shenzhen\" } } } 则整个 address
|
|
200366
|
+
description: "修改 NoSQL 数据库数据记录。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 $set/$inc/$push 等更新操作符;如果直接传 { field: value } 这类普通对象,底层会把它当作替换内容,存在覆盖整条文档的风险。更新嵌套对象中的某个字段时必须使用点号路径(如 { \"$set\": { \"address.city\": \"shenzhen\" } }),若写成 { \"$set\": { \"address\": { \"city\": \"shenzhen\" } } } 则整个 address 对象会被替换,同级其他字段将丢失。若集合中的角色/档案文档会在前端通过 `db.collection(...).doc(uid)` 读取,请确保文档 `_id` 就是该 `uid`;不要用 `query={\"uid\":\"...\"}` + `upsert=true` 去更新 `users` / `profiles`,否则经常会生成一个不同的 `_id`,导致后续 `doc(uid)` 读取命中不到。",
|
|
199444
200367
|
inputSchema: {
|
|
199445
200368
|
action: zod_1.z
|
|
199446
200369
|
.enum(["insert", "update", "delete"])
|
|
199447
200370
|
.describe(`insert: 插入数据(新增文档)\nupdate: 更新数据\ndelete: 删除数据`),
|
|
199448
200371
|
collectionName: zod_1.z.string().describe("集合名称"),
|
|
200372
|
+
instanceId: zod_1.z
|
|
200373
|
+
.string()
|
|
200374
|
+
.optional()
|
|
200375
|
+
.describe("可选:显式指定数据库实例ID;未传时会自动解析并缓存"),
|
|
199449
200376
|
documents: zod_1.z
|
|
199450
200377
|
.array(zod_1.z.object({}).passthrough())
|
|
199451
200378
|
.optional()
|
|
@@ -199474,16 +200401,18 @@ deleteCollection: 删除集合`),
|
|
|
199474
200401
|
openWorldHint: true,
|
|
199475
200402
|
category: CATEGORY,
|
|
199476
200403
|
},
|
|
199477
|
-
}, async ({ action, collectionName, documents, query, update, isMulti, upsert, }) => {
|
|
200404
|
+
}, async ({ action, collectionName, instanceId, documents, query, update, isMulti, upsert, }) => {
|
|
199478
200405
|
if (action === "insert") {
|
|
199479
200406
|
if (!documents) {
|
|
199480
200407
|
throw new Error("insert 操作时必须提供 documents");
|
|
199481
200408
|
}
|
|
199482
200409
|
const text = await insertDocuments({
|
|
199483
200410
|
collectionName,
|
|
200411
|
+
instanceId,
|
|
199484
200412
|
documents,
|
|
199485
200413
|
getManager,
|
|
199486
200414
|
logger,
|
|
200415
|
+
cloudBaseOptions,
|
|
199487
200416
|
});
|
|
199488
200417
|
return {
|
|
199489
200418
|
content: [
|
|
@@ -199503,12 +200432,14 @@ deleteCollection: 删除集合`),
|
|
|
199503
200432
|
}
|
|
199504
200433
|
const text = await updateDocuments({
|
|
199505
200434
|
collectionName,
|
|
200435
|
+
instanceId,
|
|
199506
200436
|
query,
|
|
199507
200437
|
update,
|
|
199508
200438
|
isMulti,
|
|
199509
200439
|
upsert,
|
|
199510
200440
|
getManager,
|
|
199511
200441
|
logger,
|
|
200442
|
+
cloudBaseOptions,
|
|
199512
200443
|
});
|
|
199513
200444
|
return {
|
|
199514
200445
|
content: [
|
|
@@ -199525,10 +200456,12 @@ deleteCollection: 删除集合`),
|
|
|
199525
200456
|
}
|
|
199526
200457
|
const text = await deleteDocuments({
|
|
199527
200458
|
collectionName,
|
|
200459
|
+
instanceId,
|
|
199528
200460
|
query,
|
|
199529
200461
|
isMulti,
|
|
199530
200462
|
getManager,
|
|
199531
200463
|
logger,
|
|
200464
|
+
cloudBaseOptions,
|
|
199532
200465
|
});
|
|
199533
200466
|
return {
|
|
199534
200467
|
content: [
|
|
@@ -199542,16 +200475,24 @@ deleteCollection: 删除集合`),
|
|
|
199542
200475
|
throw new Error(`不支持的操作类型: ${action}`);
|
|
199543
200476
|
});
|
|
199544
200477
|
}
|
|
199545
|
-
async function insertDocuments({ collectionName, documents, getManager, logger, }) {
|
|
200478
|
+
async function insertDocuments({ collectionName, instanceId, documents, getManager, logger, cloudBaseOptions, }) {
|
|
200479
|
+
const managerStartedAt = Date.now();
|
|
199546
200480
|
const cloudbase = await getManager();
|
|
199547
|
-
|
|
200481
|
+
logNoSqlLatency("writeNoSqlDatabaseContent", "getManager", {
|
|
200482
|
+
collectionName,
|
|
200483
|
+
action: "insert",
|
|
200484
|
+
durationMs: Date.now() - managerStartedAt,
|
|
200485
|
+
});
|
|
199548
200486
|
const docsAsStrings = documents.map((doc) => JSON.stringify(doc));
|
|
199549
|
-
const result = await
|
|
199550
|
-
|
|
199551
|
-
|
|
199552
|
-
|
|
200487
|
+
const result = await callNoSqlContentApi({
|
|
200488
|
+
toolName: "writeNoSqlDatabaseContent",
|
|
200489
|
+
action: "PutItem",
|
|
200490
|
+
collectionName,
|
|
200491
|
+
cloudbase,
|
|
200492
|
+
cloudBaseOptions,
|
|
200493
|
+
instanceIdOverride: instanceId,
|
|
200494
|
+
param: {
|
|
199553
200495
|
MgoDocs: docsAsStrings,
|
|
199554
|
-
Tag: instanceId,
|
|
199555
200496
|
},
|
|
199556
200497
|
});
|
|
199557
200498
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
|
|
@@ -199565,18 +200506,31 @@ async function insertDocuments({ collectionName, documents, getManager, logger,
|
|
|
199565
200506
|
message: "文档插入成功",
|
|
199566
200507
|
}), null, 2);
|
|
199567
200508
|
}
|
|
199568
|
-
async function updateDocuments({ collectionName, query, update, isMulti, upsert, getManager, logger, }) {
|
|
200509
|
+
async function updateDocuments({ collectionName, instanceId, query, update, isMulti, upsert, getManager, logger, cloudBaseOptions, }) {
|
|
200510
|
+
const managerStartedAt = Date.now();
|
|
199569
200511
|
const cloudbase = await getManager();
|
|
199570
|
-
|
|
199571
|
-
|
|
199572
|
-
|
|
199573
|
-
|
|
199574
|
-
|
|
200512
|
+
logNoSqlLatency("writeNoSqlDatabaseContent", "getManager", {
|
|
200513
|
+
collectionName,
|
|
200514
|
+
action: "update",
|
|
200515
|
+
durationMs: Date.now() - managerStartedAt,
|
|
200516
|
+
});
|
|
200517
|
+
const authLinkedDocWarning = buildAuthLinkedDocWarning({
|
|
200518
|
+
collectionName,
|
|
200519
|
+
query,
|
|
200520
|
+
upsert,
|
|
200521
|
+
});
|
|
200522
|
+
const result = await callNoSqlContentApi({
|
|
200523
|
+
toolName: "writeNoSqlDatabaseContent",
|
|
200524
|
+
action: "UpdateItem",
|
|
200525
|
+
collectionName,
|
|
200526
|
+
cloudbase,
|
|
200527
|
+
cloudBaseOptions,
|
|
200528
|
+
instanceIdOverride: instanceId,
|
|
200529
|
+
param: {
|
|
199575
200530
|
MgoQuery: toJSONString(query),
|
|
199576
200531
|
MgoUpdate: toJSONString(update),
|
|
199577
200532
|
MgoIsMulti: isMulti,
|
|
199578
200533
|
MgoUpsert: upsert,
|
|
199579
|
-
Tag: instanceId,
|
|
199580
200534
|
},
|
|
199581
200535
|
});
|
|
199582
200536
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
|
|
@@ -199586,19 +200540,56 @@ async function updateDocuments({ collectionName, query, update, isMulti, upsert,
|
|
|
199586
200540
|
modifiedCount: result.ModifiedNum,
|
|
199587
200541
|
matchedCount: result.MatchedNum,
|
|
199588
200542
|
upsertedId: result.UpsertedId,
|
|
199589
|
-
|
|
200543
|
+
...(authLinkedDocWarning ? { warning: authLinkedDocWarning } : {}),
|
|
200544
|
+
message: authLinkedDocWarning ? `文档更新成功;${authLinkedDocWarning}` : "文档更新成功",
|
|
199590
200545
|
}), null, 2);
|
|
199591
200546
|
}
|
|
199592
|
-
|
|
200547
|
+
function tryParseObjectLike(value) {
|
|
200548
|
+
if (typeof value === "string") {
|
|
200549
|
+
try {
|
|
200550
|
+
const parsed = JSON.parse(value);
|
|
200551
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
200552
|
+
? parsed
|
|
200553
|
+
: null;
|
|
200554
|
+
}
|
|
200555
|
+
catch {
|
|
200556
|
+
return null;
|
|
200557
|
+
}
|
|
200558
|
+
}
|
|
200559
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
200560
|
+
? value
|
|
200561
|
+
: null;
|
|
200562
|
+
}
|
|
200563
|
+
function buildAuthLinkedDocWarning({ collectionName, query, upsert, }) {
|
|
200564
|
+
if (!upsert)
|
|
200565
|
+
return null;
|
|
200566
|
+
if (!["users", "profiles", "user_roles", "userProfiles"].includes(collectionName))
|
|
200567
|
+
return null;
|
|
200568
|
+
const queryObject = tryParseObjectLike(query);
|
|
200569
|
+
if (!queryObject)
|
|
200570
|
+
return null;
|
|
200571
|
+
if (!("uid" in queryObject) && !("userId" in queryObject))
|
|
200572
|
+
return null;
|
|
200573
|
+
return "若前端会用 doc(uid) 读取该集合,请改为直接创建 `_id = uid` 的文档;基于 uid 查询再 upsert 往往会生成不同的 `_id`,导致后续 doc(uid) 读取失败。";
|
|
200574
|
+
}
|
|
200575
|
+
async function deleteDocuments({ collectionName, instanceId, query, isMulti, getManager, logger, cloudBaseOptions, }) {
|
|
200576
|
+
const managerStartedAt = Date.now();
|
|
199593
200577
|
const cloudbase = await getManager();
|
|
199594
|
-
|
|
199595
|
-
|
|
199596
|
-
|
|
199597
|
-
|
|
199598
|
-
|
|
200578
|
+
logNoSqlLatency("writeNoSqlDatabaseContent", "getManager", {
|
|
200579
|
+
collectionName,
|
|
200580
|
+
action: "delete",
|
|
200581
|
+
durationMs: Date.now() - managerStartedAt,
|
|
200582
|
+
});
|
|
200583
|
+
const result = await callNoSqlContentApi({
|
|
200584
|
+
toolName: "writeNoSqlDatabaseContent",
|
|
200585
|
+
action: "DeleteItem",
|
|
200586
|
+
collectionName,
|
|
200587
|
+
cloudbase,
|
|
200588
|
+
cloudBaseOptions,
|
|
200589
|
+
instanceIdOverride: instanceId,
|
|
200590
|
+
param: {
|
|
199599
200591
|
MgoQuery: toJSONString(query),
|
|
199600
200592
|
MgoIsMulti: isMulti,
|
|
199601
|
-
Tag: instanceId,
|
|
199602
200593
|
},
|
|
199603
200594
|
});
|
|
199604
200595
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
|
|
@@ -204210,6 +205201,12 @@ const MANAGE_PERMISSION_ACTIONS = [
|
|
|
204210
205201
|
"updateUser",
|
|
204211
205202
|
"deleteUsers",
|
|
204212
205203
|
];
|
|
205204
|
+
function buildWriteVerificationHint(resourceId) {
|
|
205205
|
+
return `对于 ${resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。`;
|
|
205206
|
+
}
|
|
205207
|
+
function buildPermissionPropagationHint(resourceId) {
|
|
205208
|
+
return `刚更新完 ${resourceId} 的安全规则时,后端权限可能需要一小段传播时间。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先等待一小段时间,再用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要立刻连续重写规则,也不要在传播窗口里把旧拒绝直接当成规则表达式仍然错误。`;
|
|
205209
|
+
}
|
|
204213
205210
|
function buildEnvelope(data, message) {
|
|
204214
205211
|
return {
|
|
204215
205212
|
success: true,
|
|
@@ -204242,6 +205239,142 @@ function normalizeRecordArray(value, label) {
|
|
|
204242
205239
|
}
|
|
204243
205240
|
return value;
|
|
204244
205241
|
}
|
|
205242
|
+
function extractRiskyDocFieldOperations(securityRule) {
|
|
205243
|
+
if (!securityRule) {
|
|
205244
|
+
return [];
|
|
205245
|
+
}
|
|
205246
|
+
const operations = [];
|
|
205247
|
+
const operationPatterns = [
|
|
205248
|
+
["update", /"update"\s*:\s*"([^"]*)"/],
|
|
205249
|
+
["delete", /"delete"\s*:\s*"([^"]*)"/],
|
|
205250
|
+
];
|
|
205251
|
+
for (const [operation, pattern] of operationPatterns) {
|
|
205252
|
+
const match = securityRule.match(pattern);
|
|
205253
|
+
const expression = match?.[1];
|
|
205254
|
+
if (!expression) {
|
|
205255
|
+
continue;
|
|
205256
|
+
}
|
|
205257
|
+
const referencesNonIdDocField = /doc\.(?!_id\b)[A-Za-z_][A-Za-z0-9_]*/.test(expression);
|
|
205258
|
+
const usesGetByDocId = /get\('database\.[^']+'\s*\+\s*doc\._id\)/.test(expression);
|
|
205259
|
+
if (referencesNonIdDocField && !usesGetByDocId) {
|
|
205260
|
+
operations.push(operation);
|
|
205261
|
+
}
|
|
205262
|
+
}
|
|
205263
|
+
return operations;
|
|
205264
|
+
}
|
|
205265
|
+
function buildRecommendedOwnerWriteRule(resourceId) {
|
|
205266
|
+
return JSON.stringify({
|
|
205267
|
+
create: "auth.uid != null",
|
|
205268
|
+
update: "auth.uid != null && (get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid)",
|
|
205269
|
+
delete: "auth.uid != null && (get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid)",
|
|
205270
|
+
});
|
|
205271
|
+
}
|
|
205272
|
+
function buildRoleLookupNote() {
|
|
205273
|
+
return "如果你需要 app-level admin override(例如 CMS 中 admin 可编辑所有文章,而 editor 只能编辑自己的文章),CUSTOM 规则通常是必要的。一个已验证可用的模式是:角色集合文档主键就是 auth.uid,并在文章权限里写 get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid。若现有 schema 已经有 users / profiles / user_roles 其一,请复用已存在且能通过 _id == auth.uid 直接 get() 到的那一份;不要把 where({ uid }) 查询得到的集合误写成 get('database.users.' + auth.uid)。";
|
|
205274
|
+
}
|
|
205275
|
+
function buildRecommendedClientWritePattern(resourceId) {
|
|
205276
|
+
return `对于 CMS 文章这类使用 app-level admin override 的 CUSTOM 规则,前端可继续使用 db.collection('${resourceId}').doc(id).update(...) / remove(...)。关键是安全规则要采用已验证模式:get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid,并且文章文档中要真实写入 authorId。`;
|
|
205277
|
+
}
|
|
205278
|
+
function buildCreateRuleHint(securityRule, resourceId) {
|
|
205279
|
+
if (!securityRule) {
|
|
205280
|
+
return undefined;
|
|
205281
|
+
}
|
|
205282
|
+
const createMatch = securityRule.match(/"create"\s*:\s*"([^"]*)"/);
|
|
205283
|
+
const writeMatch = securityRule.match(/"write"\s*:\s*"([^"]*)"/);
|
|
205284
|
+
const createExpression = createMatch?.[1];
|
|
205285
|
+
const writeExpression = writeMatch?.[1];
|
|
205286
|
+
if (!createExpression && !writeExpression) {
|
|
205287
|
+
return undefined;
|
|
205288
|
+
}
|
|
205289
|
+
const referencesDoc = (createExpression && /doc\.[A-Za-z_]/.test(createExpression)) ||
|
|
205290
|
+
(writeExpression && /doc\.[A-Za-z_]/.test(writeExpression));
|
|
205291
|
+
if (!referencesDoc) {
|
|
205292
|
+
return undefined;
|
|
205293
|
+
}
|
|
205294
|
+
return {
|
|
205295
|
+
type: "createRuleDocWarning",
|
|
205296
|
+
severity: "warning",
|
|
205297
|
+
summary: "create 规则不应引用 doc.*,因为 create 时文档尚未存在。",
|
|
205298
|
+
detail: "CloudBase 的 create 规则验证的是写入数据(request.data),此时文档尚不存在,doc.* 不可用。" +
|
|
205299
|
+
"请将 create 规则改为仅使用 auth.* 检查(如 auth.uid != null && auth.loginType != 'ANONYMOUS')," +
|
|
205300
|
+
"或在写入时将 owner 字段(如 _openid / authorId)写入 request.data,然后在 create 规则中用 request.data._openid == auth.openid 做校验。" +
|
|
205301
|
+
"read / update / delete 规则可以使用 doc.* 引用已有文档字段,且客户端查询条件必须是规则约束的子集(如 _openid: '{openid}')。",
|
|
205302
|
+
recommendedRulePattern: "auth.uid != null && auth.loginType != 'ANONYMOUS'",
|
|
205303
|
+
recommendedPermission: "CUSTOM",
|
|
205304
|
+
recommendedSecurityRule: JSON.stringify({
|
|
205305
|
+
read: "auth.uid != null && auth.loginType != 'ANONYMOUS'",
|
|
205306
|
+
create: "auth.uid != null && auth.loginType != 'ANONYMOUS'",
|
|
205307
|
+
update: "auth.uid != null && auth.loginType != 'ANONYMOUS' && doc._openid == auth.openid",
|
|
205308
|
+
delete: "auth.uid != null && auth.loginType != 'ANONYMOUS' && doc._openid == auth.openid",
|
|
205309
|
+
}),
|
|
205310
|
+
};
|
|
205311
|
+
}
|
|
205312
|
+
function buildDocIdWriteRuleHint(securityRule, resourceId) {
|
|
205313
|
+
const appliesTo = extractRiskyDocFieldOperations(securityRule);
|
|
205314
|
+
if (!appliesTo.length) {
|
|
205315
|
+
return undefined;
|
|
205316
|
+
}
|
|
205317
|
+
return {
|
|
205318
|
+
type: "docIdWriteRuleWarning",
|
|
205319
|
+
severity: "warning",
|
|
205320
|
+
appliesTo,
|
|
205321
|
+
summary: "当前安全规则在 document-id 写入场景下可能被后端直接拒绝。",
|
|
205322
|
+
detail: "这类规则经常在 owner-only 集合里被写错,但对于 CMS 文章这种“admin 可编辑所有文章、editor 只能编辑自己的文章”的场景,已验证可用的做法是保留 doc.authorId,并通过独立角色集合做 admin override:get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid。不要默认改成 where(...),也不要把同集合 owner 判断重写成 get('database.collection.' + doc._id)。",
|
|
205323
|
+
recommendedRulePattern: "doc.authorId == auth.uid",
|
|
205324
|
+
recommendedPermission: "CUSTOM",
|
|
205325
|
+
recommendedSecurityRule: buildRecommendedOwnerWriteRule(resourceId),
|
|
205326
|
+
recommendedClientWritePattern: buildRecommendedClientWritePattern(resourceId),
|
|
205327
|
+
roleLookupNote: buildRoleLookupNote(),
|
|
205328
|
+
};
|
|
205329
|
+
}
|
|
205330
|
+
function buildInvalidGetPathHint(securityRule, resourceId) {
|
|
205331
|
+
if (!securityRule) {
|
|
205332
|
+
return undefined;
|
|
205333
|
+
}
|
|
205334
|
+
const hasFieldEmbeddedInsideGetPath = /get\('database\.[^']+'\s*\+\s*[^)]*\+\s*'\.[A-Za-z_][A-Za-z0-9_]*'\)/.test(securityRule);
|
|
205335
|
+
if (!hasFieldEmbeddedInsideGetPath) {
|
|
205336
|
+
return undefined;
|
|
205337
|
+
}
|
|
205338
|
+
return {
|
|
205339
|
+
type: "invalidGetPathWarning",
|
|
205340
|
+
severity: "warning",
|
|
205341
|
+
summary: "get() 的 path 只应包含 collection 和 documentId,不应把字段名拼进 path 字符串。",
|
|
205342
|
+
detail: "请写成 get('database.collection.' + doc._id).fieldName,而不是 get('database.collection.' + doc._id + '.fieldName')。但在 CMS 文章权限里,不要把 get('database.collection.' + doc._id) 当成默认首选方案;更稳的已验证模式是读取单独的角色集合:get('database.user_roles.' + auth.uid).role == 'admin' || doc.authorId == auth.uid。",
|
|
205343
|
+
recommendedRulePattern: "doc.authorId == auth.uid",
|
|
205344
|
+
recommendedPermission: "CUSTOM",
|
|
205345
|
+
recommendedSecurityRule: buildRecommendedOwnerWriteRule(resourceId),
|
|
205346
|
+
recommendedClientWritePattern: buildRecommendedClientWritePattern(resourceId),
|
|
205347
|
+
roleLookupNote: buildRoleLookupNote(),
|
|
205348
|
+
};
|
|
205349
|
+
}
|
|
205350
|
+
function buildTemplateLiteralRuleHint(securityRule, resourceId) {
|
|
205351
|
+
if (!securityRule) {
|
|
205352
|
+
return undefined;
|
|
205353
|
+
}
|
|
205354
|
+
const usesTemplateLiteralPlaceholderInRule = /\$\{(?:auth\.uid|doc\._id|doc\.[A-Za-z_][A-Za-z0-9_]*)\}/.test(securityRule);
|
|
205355
|
+
if (!usesTemplateLiteralPlaceholderInRule) {
|
|
205356
|
+
return undefined;
|
|
205357
|
+
}
|
|
205358
|
+
return {
|
|
205359
|
+
type: "templateLiteralRuleWarning",
|
|
205360
|
+
severity: "warning",
|
|
205361
|
+
summary: "CloudBase security rule 表达式不支持把 ${...} 当作 JS 模板字符串插值。",
|
|
205362
|
+
detail: "在 securityRule 字符串里,请使用表达式拼接,例如 get('database.user_roles.' + auth.uid).role,而不是 get('database.user_roles.${auth.uid}').role。对于 CMS 文章这类需要 app-level admin override 的规则,请优先使用已验证的 user_roles + doc.authorId 模式。",
|
|
205363
|
+
recommendedRulePattern: "doc.authorId == auth.uid",
|
|
205364
|
+
recommendedPermission: "CUSTOM",
|
|
205365
|
+
recommendedSecurityRule: buildRecommendedOwnerWriteRule(resourceId),
|
|
205366
|
+
recommendedClientWritePattern: buildRecommendedClientWritePattern(resourceId),
|
|
205367
|
+
roleLookupNote: buildRoleLookupNote(),
|
|
205368
|
+
};
|
|
205369
|
+
}
|
|
205370
|
+
function buildPermissionHints(securityRule, resourceId) {
|
|
205371
|
+
return [
|
|
205372
|
+
buildCreateRuleHint(securityRule, resourceId),
|
|
205373
|
+
buildDocIdWriteRuleHint(securityRule, resourceId),
|
|
205374
|
+
buildInvalidGetPathHint(securityRule, resourceId),
|
|
205375
|
+
buildTemplateLiteralRuleHint(securityRule, resourceId),
|
|
205376
|
+
].filter(Boolean);
|
|
205377
|
+
}
|
|
204245
205378
|
function registerPermissionTools(server) {
|
|
204246
205379
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
204247
205380
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
@@ -204289,12 +205422,17 @@ function registerPermissionTools(server) {
|
|
|
204289
205422
|
resources: [resourceId],
|
|
204290
205423
|
});
|
|
204291
205424
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
205425
|
+
const permissions = result.Data.PermissionList ?? [];
|
|
205426
|
+
const securityRule = permissions.find((item) => item.Resource === resourceId)?.SecurityRule ??
|
|
205427
|
+
permissions[0]?.SecurityRule;
|
|
205428
|
+
const hints = buildPermissionHints(securityRule, resourceId);
|
|
204292
205429
|
return buildEnvelope({
|
|
204293
205430
|
action,
|
|
204294
205431
|
envId,
|
|
204295
205432
|
resourceType,
|
|
204296
205433
|
resourceId,
|
|
204297
|
-
permissions
|
|
205434
|
+
permissions,
|
|
205435
|
+
hints,
|
|
204298
205436
|
raw: result,
|
|
204299
205437
|
}, "资源权限查询成功");
|
|
204300
205438
|
}
|
|
@@ -204307,11 +205445,22 @@ function registerPermissionTools(server) {
|
|
|
204307
205445
|
resources: resourceIds,
|
|
204308
205446
|
});
|
|
204309
205447
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
205448
|
+
const permissions = result.Data.PermissionList ?? [];
|
|
205449
|
+
const resourceHints = permissions
|
|
205450
|
+
.map((item) => ({
|
|
205451
|
+
resourceId: item.Resource ?? "",
|
|
205452
|
+
permission: item.Permission,
|
|
205453
|
+
hints: item.Permission === "CUSTOM" && item.Resource
|
|
205454
|
+
? buildPermissionHints(item.SecurityRule, item.Resource)
|
|
205455
|
+
: [],
|
|
205456
|
+
}))
|
|
205457
|
+
.filter((item) => item.resourceId && item.hints.length > 0);
|
|
204310
205458
|
return buildEnvelope({
|
|
204311
205459
|
action,
|
|
204312
205460
|
envId,
|
|
204313
205461
|
resourceType,
|
|
204314
|
-
permissions
|
|
205462
|
+
permissions,
|
|
205463
|
+
resourceHints,
|
|
204315
205464
|
total: result.Data.TotalCount ?? 0,
|
|
204316
205465
|
raw: result,
|
|
204317
205466
|
}, "资源权限列表查询成功");
|
|
@@ -204393,17 +205542,24 @@ function registerPermissionTools(server) {
|
|
|
204393
205542
|
}));
|
|
204394
205543
|
server.registerTool?.("managePermissions", {
|
|
204395
205544
|
title: "管理权限与用户配置",
|
|
204396
|
-
description: "权限域统一写入口。支持修改资源权限、角色管理、成员与策略增删、应用用户 CRUD
|
|
205545
|
+
description: "权限域统一写入口。支持修改资源权限、角色管理、成员与策略增删、应用用户 CRUD。`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。注意:`securityRule` 的详细语义取决于 `resourceType`;`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则。配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。",
|
|
204397
205546
|
inputSchema: {
|
|
204398
205547
|
action: zod_1.z.enum(MANAGE_PERMISSION_ACTIONS),
|
|
204399
205548
|
resourceType: zod_1.z
|
|
204400
205549
|
.enum(["noSqlDatabase", "sqlDatabase", "function", "storage"])
|
|
204401
|
-
.optional()
|
|
205550
|
+
.optional()
|
|
205551
|
+
.describe("目标资源类型。`securityRule` 的具体语义依赖这个值;`noSqlDatabase` 使用集合安全规则,`function` 与 `storage` 也有各自独立的安全规则语义,不要套用 NoSQL 规则语法。"),
|
|
204402
205552
|
resourceId: zod_1.z.string().optional(),
|
|
204403
205553
|
permission: zod_1.z
|
|
204404
205554
|
.enum(["READONLY", "PRIVATE", "ADMINWRITE", "ADMINONLY", "CUSTOM"])
|
|
204405
205555
|
.optional(),
|
|
204406
|
-
securityRule: zod_1.z
|
|
205556
|
+
securityRule: zod_1.z
|
|
205557
|
+
.string()
|
|
205558
|
+
.optional()
|
|
205559
|
+
.describe("资源类型特定的规则内容,详细语义依赖 `resourceType`。当 `resourceType=\"noSqlDatabase\"` 且 `permission=\"CUSTOM\"` 时,应传文档数据库安全规则 JSON(文档型数据库规则:`https://docs.cloudbase.net/database/security-rules`);键通常为 `read` / `create` / `update` / `delete`,值为表达式。" +
|
|
205560
|
+
"重要:`create` 规则验证写入数据,此时文档尚不存在,不能使用 `doc.*`;`read` / `update` / `delete` 规则可使用 `doc.*` 引用已有文档字段。" +
|
|
205561
|
+
"不要把 `doc._openid`、`auth.openid`、查询条件子集校验或 `create` / `update` / `delete` 模板误用于 `function`、`storage` 或 `sqlDatabase`。" +
|
|
205562
|
+
'如需配置 `function` 或 `storage`,请改查官方安全规则文档:云函数 `https://docs.cloudbase.net/cloud-function/security-rules`,云存储 `https://docs.cloudbase.net/storage/security-rules`。示例:{"read":"auth.uid != null","create":"auth.uid != null && auth.loginType != "ANONYMOUS"","update":"auth.uid != null && doc._openid == auth.openid","delete":"auth.uid != null && doc._openid == auth.openid"}'),
|
|
204407
205563
|
roleId: zod_1.z.string().optional(),
|
|
204408
205564
|
roleIds: zod_1.z.array(zod_1.z.string()).optional(),
|
|
204409
205565
|
roleName: zod_1.z.string().optional(),
|
|
@@ -204440,12 +205596,20 @@ function registerPermissionTools(server) {
|
|
|
204440
205596
|
securityRule,
|
|
204441
205597
|
});
|
|
204442
205598
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
205599
|
+
const hints = permission === "CUSTOM" ? buildPermissionHints(securityRule, resourceId) : [];
|
|
204443
205600
|
return buildEnvelope({
|
|
204444
205601
|
action,
|
|
204445
205602
|
envId,
|
|
204446
205603
|
resourceType,
|
|
204447
205604
|
resourceId,
|
|
204448
205605
|
permission,
|
|
205606
|
+
hints,
|
|
205607
|
+
verificationHint: resourceType === "noSqlDatabase" && permission === "CUSTOM"
|
|
205608
|
+
? buildWriteVerificationHint(resourceId)
|
|
205609
|
+
: undefined,
|
|
205610
|
+
propagationHint: resourceType === "noSqlDatabase" && permission === "CUSTOM"
|
|
205611
|
+
? buildPermissionPropagationHint(resourceId)
|
|
205612
|
+
: undefined,
|
|
204449
205613
|
raw: result,
|
|
204450
205614
|
}, "资源权限更新成功");
|
|
204451
205615
|
}
|
|
@@ -205156,7 +206320,7 @@ function registerSetupTools(server) {
|
|
|
205156
206320
|
title: "下载项目模板",
|
|
205157
206321
|
description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
|
|
205158
206322
|
|
|
205159
|
-
**CRITICAL**: This tool MUST be called FIRST when starting a new project.\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.
|
|
206323
|
+
**CRITICAL**: This tool MUST be called FIRST when starting a new project.\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.17.0-beta.2" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
205160
206324
|
inputSchema: {
|
|
205161
206325
|
template: zod_1.z
|
|
205162
206326
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -222298,12 +223462,25 @@ function registerGatewayTools(server) {
|
|
|
222298
223462
|
}
|
|
222299
223463
|
const cloudbase = await getManager();
|
|
222300
223464
|
const accessPath = normalizeAccessPath(input.path || `/${input.targetName}`);
|
|
222301
|
-
|
|
222302
|
-
|
|
222303
|
-
|
|
222304
|
-
|
|
222305
|
-
|
|
222306
|
-
|
|
223465
|
+
let result;
|
|
223466
|
+
try {
|
|
223467
|
+
result = await cloudbase.access.createAccess({
|
|
223468
|
+
name: input.targetName,
|
|
223469
|
+
path: accessPath,
|
|
223470
|
+
type: ((input.type || "Event") === "HTTP" ? 6 : 1),
|
|
223471
|
+
auth: input.auth,
|
|
223472
|
+
});
|
|
223473
|
+
}
|
|
223474
|
+
catch (err) {
|
|
223475
|
+
if (err.message && err.message.includes("An error has occurred")) {
|
|
223476
|
+
let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化;3) 该访问路径未被占用。";
|
|
223477
|
+
if (input.type === "HTTP") {
|
|
223478
|
+
hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 必须依然传 Event(或省略),传 HTTP 会导致此错误。";
|
|
223479
|
+
}
|
|
223480
|
+
throw new Error(`${hint} 原始错误:${err.message}`);
|
|
223481
|
+
}
|
|
223482
|
+
throw err;
|
|
223483
|
+
}
|
|
222307
223484
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
222308
223485
|
return buildEnvelope({
|
|
222309
223486
|
action: input.action,
|
|
@@ -222479,7 +223656,7 @@ function registerGatewayTools(server) {
|
|
|
222479
223656
|
.describe("目标资源类型。当前支持 function,后续可扩展"),
|
|
222480
223657
|
targetName: zod_1.z.string().optional().describe("目标资源名称"),
|
|
222481
223658
|
path: zod_1.z.string().optional().describe("访问路径,默认 /{targetName}"),
|
|
222482
|
-
type: zod_1.z.enum(["Event", "HTTP"]).optional().describe("
|
|
223659
|
+
type: zod_1.z.enum(["Event", "HTTP"]).optional().describe("目标函数的本身类型(非接入形式)。如果被访问的函数是 Event 型(默认),此处必须传 Event;只有当被访问函数在创建时就是 HTTP 函数时才传 HTTP。"),
|
|
222483
223660
|
auth: zod_1.z.boolean().optional().describe("是否开启鉴权"),
|
|
222484
223661
|
route: zod_1.z
|
|
222485
223662
|
.object({
|
|
@@ -256973,14 +258150,16 @@ class FunctionService {
|
|
|
256973
258150
|
});
|
|
256974
258151
|
}
|
|
256975
258152
|
/**
|
|
256976
|
-
* 通过
|
|
258153
|
+
* 通过 SCF COS 上传方式(通过 GetTempCosInfo + COS SDK 上传)
|
|
256977
258154
|
* 返回 TempCosObjectName 用于创建/更新函数
|
|
256978
258155
|
*/
|
|
256979
|
-
async
|
|
258156
|
+
async uploadFunctionZipToCos(options, installDependency) {
|
|
256980
258157
|
const { func, functionPath, functionRootPath } = options;
|
|
256981
|
-
const { env } = this.getFunctionConfig();
|
|
256982
|
-
|
|
256983
|
-
|
|
258158
|
+
const { env, appId } = this.getFunctionConfig();
|
|
258159
|
+
if (!appId) {
|
|
258160
|
+
throw new error_1.CloudBaseError('无法获取 AppId,请确认环境已开通存储服务');
|
|
258161
|
+
}
|
|
258162
|
+
const objectPath = `${appId}/${env}/${func.name}.zip`;
|
|
256984
258163
|
// 1. 生成存放函数包的临时 Cos 目录
|
|
256985
258164
|
const { Date: cosDate, Sign } = await this.scfService.request('GetTempCosInfo', {
|
|
256986
258165
|
ObjectPath: `${objectPath}`
|
|
@@ -257041,10 +258220,11 @@ class FunctionService {
|
|
|
257041
258220
|
});
|
|
257042
258221
|
}
|
|
257043
258222
|
/**
|
|
257044
|
-
*
|
|
258223
|
+
* 旧的 COS 上传方式(通过 DescribeBuildServiceCosInfo + PUT 上传)
|
|
257045
258224
|
* 返回 CosTimestamp 用于创建/更新函数
|
|
258225
|
+
* @deprecated 请使用 uploadFunctionZipToCos 代替
|
|
257046
258226
|
*/
|
|
257047
|
-
async
|
|
258227
|
+
async uploadFunctionZipToCosLegacy(options, installDependency) {
|
|
257048
258228
|
const { func, functionPath, functionRootPath } = options;
|
|
257049
258229
|
const { env } = this.getFunctionConfig();
|
|
257050
258230
|
// 1. 生成存放函数包的临时 Cos 目录
|
|
@@ -257145,13 +258325,13 @@ class FunctionService {
|
|
|
257145
258325
|
ZipFile: base64
|
|
257146
258326
|
};
|
|
257147
258327
|
}
|
|
257148
|
-
// 默认走 COS
|
|
258328
|
+
// 默认走 COS 上传(GetTempCosInfo + COS SDK)
|
|
257149
258329
|
console.log(`[${func.name}] 部署方式: COS 上传`);
|
|
257150
258330
|
const region = this.environment.cloudBaseContext.region || constant_1.SCF_TEMP_COS.DEFAULT_REGION;
|
|
257151
|
-
const
|
|
258331
|
+
const result = await this.uploadFunctionZipToCos(options, installDependency);
|
|
257152
258332
|
return {
|
|
257153
258333
|
CosBucketRegion: region,
|
|
257154
|
-
TempCosObjectName: `/${
|
|
258334
|
+
TempCosObjectName: `/${result.Key}`
|
|
257155
258335
|
};
|
|
257156
258336
|
}
|
|
257157
258337
|
async createAccessPath(name, path) {
|
|
@@ -257213,11 +258393,14 @@ class FunctionService {
|
|
|
257213
258393
|
* @memberof FunctionService
|
|
257214
258394
|
*/
|
|
257215
258395
|
getFunctionConfig() {
|
|
257216
|
-
var _a, _b, _c, _d;
|
|
258396
|
+
var _a, _b, _c, _d, _e;
|
|
257217
258397
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
257218
258398
|
// Functions 可能为空
|
|
257219
258399
|
const namespace = ((_b = (_a = envConfig.Functions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Namespace) || '';
|
|
257220
|
-
|
|
258400
|
+
// AppId 可能为空字符串,从 Bucket 名称中提取
|
|
258401
|
+
// COS Bucket 格式: {BucketName}-{APPID},APPID 为纯数字
|
|
258402
|
+
const storage = (_c = envConfig.Storages) === null || _c === void 0 ? void 0 : _c[0];
|
|
258403
|
+
const appId = (storage === null || storage === void 0 ? void 0 : storage.AppId) || ((_e = (_d = storage === null || storage === void 0 ? void 0 : storage.Bucket) === null || _d === void 0 ? void 0 : _d.match(/-(\d+)$/)) === null || _e === void 0 ? void 0 : _e[1]) || '';
|
|
257221
258404
|
const { proxy } = this.environment.cloudBaseContext;
|
|
257222
258405
|
return {
|
|
257223
258406
|
proxy,
|
|
@@ -257378,10 +258561,10 @@ __decorate([
|
|
|
257378
258561
|
], FunctionService.prototype, "getFunctionAlias", null);
|
|
257379
258562
|
__decorate([
|
|
257380
258563
|
(0, utils_1.preLazy)()
|
|
257381
|
-
], FunctionService.prototype, "
|
|
258564
|
+
], FunctionService.prototype, "uploadFunctionZipToCos", null);
|
|
257382
258565
|
__decorate([
|
|
257383
258566
|
(0, utils_1.preLazy)()
|
|
257384
|
-
], FunctionService.prototype, "
|
|
258567
|
+
], FunctionService.prototype, "uploadFunctionZipToCosLegacy", null);
|
|
257385
258568
|
__decorate([
|
|
257386
258569
|
(0, utils_1.preLazy)()
|
|
257387
258570
|
], FunctionService.prototype, "getCodeParams", null);
|