@cloudbase/cloudbase-mcp 2.23.10 → 2.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -122997,6 +122997,7 @@ const STORAGE_ACTIONS = [
122997
122997
  "objectInfo",
122998
122998
  "signUpload",
122999
122999
  "signDownload",
123000
+ "createBucket",
123000
123001
  ];
123001
123002
  function buildPgStorageResult(payload) {
123002
123003
  return (0, tool_result_js_1.buildJsonToolResult)(payload);
@@ -123055,12 +123056,52 @@ async function handleBuckets(server) {
123055
123056
  data: {
123056
123057
  capability: "storageManagerSdkOrHttpApi",
123057
123058
  envId: server.cloudBaseOptions?.envId ?? null,
123058
- supportedActions: ["buckets", "config", "uploadPlan", "objectInfo"],
123059
+ supportedActions: ["buckets", "config", "uploadPlan", "objectInfo", "createBucket"],
123059
123060
  note: "PG environment storage is exposed as metadata and implementation plans in MCP. Application data-plane upload/download should use SDK or HTTP API code.",
123060
123061
  },
123061
123062
  message: "Resolved PostgreSQL storage capability summary. Use uploadPlan for application-side upload implementation.",
123062
123063
  });
123063
123064
  }
123065
+ async function handleCreateBucket(args, server) {
123066
+ if (!args.bucket?.trim()) {
123067
+ return buildPgStorageResult({
123068
+ success: false,
123069
+ errorCode: "BUCKET_REQUIRED",
123070
+ message: "Provide bucket name to create a PG storage bucket.",
123071
+ });
123072
+ }
123073
+ const bucket = args.bucket.trim();
123074
+ const envId = server.cloudBaseOptions?.envId ?? "${envId}";
123075
+ return buildPgStorageResult({
123076
+ success: true,
123077
+ data: {
123078
+ action: "createBucket",
123079
+ bucket,
123080
+ description: `Creates PG storage bucket '${bucket}'`,
123081
+ plans: {
123082
+ sql: `INSERT INTO storage.buckets (id, name, public) VALUES ('${bucket}', '${bucket}', false);`,
123083
+ httpApi: {
123084
+ method: "POST",
123085
+ url: `https://${envId}.api.tcloudbasegateway.com/v1/storages/bucket/`,
123086
+ headers: {
123087
+ "Authorization": "Bearer {service_role_token}",
123088
+ "Content-Type": "application/json",
123089
+ },
123090
+ body: { name: bucket, public: false },
123091
+ },
123092
+ cli: `tcb db execute -e ${envId} --sql "INSERT INTO storage.buckets (id, name, public) VALUES ('${bucket}', '${bucket}', false);"`,
123093
+ },
123094
+ tokenRequired: "service_role",
123095
+ tokenGuide: "Use manageAppAuth(action='createApiKey', name='storage-server-key') to get a service_role level API key token.",
123096
+ nextSteps: [
123097
+ "Option A: Run the SQL via managePgDatabase(action='execute', confirm=true)",
123098
+ "Option B: Call the HTTP API with a service_role token",
123099
+ "Option C: Use CloudBase CLI: tcb db execute",
123100
+ ],
123101
+ },
123102
+ message: `Generated bucket creation plan for '${bucket}'. Execute one of the provided plans to create the bucket, then configure RLS policies.`,
123103
+ });
123104
+ }
123064
123105
  async function handleUploadPlan(args, server) {
123065
123106
  const missingBucket = requireBucket(args);
123066
123107
  if (missingBucket)
@@ -123131,7 +123172,7 @@ function registerPGStorageTools(server) {
123131
123172
  inputSchema: {
123132
123173
  action: zod_1.z
123133
123174
  .enum(STORAGE_ACTIONS)
123134
- .describe("操作类型:buckets/config=查询存储能力摘要;uploadPlan=生成 HTTP API/SDK 上传方案;objectInfo=生成对象元信息查询方案;signUpload/signDownload=显式的一次性签名 URL 请求占位"),
123175
+ .describe("操作类型:buckets/config=查询存储能力摘要;createBucket=生成 bucket 创建方案(SQL/HTTP API/CLI);uploadPlan=生成 HTTP API/SDK 上传方案;objectInfo=生成对象元信息查询方案;signUpload/signDownload=显式的一次性签名 URL 请求占位"),
123135
123176
  bucket: zod_1.z.string().optional().describe("云存储 bucket 名称"),
123136
123177
  objectKey: zod_1.z.string().optional().describe("单个对象 key"),
123137
123178
  objectKeys: zod_1.z.array(zod_1.z.string()).optional().describe("多个对象 key,用于对象元信息查询规划"),
@@ -123155,6 +123196,8 @@ function registerPGStorageTools(server) {
123155
123196
  case "buckets":
123156
123197
  case "config":
123157
123198
  return handleBuckets(server);
123199
+ case "createBucket":
123200
+ return handleCreateBucket(args, server);
123158
123201
  case "uploadPlan":
123159
123202
  return handleUploadPlan(args, server);
123160
123203
  case "objectInfo":
@@ -136836,7 +136879,7 @@ class TelemetryReporter {
136836
136879
  const nodeVersion = process.version; // Node.js版本
136837
136880
  const arch = os_1.default.arch(); // 系统架构
136838
136881
  // 从构建时注入的版本号获取MCP版本信息
136839
- const mcpVersion = process.env.npm_package_version || "2.23.10" || 0;
136882
+ const mcpVersion = process.env.npm_package_version || "2.24.0" || 0;
136840
136883
  return {
136841
136884
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
136842
136885
  deviceId: this.deviceId,
@@ -160299,6 +160342,7 @@ const MANAGE_ACTIONS = [
160299
160342
  "listMigrations",
160300
160343
  "migrationDetail",
160301
160344
  "rollbackMigration",
160345
+ "repairMigration",
160302
160346
  ];
160303
160347
  function buildPgToolResult(payload) {
160304
160348
  return (0, tool_result_js_1.buildJsonToolResult)(payload);
@@ -160442,6 +160486,38 @@ function classifySqlRisk(sql) {
160442
160486
  requiresConfirm: true,
160443
160487
  };
160444
160488
  }
160489
+ /**
160490
+ * Schema DDL that must go through applyMigration by default.
160491
+ * Includes CREATE/ALTER/COMMENT (schema_change) and DROP/TRUNCATE / destructive ALTER.
160492
+ * Excludes DML DELETE and security_change (GRANT/POLICY), which may still use execute.
160493
+ */
160494
+ function isSchemaDdlRisk(risk, sql) {
160495
+ if (risk === "schema_change") {
160496
+ return true;
160497
+ }
160498
+ if (risk !== "destructive") {
160499
+ return false;
160500
+ }
160501
+ const verb = getSqlVerb(stripLeadingSqlComments(sql));
160502
+ return ["DROP", "TRUNCATE", "ALTER"].includes(verb);
160503
+ }
160504
+ function buildLocalMigrationFileHint(version, name) {
160505
+ return `migrations/${version}_${name}.sql`;
160506
+ }
160507
+ const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
160508
+ "Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
160509
+ "then call planMigration/applyMigration with the same migrationVersion and migrationName.";
160510
+ function requireExplicitMigrationVersion(args, actionLabel) {
160511
+ const version = args.migrationVersion?.trim();
160512
+ if (!version) {
160513
+ return buildPgToolResult({
160514
+ success: false,
160515
+ errorCode: "MIGRATION_VERSION_REQUIRED",
160516
+ message: `Provide migrationVersion when action=${actionLabel}. ${MIGRATION_VERSION_REQUIRED_MESSAGE}`,
160517
+ });
160518
+ }
160519
+ return version;
160520
+ }
160445
160521
  function quoteIdentifier(identifier) {
160446
160522
  return `"${identifier.replace(/"/g, '""')}"`;
160447
160523
  }
@@ -161048,6 +161124,34 @@ async function handleExecuteSql(args, context, deps) {
161048
161124
  });
161049
161125
  }
161050
161126
  const classification = classifySqlRisk(args.sql);
161127
+ if (isSchemaDdlRisk(classification.risk, args.sql) && args.allowDdlViaExecute !== true) {
161128
+ return buildPgToolResult({
161129
+ success: false,
161130
+ errorCode: "DDL_USE_APPLY_MIGRATION",
161131
+ message: "Schema DDL (CREATE/ALTER/DROP/TRUNCATE/...) must use applyMigration with an explicit migrationVersion, " +
161132
+ "not execute. Write local migrations/<version>_<name>.sql first, then call applyMigration. " +
161133
+ "Set allowDdlViaExecute=true only for exceptional one-off ops that intentionally bypass migration history.",
161134
+ data: {
161135
+ classification,
161136
+ localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
161137
+ },
161138
+ nextActions: [
161139
+ buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview the schema change as a versioned migration (provide migrationVersion + migrationName).", {
161140
+ action: "planMigration",
161141
+ migrationName: "describe_your_change",
161142
+ migrationVersion: "YYYYMMDDHHMMSS",
161143
+ sql: args.sql,
161144
+ }),
161145
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Apply the schema change via PushPGUserMigrations with the same explicit migrationVersion.", {
161146
+ action: "applyMigration",
161147
+ migrationName: "describe_your_change",
161148
+ migrationVersion: "YYYYMMDDHHMMSS",
161149
+ sql: args.sql,
161150
+ confirm: true,
161151
+ }),
161152
+ ],
161153
+ });
161154
+ }
161051
161155
  if (!classification.readOnly && !args.confirm) {
161052
161156
  return buildPgToolResult({
161053
161157
  success: false,
@@ -161057,7 +161161,12 @@ async function handleExecuteSql(args, context, deps) {
161057
161161
  classification,
161058
161162
  },
161059
161163
  nextActions: [
161060
- buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue with confirm=true to actually run the SQL.", { action: "execute", sql: args.sql, confirm: true }),
161164
+ buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue with confirm=true to actually run the SQL.", {
161165
+ action: "execute",
161166
+ sql: args.sql,
161167
+ confirm: true,
161168
+ ...(args.allowDdlViaExecute === true ? { allowDdlViaExecute: true } : {}),
161169
+ }),
161061
161170
  buildNextAction(QUERY_PG_DATABASE, "metadata", "Inspect table size and shape before destructive changes.", { action: "metadata", limit: 20 }),
161062
161171
  ],
161063
161172
  });
@@ -161074,8 +161183,15 @@ async function handleExecuteSql(args, context, deps) {
161074
161183
  .slice(0, 5)
161075
161184
  .map((row) => serializeValue(row)),
161076
161185
  targetTable: targetTable ?? null,
161186
+ ...(args.allowDdlViaExecute === true && isSchemaDdlRisk(classification.risk, args.sql)
161187
+ ? {
161188
+ warning: "DDL executed via allowDdlViaExecute bypasses migration history. Prefer applyMigration for reproducible schema changes.",
161189
+ }
161190
+ : {}),
161077
161191
  },
161078
- message: "Write SQL executed successfully.",
161192
+ message: args.allowDdlViaExecute === true && isSchemaDdlRisk(classification.risk, args.sql)
161193
+ ? "Write SQL executed successfully (DDL via allowDdlViaExecute; migration history was bypassed)."
161194
+ : "Write SQL executed successfully.",
161079
161195
  nextActions: classification.risk === "schema_change" && targetTable
161080
161196
  ? [
161081
161197
  buildNextAction(QUERY_PG_DATABASE, "schema", "Inspect the table schema, columns, indexes, and RLS policies after schema changes.", {
@@ -161103,6 +161219,43 @@ async function handleDryRun(args) {
161103
161219
  });
161104
161220
  }
161105
161221
  const classification = classifySqlRisk(args.sql);
161222
+ const schemaDdl = isSchemaDdlRisk(classification.risk, args.sql);
161223
+ if (classification.readOnly) {
161224
+ return buildPgToolResult({
161225
+ success: true,
161226
+ data: {
161227
+ classification,
161228
+ wouldExecute: false,
161229
+ sqlPreview: args.sql.trim().slice(0, 500),
161230
+ },
161231
+ message: "SQL dry run completed. This statement is read-only; use queryPgDatabase(action=sql) to execute it.",
161232
+ nextActions: [
161233
+ buildNextAction(QUERY_PG_DATABASE, "sql", "Execute the read-only SQL through queryPgDatabase.", { action: "sql", sql: args.sql, limit: 20 }),
161234
+ ],
161235
+ });
161236
+ }
161237
+ if (schemaDdl) {
161238
+ return buildPgToolResult({
161239
+ success: true,
161240
+ data: {
161241
+ classification,
161242
+ wouldExecute: false,
161243
+ sqlPreview: args.sql.trim().slice(0, 500),
161244
+ preferredAction: "applyMigration",
161245
+ localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
161246
+ },
161247
+ message: "SQL dry run completed. Schema DDL should use applyMigration with an explicit migrationVersion, not execute.",
161248
+ nextActions: [
161249
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Apply schema DDL via versioned migration (provide migrationVersion + migrationName + confirm=true).", {
161250
+ action: "applyMigration",
161251
+ migrationName: "describe_your_change",
161252
+ migrationVersion: "YYYYMMDDHHMMSS",
161253
+ sql: args.sql,
161254
+ confirm: true,
161255
+ }),
161256
+ ],
161257
+ });
161258
+ }
161106
161259
  return buildPgToolResult({
161107
161260
  success: true,
161108
161261
  data: {
@@ -161110,15 +161263,9 @@ async function handleDryRun(args) {
161110
161263
  wouldExecute: false,
161111
161264
  sqlPreview: args.sql.trim().slice(0, 500),
161112
161265
  },
161113
- message: classification.readOnly
161114
- ? "SQL dry run completed. This statement is read-only; use queryPgDatabase(action=sql) to execute it."
161115
- : "SQL dry run completed. Write SQL requires managePgDatabase(action=execute, confirm=true).",
161266
+ message: "SQL dry run completed. Write SQL requires managePgDatabase(action=execute, confirm=true).",
161116
161267
  nextActions: [
161117
- buildNextAction(classification.readOnly ? QUERY_PG_DATABASE : MANAGE_PG_DATABASE, classification.readOnly ? "sql" : "execute", classification.readOnly
161118
- ? "Execute the read-only SQL through queryPgDatabase."
161119
- : "Execute the write SQL only after explicit confirmation.", classification.readOnly
161120
- ? { action: "sql", sql: args.sql, limit: 20 }
161121
- : { action: "execute", sql: args.sql, confirm: true }),
161268
+ buildNextAction(MANAGE_PG_DATABASE, "execute", "Execute the write SQL only after explicit confirmation.", { action: "execute", sql: args.sql, confirm: true }),
161122
161269
  ],
161123
161270
  });
161124
161271
  }
@@ -161130,16 +161277,49 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
161130
161277
  message: "Provide migration SQL when action=planMigration.",
161131
161278
  });
161132
161279
  }
161280
+ if (!args.migrationName?.trim()) {
161281
+ return buildPgToolResult({
161282
+ success: false,
161283
+ errorCode: "MIGRATION_NAME_REQUIRED",
161284
+ message: "Provide migrationName (lowercase letters, digits and underscores, starting with a letter) when action=planMigration.",
161285
+ });
161286
+ }
161287
+ const versionOrError = requireExplicitMigrationVersion(args, "planMigration");
161288
+ if (typeof versionOrError !== "string") {
161289
+ return versionOrError;
161290
+ }
161291
+ const version = versionOrError;
161292
+ const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
161293
+ const migration = {
161294
+ Version: version,
161295
+ Name: args.migrationName,
161296
+ Query: args.sql,
161297
+ };
161298
+ if (args.rollbackSql?.trim()) {
161299
+ migration.Rollback = args.rollbackSql;
161300
+ }
161133
161301
  try {
161134
161302
  const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
161135
- Sql: args.sql,
161303
+ Migrations: [migration],
161136
161304
  }, cloudBaseOptions);
161137
161305
  return buildPgToolResult({
161138
161306
  success: true,
161139
- data: result,
161140
- message: "Migration plan generated via PreviewPGUserMigrations.",
161307
+ data: {
161308
+ migrationVersion: version,
161309
+ migrationName: args.migrationName,
161310
+ localFileHint,
161311
+ apiResult: result,
161312
+ },
161313
+ message: `Migration plan generated via PreviewPGUserMigrations. Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`,
161141
161314
  nextActions: [
161142
- buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration to execute.", { action: "applyMigration", sql: args.sql, confirm: true }),
161315
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion.", {
161316
+ action: "applyMigration",
161317
+ migrationName: args.migrationName,
161318
+ migrationVersion: version,
161319
+ sql: args.sql,
161320
+ confirm: true,
161321
+ ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
161322
+ }),
161143
161323
  ],
161144
161324
  });
161145
161325
  }
@@ -161159,6 +161339,13 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
161159
161339
  message: "Provide migration SQL when action=applyMigration.",
161160
161340
  });
161161
161341
  }
161342
+ if (!args.migrationName?.trim()) {
161343
+ return buildPgToolResult({
161344
+ success: false,
161345
+ errorCode: "MIGRATION_NAME_REQUIRED",
161346
+ message: "Provide migrationName (lowercase letters, digits and underscores, starting with a letter) when action=applyMigration.",
161347
+ });
161348
+ }
161162
161349
  if (args.confirm !== true) {
161163
161350
  return buildPgToolResult({
161164
161351
  success: false,
@@ -161166,14 +161353,41 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
161166
161353
  message: "PushPGUserMigrations requires confirm=true. Run with confirm=true to proceed.",
161167
161354
  });
161168
161355
  }
161356
+ const versionOrError = requireExplicitMigrationVersion(args, "applyMigration");
161357
+ if (typeof versionOrError !== "string") {
161358
+ return versionOrError;
161359
+ }
161360
+ const version = versionOrError;
161361
+ const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
161362
+ const migration = {
161363
+ Version: version,
161364
+ Name: args.migrationName,
161365
+ Query: args.sql,
161366
+ };
161367
+ if (args.rollbackSql?.trim()) {
161368
+ migration.Rollback = args.rollbackSql;
161369
+ }
161370
+ const params = { Migrations: [migration] };
161371
+ if (args.lockTimeoutMs !== undefined) {
161372
+ params.LockTimeoutMs = args.lockTimeoutMs;
161373
+ }
161374
+ if (args.statementTimeoutMs !== undefined) {
161375
+ params.StatementTimeoutMs = args.statementTimeoutMs;
161376
+ }
161169
161377
  try {
161170
- const result = await callPgMigrationApi(context, "PushPGUserMigrations", {
161171
- Sql: args.sql,
161172
- }, cloudBaseOptions);
161378
+ const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
161173
161379
  return buildPgToolResult({
161174
161380
  success: true,
161175
- data: result,
161176
- message: "Migrations applied via PushPGUserMigrations.",
161381
+ data: {
161382
+ migrationVersion: version,
161383
+ migrationName: args.migrationName,
161384
+ localFileHint,
161385
+ apiResult: result,
161386
+ },
161387
+ message: `Migrations applied via PushPGUserMigrations. Ensure local file ${localFileHint} exists and matches.`,
161388
+ nextActions: [
161389
+ buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Verify the remote migration history records the same migrationVersion.", { action: "listMigrations", limit: 20 }),
161390
+ ],
161177
161391
  });
161178
161392
  }
161179
161393
  catch (error) {
@@ -161185,8 +161399,15 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
161185
161399
  }
161186
161400
  }
161187
161401
  async function handleListMigrations(args, context, deps, cloudBaseOptions) {
161402
+ const params = {};
161403
+ if (args.limit !== undefined) {
161404
+ params.Limit = args.limit;
161405
+ }
161406
+ if (args.offset !== undefined) {
161407
+ params.Offset = args.offset;
161408
+ }
161188
161409
  try {
161189
- const result = await callPgMigrationApi(context, "ListPGUserMigrations", {}, cloudBaseOptions);
161410
+ const result = await callPgMigrationApi(context, "ListPGUserMigrations", params, cloudBaseOptions);
161190
161411
  return buildPgToolResult({
161191
161412
  success: true,
161192
161413
  data: result,
@@ -161202,16 +161423,16 @@ async function handleListMigrations(args, context, deps, cloudBaseOptions) {
161202
161423
  }
161203
161424
  }
161204
161425
  async function handleMigrationDetail(args, context, deps, cloudBaseOptions) {
161205
- if (!args.objectName?.trim()) {
161426
+ if (!args.migrationVersion?.trim()) {
161206
161427
  return buildPgToolResult({
161207
161428
  success: false,
161208
- errorCode: "OBJECT_NAME_REQUIRED",
161209
- message: "Provide objectName (migration ID) when action=migrationDetail.",
161429
+ errorCode: "MIGRATION_VERSION_REQUIRED",
161430
+ message: "Provide migrationVersion (14-digit timestamp YYYYMMDDHHMMSS) when action=migrationDetail.",
161210
161431
  });
161211
161432
  }
161212
161433
  try {
161213
161434
  const result = await callPgMigrationApi(context, "DescribePGUserMigration", {
161214
- ObjectId: args.objectName,
161435
+ MigrationVersion: args.migrationVersion,
161215
161436
  }, cloudBaseOptions);
161216
161437
  return buildPgToolResult({
161217
161438
  success: true,
@@ -161228,11 +161449,11 @@ async function handleMigrationDetail(args, context, deps, cloudBaseOptions) {
161228
161449
  }
161229
161450
  }
161230
161451
  async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
161231
- if (!args.objectName?.trim()) {
161452
+ if (args.lastN === undefined || args.lastN < 1 || !Number.isInteger(args.lastN)) {
161232
161453
  return buildPgToolResult({
161233
161454
  success: false,
161234
- errorCode: "OBJECT_NAME_REQUIRED",
161235
- message: "Provide objectName (migration ID) when action=rollbackMigration.",
161455
+ errorCode: "LAST_N_REQUIRED",
161456
+ message: "Provide lastN (positive integer) when action=rollbackMigration.",
161236
161457
  });
161237
161458
  }
161238
161459
  if (args.confirm !== true) {
@@ -161244,7 +161465,7 @@ async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
161244
161465
  }
161245
161466
  try {
161246
161467
  const result = await callPgMigrationApi(context, "RollbackPGUserMigrations", {
161247
- ObjectId: args.objectName,
161468
+ LastN: args.lastN,
161248
161469
  }, cloudBaseOptions);
161249
161470
  return buildPgToolResult({
161250
161471
  success: true,
@@ -161260,6 +161481,67 @@ async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
161260
161481
  });
161261
161482
  }
161262
161483
  }
161484
+ async function handleRepairMigration(args, context, deps, cloudBaseOptions) {
161485
+ if (!args.migrationVersion?.trim()) {
161486
+ return buildPgToolResult({
161487
+ success: false,
161488
+ errorCode: "MIGRATION_VERSION_REQUIRED",
161489
+ message: "Provide migrationVersion (14-digit timestamp YYYYMMDDHHMMSS) when action=repairMigration.",
161490
+ });
161491
+ }
161492
+ if (!args.migrationName?.trim()) {
161493
+ return buildPgToolResult({
161494
+ success: false,
161495
+ errorCode: "MIGRATION_NAME_REQUIRED",
161496
+ message: "Provide migrationName when action=repairMigration.",
161497
+ });
161498
+ }
161499
+ if (!args.repairStatus) {
161500
+ return buildPgToolResult({
161501
+ success: false,
161502
+ errorCode: "REPAIR_STATUS_REQUIRED",
161503
+ message: "Provide repairStatus (applied or reverted) when action=repairMigration.",
161504
+ });
161505
+ }
161506
+ if (!args.repairReason?.trim()) {
161507
+ return buildPgToolResult({
161508
+ success: false,
161509
+ errorCode: "REPAIR_REASON_REQUIRED",
161510
+ message: "Provide repairReason when action=repairMigration.",
161511
+ });
161512
+ }
161513
+ if (args.repairStatus === "applied" && !args.sql?.trim()) {
161514
+ return buildPgToolResult({
161515
+ success: false,
161516
+ errorCode: "SQL_REQUIRED",
161517
+ message: "Provide sql (Query) when action=repairMigration with repairStatus=applied.",
161518
+ });
161519
+ }
161520
+ const params = {
161521
+ MigrationVersion: args.migrationVersion,
161522
+ Name: args.migrationName,
161523
+ Status: args.repairStatus,
161524
+ Reason: args.repairReason,
161525
+ };
161526
+ if (args.repairStatus === "applied" && args.sql?.trim()) {
161527
+ params.Query = args.sql;
161528
+ }
161529
+ try {
161530
+ const result = await callPgMigrationApi(context, "RepairPGUserMigrationHistory", params, cloudBaseOptions);
161531
+ return buildPgToolResult({
161532
+ success: true,
161533
+ data: result,
161534
+ message: "Migration history repaired via RepairPGUserMigrationHistory.",
161535
+ });
161536
+ }
161537
+ catch (error) {
161538
+ return buildPgToolResult({
161539
+ success: false,
161540
+ errorCode: "MIGRATION_API_ERROR",
161541
+ message: `RepairPGUserMigrationHistory failed: ${error instanceof Error ? error.message : String(error)}`,
161542
+ });
161543
+ }
161544
+ }
161263
161545
  async function handleGetPgSchema(args, context, deps) {
161264
161546
  const objectName = args.objectName ?? "";
161265
161547
  const parsed = parseSchemaQualifiedName(objectName);
@@ -161380,15 +161662,15 @@ function registerPGDatabaseTools(server, providedDeps) {
161380
161662
  });
161381
161663
  server.registerTool?.(MANAGE_PG_DATABASE, {
161382
161664
  title: "管理 PostgreSQL 上下文或执行写入 SQL",
161383
- description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL 或 DDL、SQL 风险预检、迁移规划。上下文自动从当前 MCP 环境推导,无需 init。仅支持 CloudBase 内置 PostgreSQL 云端 Manager SDK 执行路径。",
161665
+ description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL、SQL 风险预检、迁移管理。建表/ALTER/DROP 等 schema 变更必须使用 applyMigration(显式 migrationVersion + 本地 migrations/ 留档),不要默认用 execute。execute 主要用于 DML 与 GRANT/RLS 等运维 SQL。",
161384
161666
  inputSchema: {
161385
161667
  action: zod_1.z
161386
161668
  .enum(MANAGE_ACTIONS)
161387
- .describe("操作类型:execute=执行已确认的写入 SQL 或 DDL;dryRun=只分析 SQL 风险不执行;planMigration=通过 PreviewPGUserMigrations 预览迁移计划;applyMigration=通过 PushPGUserMigrations 应用迁移;listMigrations=查询已应用的 Migration 列表;migrationDetail=查看单条 Migration 详情;rollbackMigration=回滚指定 Migration"),
161669
+ .describe("操作类型:execute=执行已确认的写入 SQL(DML/GRANT/RLS;schema DDL 默认拒绝,需 allowDdlViaExecute=true);dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + migrationVersion + sql);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + migrationVersion + sql + confirm=true);listMigrations=查询已应用的 Migration 列表(可传 limit/offset 分页);migrationDetail=查看单条 Migration 详情(需 migrationVersion);rollbackMigration=回滚最近 N 条 Migration(需 lastN + confirm=true);repairMigration=修复 Migration 历史记录(需 migrationVersion + migrationName + repairStatus + repairReason)"),
161388
161670
  sql: zod_1.z
161389
161671
  .string()
161390
161672
  .optional()
161391
- .describe("action=execute、dryRun、planMigration 或 applyMigration 使用的 SQL 语句"),
161673
+ .describe("action=execute、dryRun、planMigration、applyMigration 或 repairMigration(applied) 使用的 SQL 语句"),
161392
161674
  confirm: zod_1.z
161393
161675
  .boolean()
161394
161676
  .optional()
@@ -161412,7 +161694,62 @@ function registerPGDatabaseTools(server, providedDeps) {
161412
161694
  objectName: zod_1.z
161413
161695
  .string()
161414
161696
  .optional()
161415
- .describe("action=migrationDetail 或 rollbackMigration 时使用的 migration ID。"),
161697
+ .describe("可选的对象名,当前仅用于非 migration 场景。migration 相关操作请使用 migrationName / migrationVersion / lastN。"),
161698
+ migrationName: zod_1.z
161699
+ .string()
161700
+ .regex(/^[a-z][a-z0-9_]*$/)
161701
+ .optional()
161702
+ .describe("plan/apply/repair 必填:migration 名称,小写字母开头,仅允许小写字母、数字和下划线。"),
161703
+ migrationVersion: zod_1.z
161704
+ .string()
161705
+ .regex(/^\d{14}$/)
161706
+ .optional()
161707
+ .describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair 必填;禁止由服务端静默生成,避免与本地 migrations/<version>_<name>.sql 分叉。"),
161708
+ rollbackSql: zod_1.z
161709
+ .string()
161710
+ .optional()
161711
+ .describe("plan/apply 可选:回滚 SQL 语句。"),
161712
+ lastN: zod_1.z
161713
+ .number()
161714
+ .int()
161715
+ .positive()
161716
+ .optional()
161717
+ .describe("rollback 必填:回滚最近 N 条已应用的 Migration,正整数。"),
161718
+ limit: zod_1.z
161719
+ .number()
161720
+ .int()
161721
+ .min(1)
161722
+ .max(500)
161723
+ .optional()
161724
+ .describe("list 可选:返回数量上限,1-500,默认 100。"),
161725
+ offset: zod_1.z
161726
+ .number()
161727
+ .int()
161728
+ .min(0)
161729
+ .optional()
161730
+ .describe("list 可选:分页偏移,默认 0。"),
161731
+ lockTimeoutMs: zod_1.z
161732
+ .number()
161733
+ .int()
161734
+ .optional()
161735
+ .describe("apply 可选:获取数据库锁的最长时间(毫秒),默认 5000。"),
161736
+ statementTimeoutMs: zod_1.z
161737
+ .number()
161738
+ .int()
161739
+ .optional()
161740
+ .describe("apply 可选:单条 SQL 执行最长时间(毫秒),默认 300000。"),
161741
+ repairStatus: zod_1.z
161742
+ .enum(["applied", "reverted"])
161743
+ .optional()
161744
+ .describe("repair 必填:applied=标记为已应用(可补录 Query),reverted=删除 history 记录。"),
161745
+ repairReason: zod_1.z
161746
+ .string()
161747
+ .optional()
161748
+ .describe("repair 必填:修复原因。"),
161749
+ allowDdlViaExecute: zod_1.z
161750
+ .boolean()
161751
+ .optional()
161752
+ .describe("可选,默认 false。仅当需要故意绕过 migration history 时设为 true,才允许 schema DDL 走 execute;正常建表/改 schema 必须用 applyMigration。"),
161416
161753
  },
161417
161754
  annotations: {
161418
161755
  readOnlyHint: false,
@@ -161426,6 +161763,14 @@ function registerPGDatabaseTools(server, providedDeps) {
161426
161763
  const cbOpts = server.cloudBaseOptions;
161427
161764
  switch (args.action) {
161428
161765
  case "execute": {
161766
+ // Soft-block schema DDL before readiness probe so agents get migration guidance immediately.
161767
+ if (args.sql?.trim()) {
161768
+ const classification = classifySqlRisk(args.sql);
161769
+ if (isSchemaDdlRisk(classification.risk, args.sql) &&
161770
+ args.allowDdlViaExecute !== true) {
161771
+ return handleExecuteSql(args, context, deps);
161772
+ }
161773
+ }
161429
161774
  try {
161430
161775
  await ensurePgReadyOnce(cbOpts, deps);
161431
161776
  }
@@ -161448,13 +161793,15 @@ function registerPGDatabaseTools(server, providedDeps) {
161448
161793
  case "applyMigration":
161449
161794
  case "listMigrations":
161450
161795
  case "migrationDetail":
161451
- case "rollbackMigration": {
161796
+ case "rollbackMigration":
161797
+ case "repairMigration": {
161452
161798
  switch (args.action) {
161453
161799
  case "planMigration": return handlePlanMigration(args, context, deps, cbOpts);
161454
161800
  case "applyMigration": return handleApplyMigration(args, context, deps, cbOpts);
161455
161801
  case "listMigrations": return handleListMigrations(args, context, deps, cbOpts);
161456
161802
  case "migrationDetail": return handleMigrationDetail(args, context, deps, cbOpts);
161457
161803
  case "rollbackMigration": return handleRollbackMigration(args, context, deps, cbOpts);
161804
+ case "repairMigration": return handleRepairMigration(args, context, deps, cbOpts);
161458
161805
  default: return buildPgToolResult({ success: false, errorCode: "UNSUPPORTED_ACTION", message: `Unsupported action: ${args.action}` });
161459
161806
  }
161460
161807
  }
@@ -196570,7 +196917,7 @@ ${envIdSection}
196570
196917
  ## 环境信息
196571
196918
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
196572
196919
  - Node.js版本: ${process.version}
196573
- - MCP 版本:${process.env.npm_package_version || "2.23.10" || 0}
196920
+ - MCP 版本:${process.env.npm_package_version || "2.24.0" || 0}
196574
196921
  - 系统架构: ${os_1.default.arch()}
196575
196922
  - 时间: ${new Date().toISOString()}
196576
196923
  - 请求ID: ${requestId}
@@ -196651,7 +196998,7 @@ function createWrappedHandler(name, handler, server) {
196651
196998
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
196652
196999
  ide: server.ide || process.env.INTEGRATION_IDE || ''
196653
197000
  });
196654
- const mcpVersion = true ? "2.23.10" : 0;
197001
+ const mcpVersion = true ? "2.24.0" : 0;
196655
197002
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
196656
197003
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
196657
197004
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -212275,7 +212622,7 @@ exports.IDE_TYPES = [
212275
212622
  "openai-codex-cli", // OpenAI Codex CLI
212276
212623
  "augment-code", // Augment Code
212277
212624
  "github-copilot", // GitHub Copilot
212278
- "roocode", // RooCode AI编辑器
212625
+ "roocode", // RooCode AI编辑器(已 deprecated 2026-05,可考虑引导转向 Cline/ZooCode)
212279
212626
  "tongyi-lingma", // 通义灵码
212280
212627
  "trae", // Trae AI编辑器
212281
212628
  "qoder", // Qoder AI编辑器
@@ -212317,7 +212664,6 @@ exports.RAW_IDE_FILE_MAPPINGS = {
212317
212664
  ],
212318
212665
  "baidu-comate": [
212319
212666
  { path: ".comate/rules/cloudbase-rules.mdr" },
212320
- { path: ".comate/rules/cloudbaase-rules.mdr" },
212321
212667
  { path: ".comate/mcp.json", isMcpConfig: true },
212322
212668
  ],
212323
212669
  "openai-codex-cli": [
@@ -212327,10 +212673,10 @@ exports.RAW_IDE_FILE_MAPPINGS = {
212327
212673
  "augment-code": [{ path: ".augment-guidelines" }],
212328
212674
  "github-copilot": [{ path: ".github/copilot-instructions.md" }],
212329
212675
  roocode: [
212330
- { path: ".roo/rules/cloudbaase-rules.md" },
212676
+ { path: ".roo/rules/cloudbase-rules.md" },
212331
212677
  { path: ".roo/mcp.json", isMcpConfig: true },
212332
212678
  ],
212333
- "tongyi-lingma": [{ path: ".lingma/rules/cloudbaase-rules.md" }],
212679
+ "tongyi-lingma": [{ path: ".lingma/rules/cloudbase-rules.md" }],
212334
212680
  trae: [{ path: ".trae/rules/" }],
212335
212681
  qoder: [{ path: ".qoder/rules/" }],
212336
212682
  antigravity: [{ path: ".agent/rules/" }],
@@ -212370,7 +212716,7 @@ const IDE_DESCRIPTIONS = {
212370
212716
  "openai-codex-cli": "OpenAI Codex CLI",
212371
212717
  "augment-code": "Augment Code",
212372
212718
  "github-copilot": "GitHub Copilot",
212373
- roocode: "RooCode AI编辑器",
212719
+ roocode: "RooCode AI编辑器(deprecated)",
212374
212720
  "tongyi-lingma": "通义灵码",
212375
212721
  trae: "Trae AI编辑器",
212376
212722
  qoder: "Qoder AI编辑器",
@@ -212665,7 +213011,7 @@ function registerSetupTools(server) {
212665
213011
  server.registerTool("downloadTemplate", {
212666
213012
  title: "下载项目模板",
212667
213013
  description: `自动下载并部署CloudBase项目模板。
212668
- **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.23.10" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
213014
+ **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.24.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
212669
213015
  inputSchema: {
212670
213016
  template: zod_1.z
212671
213017
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -253715,6 +254061,7 @@ const os = __importStar(__webpack_require__(21820));
253715
254061
  const path = __importStar(__webpack_require__(39902));
253716
254062
  const zod_1 = __webpack_require__(21614);
253717
254063
  const cloudbase_manager_js_1 = __webpack_require__(3431);
254064
+ const tool_result_js_1 = __webpack_require__(9835);
253718
254065
  const MAX_INLINE_TEXT_BYTES = 256 * 1024;
253719
254066
  const STORAGE_READ_TEMP_PREFIX = 'cloudbase-mcp-storage-read-';
253720
254067
  // Input schema for queryStorage tool
@@ -253797,7 +254144,7 @@ function registerStorageTools(server) {
253797
254144
  // Tool 1: queryStorage - 查询存储信息(只读操作)
253798
254145
  server.registerTool("queryStorage", {
253799
254146
  title: "查询 CloudBase 存储信息",
253800
- description: "查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 环境:`DescribeEnvs.Storages[]` 列出的 bucket 是旧 NoSQL 后端的,不等于 pgstore bucket。本工具用于查看常规存储;为 PG 浏览器上传准备 bucket 时,请确认目标 bucket 是 pgstore 后端可用的,否则浏览器 `app.storage.from().upload(...)` 会得到 `STORAGE_BUCKET_NOT_FOUND` 并出现 `PUT https://undefined/`。",
254147
+ description: "⚠️ PG 模式环境请使用 queryPgStorage 而非本工具(pgstore 与旧 COS 是两套独立系统)。\n\n查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 环境:`DescribeEnvs.Storages[]` 列出的 bucket 是旧 NoSQL 后端的,不等于 pgstore bucket。本工具用于查看常规存储;为 PG 浏览器上传准备 bucket 时,请确认目标 bucket 是 pgstore 后端可用的,否则浏览器 `app.storage.from().upload(...)` 会得到 `STORAGE_BUCKET_NOT_FOUND` 并出现 `PUT https://undefined/`。",
253801
254148
  inputSchema: queryStorageInputSchema,
253802
254149
  annotations: {
253803
254150
  readOnlyHint: true,
@@ -253806,125 +254153,89 @@ function registerStorageTools(server) {
253806
254153
  }
253807
254154
  }, async (args) => {
253808
254155
  const input = args;
253809
- const manager = await getManager();
253810
- if (!manager) {
253811
- throw new Error("Failed to initialize CloudBase manager. Please check your credentials and environment configuration.");
253812
- }
253813
- const storageService = manager.storage;
253814
- switch (input.action) {
253815
- case 'list': {
253816
- let files;
253817
- if (storageOverrides?.listFiles) {
253818
- files = await storageOverrides.listFiles({ cloudPath: input.cloudPath });
253819
- }
253820
- else {
253821
- const result = await storageService.listDirectoryFiles(input.cloudPath);
253822
- files = result || [];
253823
- }
253824
- return {
253825
- content: [
253826
- {
253827
- type: "text",
253828
- text: JSON.stringify({
253829
- success: true,
253830
- data: {
253831
- action: 'list',
253832
- cloudPath: input.cloudPath,
253833
- files,
253834
- totalCount: files.length
253835
- },
253836
- message: `Successfully listed ${files.length} files in directory '${input.cloudPath}'`
253837
- }, null, 2)
253838
- }
253839
- ]
253840
- };
253841
- }
253842
- case 'info': {
253843
- let fileInfo;
253844
- if (storageOverrides?.getFileInfo) {
253845
- fileInfo = await storageOverrides.getFileInfo({ cloudPath: input.cloudPath });
253846
- }
253847
- else {
253848
- fileInfo = await storageService.getFileInfo(input.cloudPath);
253849
- }
253850
- return {
253851
- content: [
253852
- {
253853
- type: "text",
253854
- text: JSON.stringify({
253855
- success: true,
253856
- data: {
253857
- action: 'info',
253858
- cloudPath: input.cloudPath,
253859
- fileInfo
253860
- },
253861
- message: `Successfully retrieved file info for '${input.cloudPath}'`
253862
- }, null, 2)
253863
- }
253864
- ]
253865
- };
254156
+ try {
254157
+ const manager = await getManager();
254158
+ if (!manager) {
254159
+ throw new Error("Failed to initialize CloudBase manager. Please check your credentials and environment configuration.");
253866
254160
  }
253867
- case 'url': {
253868
- let temporaryUrl = "";
253869
- let fileId = "";
253870
- if (storageOverrides?.getFileUrl) {
253871
- const urlResult = await storageOverrides.getFileUrl({
253872
- cloudPath: input.cloudPath,
253873
- maxAge: input.maxAge || 3600,
253874
- });
253875
- temporaryUrl = urlResult.url;
253876
- fileId = urlResult.fileId || "";
253877
- }
253878
- else {
253879
- const result = await storageService.getTemporaryUrl([{
253880
- cloudPath: input.cloudPath,
253881
- maxAge: input.maxAge || 3600
253882
- }]);
253883
- temporaryUrl = result[0]?.url || "";
253884
- fileId = result[0]?.fileId || "";
254161
+ const storageService = manager.storage;
254162
+ switch (input.action) {
254163
+ case 'list': {
254164
+ let files;
254165
+ if (storageOverrides?.listFiles) {
254166
+ files = await storageOverrides.listFiles({ cloudPath: input.cloudPath });
254167
+ }
254168
+ else {
254169
+ const result = await storageService.listDirectoryFiles(input.cloudPath);
254170
+ files = result || [];
254171
+ }
254172
+ return {
254173
+ content: [
254174
+ {
254175
+ type: "text",
254176
+ text: JSON.stringify({
254177
+ success: true,
254178
+ data: {
254179
+ action: 'list',
254180
+ cloudPath: input.cloudPath,
254181
+ files,
254182
+ totalCount: files.length
254183
+ },
254184
+ message: `Successfully listed ${files.length} files in directory '${input.cloudPath}'`
254185
+ }, null, 2)
254186
+ }
254187
+ ]
254188
+ };
253885
254189
  }
253886
- const publicAccess = await resolveStoragePublicAccess({
253887
- cloudPath: input.cloudPath,
253888
- cloudBaseOptions,
253889
- manager,
253890
- });
253891
- return {
253892
- content: [
253893
- {
253894
- type: "text",
253895
- text: JSON.stringify({
253896
- success: true,
253897
- data: {
253898
- action: 'url',
253899
- cloudPath: input.cloudPath,
253900
- temporaryUrl,
253901
- expireTime: `${input.maxAge || 3600}秒`,
253902
- fileId,
253903
- storageCdnDomain: publicAccess.storageCdnDomain,
253904
- publicUrl: publicAccess.publicUrl,
253905
- note: "temporaryUrl 是临时签名链接,会按 expireTime 过期。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
253906
- },
253907
- message: `Successfully generated temporary URL for '${input.cloudPath}'`
253908
- }, null, 2)
253909
- }
253910
- ]
253911
- };
253912
- }
253913
- case 'read': {
253914
- const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), STORAGE_READ_TEMP_PREFIX));
253915
- const localPath = path.join(tempDir, getStorageTempFileName(input.cloudPath));
253916
- try {
253917
- if (storageOverrides?.downloadFile) {
253918
- await storageOverrides.downloadFile({ cloudPath: input.cloudPath, localPath });
254190
+ case 'info': {
254191
+ let fileInfo;
254192
+ if (storageOverrides?.getFileInfo) {
254193
+ fileInfo = await storageOverrides.getFileInfo({ cloudPath: input.cloudPath });
253919
254194
  }
253920
254195
  else {
253921
- await storageService.downloadFile({
254196
+ fileInfo = await storageService.getFileInfo(input.cloudPath);
254197
+ }
254198
+ return {
254199
+ content: [
254200
+ {
254201
+ type: "text",
254202
+ text: JSON.stringify({
254203
+ success: true,
254204
+ data: {
254205
+ action: 'info',
254206
+ cloudPath: input.cloudPath,
254207
+ fileInfo
254208
+ },
254209
+ message: `Successfully retrieved file info for '${input.cloudPath}'`
254210
+ }, null, 2)
254211
+ }
254212
+ ]
254213
+ };
254214
+ }
254215
+ case 'url': {
254216
+ let temporaryUrl = "";
254217
+ let fileId = "";
254218
+ if (storageOverrides?.getFileUrl) {
254219
+ const urlResult = await storageOverrides.getFileUrl({
253922
254220
  cloudPath: input.cloudPath,
253923
- localPath
254221
+ maxAge: input.maxAge || 3600,
253924
254222
  });
254223
+ temporaryUrl = urlResult.url;
254224
+ fileId = urlResult.fileId || "";
254225
+ }
254226
+ else {
254227
+ const result = await storageService.getTemporaryUrl([{
254228
+ cloudPath: input.cloudPath,
254229
+ maxAge: input.maxAge || 3600
254230
+ }]);
254231
+ temporaryUrl = result[0]?.url || "";
254232
+ fileId = result[0]?.fileId || "";
253925
254233
  }
253926
- const buffer = await fs.readFile(localPath);
253927
- const decoded = decodeInlineTextContent(buffer);
254234
+ const publicAccess = await resolveStoragePublicAccess({
254235
+ cloudPath: input.cloudPath,
254236
+ cloudBaseOptions,
254237
+ manager,
254238
+ });
253928
254239
  return {
253929
254240
  content: [
253930
254241
  {
@@ -253932,33 +254243,80 @@ function registerStorageTools(server) {
253932
254243
  text: JSON.stringify({
253933
254244
  success: true,
253934
254245
  data: {
253935
- action: 'read',
254246
+ action: 'url',
253936
254247
  cloudPath: input.cloudPath,
253937
- content: decoded.content,
253938
- encoding: decoded.encoding,
253939
- sizeBytes: decoded.sizeBytes,
253940
- truncated: decoded.truncated
254248
+ temporaryUrl,
254249
+ expireTime: `${input.maxAge || 3600}秒`,
254250
+ fileId,
254251
+ storageCdnDomain: publicAccess.storageCdnDomain,
254252
+ publicUrl: publicAccess.publicUrl,
254253
+ note: "temporaryUrl 是临时签名链接,会按 expireTime 过期。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
253941
254254
  },
253942
- message: decoded.truncated
253943
- ? `Successfully read text content for '${input.cloudPath}' (truncated to ${MAX_INLINE_TEXT_BYTES} bytes)`
253944
- : `Successfully read text content for '${input.cloudPath}'`
254255
+ message: `Successfully generated temporary URL for '${input.cloudPath}'`
253945
254256
  }, null, 2)
253946
254257
  }
253947
254258
  ]
253948
254259
  };
253949
254260
  }
253950
- finally {
253951
- await fs.rm(tempDir, { recursive: true, force: true });
254261
+ case 'read': {
254262
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), STORAGE_READ_TEMP_PREFIX));
254263
+ const localPath = path.join(tempDir, getStorageTempFileName(input.cloudPath));
254264
+ try {
254265
+ if (storageOverrides?.downloadFile) {
254266
+ await storageOverrides.downloadFile({ cloudPath: input.cloudPath, localPath });
254267
+ }
254268
+ else {
254269
+ await storageService.downloadFile({
254270
+ cloudPath: input.cloudPath,
254271
+ localPath
254272
+ });
254273
+ }
254274
+ const buffer = await fs.readFile(localPath);
254275
+ const decoded = decodeInlineTextContent(buffer);
254276
+ return {
254277
+ content: [
254278
+ {
254279
+ type: "text",
254280
+ text: JSON.stringify({
254281
+ success: true,
254282
+ data: {
254283
+ action: 'read',
254284
+ cloudPath: input.cloudPath,
254285
+ content: decoded.content,
254286
+ encoding: decoded.encoding,
254287
+ sizeBytes: decoded.sizeBytes,
254288
+ truncated: decoded.truncated
254289
+ },
254290
+ message: decoded.truncated
254291
+ ? `Successfully read text content for '${input.cloudPath}' (truncated to ${MAX_INLINE_TEXT_BYTES} bytes)`
254292
+ : `Successfully read text content for '${input.cloudPath}'`
254293
+ }, null, 2)
254294
+ }
254295
+ ]
254296
+ };
254297
+ }
254298
+ finally {
254299
+ await fs.rm(tempDir, { recursive: true, force: true });
254300
+ }
253952
254301
  }
254302
+ default:
254303
+ throw new Error(`Unsupported action: ${input.action}`);
253953
254304
  }
253954
- default:
253955
- throw new Error(`Unsupported action: ${input.action}`);
254305
+ }
254306
+ catch (error) {
254307
+ const message = error instanceof Error ? error.message : String(error);
254308
+ return (0, tool_result_js_1.buildJsonToolResult)({
254309
+ success: false,
254310
+ message: `queryStorage 操作失败: ${message}`,
254311
+ action: input.action,
254312
+ cloudPath: input.cloudPath,
254313
+ });
253956
254314
  }
253957
254315
  });
253958
254316
  // Tool 2: manageStorage - 管理存储文件(写操作)
253959
254317
  server.registerTool("manageStorage", {
253960
254318
  title: "管理 CloudBase 存储文件",
253961
- description: "管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 桶必须先创建后使用(与 Supabase Storage 一致:upload 前 bucket 必须存在)。浏览器 SDK `app.storage.from().upload(path, file)` 不会自动建桶,且 `path` 的第一段就是 bucket 名(例如 `covers/foo.png` → bucket=`covers`);`from('covers')` 这个参数当前不会被拼到 path 里。如果上传时浏览器看到 `STORAGE_BUCKET_NOT_FOUND` 或 `PUT https://undefined/`(DevTools 表现为 `net::ERR_NAME_NOT_RESOLVED`),先用本工具或控制台确认 / 创建对应的 pgstore bucket,再让前端重试上传,不要让前端把上传失败静默吞掉。`DescribeEnvs.Storages[]` 返回的旧 NoSQL bucket(形如 `<hash>-<envId>-<appId>`)不是可用的 pgstore bucket,切勿当作默认目标使用。",
254319
+ description: "⚠️ PG 模式环境请使用 queryPgStorage 而非本工具(pgstore 与旧 COS 是两套独立系统)。\n\n管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。\n\n📦 CloudBase PG / pgstore 桶必须先创建后使用(与 Supabase Storage 一致:upload 前 bucket 必须存在)。浏览器 SDK `app.storage.from().upload(path, file)` 不会自动建桶,且 `path` 的第一段就是 bucket 名(例如 `covers/foo.png` → bucket=`covers`);`from('covers')` 这个参数当前不会被拼到 path 里。如果上传时浏览器看到 `STORAGE_BUCKET_NOT_FOUND` 或 `PUT https://undefined/`(DevTools 表现为 `net::ERR_NAME_NOT_RESOLVED`),先用本工具或控制台确认 / 创建对应的 pgstore bucket,再让前端重试上传,不要让前端把上传失败静默吞掉。`DescribeEnvs.Storages[]` 返回的旧 NoSQL bucket(形如 `<hash>-<envId>-<appId>`)不是可用的 pgstore bucket,切勿当作默认目标使用。",
253962
254320
  inputSchema: manageStorageInputSchema,
253963
254321
  annotations: {
253964
254322
  readOnlyHint: false,
@@ -253969,196 +254327,207 @@ function registerStorageTools(server) {
253969
254327
  }
253970
254328
  }, async (args) => {
253971
254329
  const input = args;
253972
- const manager = await getManager();
253973
- if (!manager) {
253974
- throw new Error("Failed to initialize CloudBase manager. Please check your credentials and environment configuration.");
253975
- }
253976
- const storageService = manager.storage;
253977
- switch (input.action) {
253978
- case 'upload': {
253979
- const localPath = input.localPath;
253980
- const cloudPath = input.cloudPath;
253981
- if (input.isDirectory) {
253982
- if (storageOverrides?.uploadDirectory) {
253983
- await storageOverrides.uploadDirectory({ localPath, cloudPath });
254330
+ try {
254331
+ const manager = await getManager();
254332
+ if (!manager) {
254333
+ throw new Error("Failed to initialize CloudBase manager. Please check your credentials and environment configuration.");
254334
+ }
254335
+ const storageService = manager.storage;
254336
+ switch (input.action) {
254337
+ case 'upload': {
254338
+ const localPath = input.localPath;
254339
+ const cloudPath = input.cloudPath;
254340
+ if (input.isDirectory) {
254341
+ if (storageOverrides?.uploadDirectory) {
254342
+ await storageOverrides.uploadDirectory({ localPath, cloudPath });
254343
+ }
254344
+ else {
254345
+ await storageService.uploadDirectory({
254346
+ localPath,
254347
+ cloudPath,
254348
+ onProgress: (progressData) => {
254349
+ console.log("Upload directory progress:", progressData);
254350
+ }
254351
+ });
254352
+ }
253984
254353
  }
253985
254354
  else {
253986
- await storageService.uploadDirectory({
253987
- localPath,
253988
- cloudPath,
253989
- onProgress: (progressData) => {
253990
- console.log("Upload directory progress:", progressData);
253991
- }
253992
- });
254355
+ if (storageOverrides?.uploadFile) {
254356
+ await storageOverrides.uploadFile({ localPath, cloudPath });
254357
+ }
254358
+ else {
254359
+ await storageService.uploadFile({
254360
+ localPath,
254361
+ cloudPath,
254362
+ onProgress: (progressData) => {
254363
+ console.log("Upload file progress:", progressData);
254364
+ }
254365
+ });
254366
+ }
253993
254367
  }
253994
- }
253995
- else {
253996
- if (storageOverrides?.uploadFile) {
253997
- await storageOverrides.uploadFile({ localPath, cloudPath });
254368
+ // 上传成功后获取临时 URL 和公网访问信息
254369
+ let temporaryUrl = "";
254370
+ let fileId = "";
254371
+ if (storageOverrides?.getFileUrl) {
254372
+ const urlResult = await storageOverrides.getFileUrl({
254373
+ cloudPath: input.cloudPath,
254374
+ maxAge: 3600,
254375
+ });
254376
+ temporaryUrl = urlResult.url;
254377
+ fileId = urlResult.fileId || "";
253998
254378
  }
253999
254379
  else {
254000
- await storageService.uploadFile({
254001
- localPath,
254002
- cloudPath,
254003
- onProgress: (progressData) => {
254004
- console.log("Upload file progress:", progressData);
254005
- }
254006
- });
254380
+ try {
254381
+ const fileUrls = await storageService.getTemporaryUrl([{
254382
+ cloudPath: input.cloudPath,
254383
+ maxAge: 3600
254384
+ }]);
254385
+ temporaryUrl = fileUrls[0]?.url || "";
254386
+ fileId = fileUrls[0]?.fileId || "";
254387
+ }
254388
+ catch {
254389
+ // StorageOverrides 下 getTemporaryUrl 可能不可用
254390
+ }
254007
254391
  }
254008
- }
254009
- // 上传成功后获取临时 URL 和公网访问信息
254010
- let temporaryUrl = "";
254011
- let fileId = "";
254012
- if (storageOverrides?.getFileUrl) {
254013
- const urlResult = await storageOverrides.getFileUrl({
254014
- cloudPath: input.cloudPath,
254015
- maxAge: 3600,
254016
- });
254017
- temporaryUrl = urlResult.url;
254018
- fileId = urlResult.fileId || "";
254019
- }
254020
- else {
254392
+ let publicAccess = {};
254021
254393
  try {
254022
- const fileUrls = await storageService.getTemporaryUrl([{
254023
- cloudPath: input.cloudPath,
254024
- maxAge: 3600
254025
- }]);
254026
- temporaryUrl = fileUrls[0]?.url || "";
254027
- fileId = fileUrls[0]?.fileId || "";
254394
+ publicAccess = await resolveStoragePublicAccess({
254395
+ cloudPath: input.cloudPath,
254396
+ cloudBaseOptions,
254397
+ manager,
254398
+ });
254028
254399
  }
254029
254400
  catch {
254030
- // StorageOverrides 下 getTemporaryUrl 可能不可用
254401
+ // DescribeEnvs 可能不支持(IDE 模式下会绕过)
254402
+ publicAccess = {};
254031
254403
  }
254404
+ return {
254405
+ content: [
254406
+ {
254407
+ type: "text",
254408
+ text: JSON.stringify({
254409
+ success: true,
254410
+ data: {
254411
+ action: 'upload',
254412
+ localPath: input.localPath,
254413
+ cloudPath: input.cloudPath,
254414
+ isDirectory: input.isDirectory,
254415
+ temporaryUrl,
254416
+ expireTime: "1小时",
254417
+ storageCdnDomain: publicAccess.storageCdnDomain,
254418
+ publicUrl: publicAccess.publicUrl,
254419
+ note: "temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
254420
+ },
254421
+ message: `Successfully uploaded ${input.isDirectory ? 'directory' : 'file'} from '${input.localPath}' to '${input.cloudPath}'`
254422
+ }, null, 2)
254423
+ }
254424
+ ]
254425
+ };
254032
254426
  }
254033
- let publicAccess = {};
254034
- try {
254035
- publicAccess = await resolveStoragePublicAccess({
254036
- cloudPath: input.cloudPath,
254037
- cloudBaseOptions,
254038
- manager,
254039
- });
254040
- }
254041
- catch {
254042
- // DescribeEnvs 可能不支持(IDE 模式下会绕过)
254043
- publicAccess = {};
254044
- }
254045
- return {
254046
- content: [
254047
- {
254048
- type: "text",
254049
- text: JSON.stringify({
254050
- success: true,
254051
- data: {
254052
- action: 'upload',
254053
- localPath: input.localPath,
254054
- cloudPath: input.cloudPath,
254055
- isDirectory: input.isDirectory,
254056
- temporaryUrl,
254057
- expireTime: "1小时",
254058
- storageCdnDomain: publicAccess.storageCdnDomain,
254059
- publicUrl: publicAccess.publicUrl,
254060
- note: "temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
254061
- },
254062
- message: `Successfully uploaded ${input.isDirectory ? 'directory' : 'file'} from '${input.localPath}' to '${input.cloudPath}'`
254063
- }, null, 2)
254427
+ case 'download': {
254428
+ const dlCloudPath = input.cloudPath;
254429
+ const dlLocalPath = input.localPath;
254430
+ if (input.isDirectory) {
254431
+ if (storageOverrides?.downloadDirectory) {
254432
+ await storageOverrides.downloadDirectory({ cloudPath: dlCloudPath, localPath: dlLocalPath });
254433
+ }
254434
+ else {
254435
+ await storageService.downloadDirectory({
254436
+ cloudPath: dlCloudPath,
254437
+ localPath: dlLocalPath
254438
+ });
254064
254439
  }
254065
- ]
254066
- };
254067
- }
254068
- case 'download': {
254069
- const dlCloudPath = input.cloudPath;
254070
- const dlLocalPath = input.localPath;
254071
- if (input.isDirectory) {
254072
- if (storageOverrides?.downloadDirectory) {
254073
- await storageOverrides.downloadDirectory({ cloudPath: dlCloudPath, localPath: dlLocalPath });
254074
- }
254075
- else {
254076
- await storageService.downloadDirectory({
254077
- cloudPath: dlCloudPath,
254078
- localPath: dlLocalPath
254079
- });
254080
- }
254081
- }
254082
- else {
254083
- if (storageOverrides?.downloadFile) {
254084
- await storageOverrides.downloadFile({ cloudPath: dlCloudPath, localPath: dlLocalPath });
254085
254440
  }
254086
254441
  else {
254087
- await storageService.downloadFile({
254088
- cloudPath: dlCloudPath,
254089
- localPath: dlLocalPath
254090
- });
254091
- }
254092
- }
254093
- return {
254094
- content: [
254095
- {
254096
- type: "text",
254097
- text: JSON.stringify({
254098
- success: true,
254099
- data: {
254100
- action: 'download',
254101
- cloudPath: input.cloudPath,
254102
- localPath: input.localPath,
254103
- isDirectory: input.isDirectory
254104
- },
254105
- message: `Successfully downloaded ${input.isDirectory ? 'directory' : 'file'} from '${input.cloudPath}' to '${input.localPath}'`
254106
- }, null, 2)
254442
+ if (storageOverrides?.downloadFile) {
254443
+ await storageOverrides.downloadFile({ cloudPath: dlCloudPath, localPath: dlLocalPath });
254107
254444
  }
254108
- ]
254109
- };
254110
- }
254111
- case 'delete': {
254112
- if (!input.force) {
254445
+ else {
254446
+ await storageService.downloadFile({
254447
+ cloudPath: dlCloudPath,
254448
+ localPath: dlLocalPath
254449
+ });
254450
+ }
254451
+ }
254113
254452
  return {
254114
254453
  content: [
254115
254454
  {
254116
254455
  type: "text",
254117
254456
  text: JSON.stringify({
254118
- success: false,
254119
- error: "Delete operation requires confirmation",
254120
- message: "Please set force: true to confirm deletion. This action cannot be undone."
254457
+ success: true,
254458
+ data: {
254459
+ action: 'download',
254460
+ cloudPath: input.cloudPath,
254461
+ localPath: input.localPath,
254462
+ isDirectory: input.isDirectory
254463
+ },
254464
+ message: `Successfully downloaded ${input.isDirectory ? 'directory' : 'file'} from '${input.cloudPath}' to '${input.localPath}'`
254121
254465
  }, null, 2)
254122
254466
  }
254123
254467
  ]
254124
254468
  };
254125
254469
  }
254126
- if (input.isDirectory) {
254127
- if (storageOverrides?.deleteDirectory) {
254128
- await storageOverrides.deleteDirectory({ cloudPath: input.cloudPath });
254129
- }
254130
- else {
254131
- await storageService.deleteDirectory(input.cloudPath);
254470
+ case 'delete': {
254471
+ if (!input.force) {
254472
+ return {
254473
+ content: [
254474
+ {
254475
+ type: "text",
254476
+ text: JSON.stringify({
254477
+ success: false,
254478
+ error: "Delete operation requires confirmation",
254479
+ message: "Please set force: true to confirm deletion. This action cannot be undone."
254480
+ }, null, 2)
254481
+ }
254482
+ ]
254483
+ };
254132
254484
  }
254133
- }
254134
- else {
254135
- if (storageOverrides?.deleteFiles) {
254136
- await storageOverrides.deleteFiles({ cloudPaths: [input.cloudPath] });
254485
+ if (input.isDirectory) {
254486
+ if (storageOverrides?.deleteDirectory) {
254487
+ await storageOverrides.deleteDirectory({ cloudPath: input.cloudPath });
254488
+ }
254489
+ else {
254490
+ await storageService.deleteDirectory(input.cloudPath);
254491
+ }
254137
254492
  }
254138
254493
  else {
254139
- await storageService.deleteFile([input.cloudPath]);
254494
+ if (storageOverrides?.deleteFiles) {
254495
+ await storageOverrides.deleteFiles({ cloudPaths: [input.cloudPath] });
254496
+ }
254497
+ else {
254498
+ await storageService.deleteFile([input.cloudPath]);
254499
+ }
254140
254500
  }
254501
+ return {
254502
+ content: [
254503
+ {
254504
+ type: "text",
254505
+ text: JSON.stringify({
254506
+ success: true,
254507
+ data: {
254508
+ action: 'delete',
254509
+ cloudPath: input.cloudPath,
254510
+ isDirectory: input.isDirectory,
254511
+ deleted: true
254512
+ },
254513
+ message: `Successfully deleted ${input.isDirectory ? 'directory' : 'file'} '${input.cloudPath}'`
254514
+ }, null, 2)
254515
+ }
254516
+ ]
254517
+ };
254141
254518
  }
254142
- return {
254143
- content: [
254144
- {
254145
- type: "text",
254146
- text: JSON.stringify({
254147
- success: true,
254148
- data: {
254149
- action: 'delete',
254150
- cloudPath: input.cloudPath,
254151
- isDirectory: input.isDirectory,
254152
- deleted: true
254153
- },
254154
- message: `Successfully deleted ${input.isDirectory ? 'directory' : 'file'} '${input.cloudPath}'`
254155
- }, null, 2)
254156
- }
254157
- ]
254158
- };
254519
+ default:
254520
+ throw new Error(`Unsupported action: ${input.action}`);
254159
254521
  }
254160
- default:
254161
- throw new Error(`Unsupported action: ${input.action}`);
254522
+ }
254523
+ catch (error) {
254524
+ const message = error instanceof Error ? error.message : String(error);
254525
+ return (0, tool_result_js_1.buildJsonToolResult)({
254526
+ success: false,
254527
+ message: `manageStorage 操作失败: ${message}`,
254528
+ action: input.action,
254529
+ cloudPath: input.cloudPath,
254530
+ });
254162
254531
  }
254163
254532
  });
254164
254533
  }