From f3c294bc75c68dc87b0311e927aa389155c0b516 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Wed, 29 Apr 2026 04:32:32 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/source/skills/web-development/SKILL.md | 21 +++++++++++++++ .../skills/web-development/frameworks.md | 26 +++++++++++++++++++ mcp/src/tools/hosting.ts | 4 +-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/config/source/skills/web-development/SKILL.md b/config/source/skills/web-development/SKILL.md index f13b3ebf..25594152 100644 --- a/config/source/skills/web-development/SKILL.md +++ b/config/source/skills/web-development/SKILL.md @@ -132,6 +132,27 @@ Use this section only when the Web project needs CloudBase platform features. - Use hash routing by default when the project lacks server-side route rewrites - If the user does not specify a root path, avoid deploying directly to the site root by default +### Subdirectory deployment requirements + +When deploying to a subdirectory (e.g., `/vite-test`), the `uploadFiles` tool requires specific pre-deployment checks: + +1. **cloudPath format**: Relative to hosting root, no leading `/` + - Correct: `cloudPath: 'vite-test'` + - Wrong: `cloudPath: '/vite-test'` or `cloudPath: './vite-test'` + +2. **Build configuration**: Must set `base`/`publicPath`/`assetPrefix` to match the deployment path + - For Vite: `base: '/vite-test/'` (absolute path with leading and trailing slashes) + - Forbidden: `base: './'` (causes 404 when URL lacks trailing slash) + - See `frameworks.md` for detailed Vite base configuration guidance + +3. **Mandatory pre-deployment checklist**: + - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path + - [ ] Build has been re-run after config change + - [ ] Built `index.html` references assets with correct paths (not absolute root `/`) + - [ ] `cloudPath` has no leading `/` + +4. **Common 404 cause**: Using `./` relative paths in build config. When accessing `https://env.tcloudbase.com/vite-test` (no trailing slash), relative paths resolve incorrectly. + ### CloudBase quick start ```js diff --git a/config/source/skills/web-development/frameworks.md b/config/source/skills/web-development/frameworks.md index 68d2935f..7530834d 100644 --- a/config/source/skills/web-development/frameworks.md +++ b/config/source/skills/web-development/frameworks.md @@ -19,6 +19,32 @@ - Keep environment-specific values in `.env` or the project's existing config pattern instead of hardcoding them into UI files. - Check route base paths, asset paths, and build output behavior before deployment. +### Vite base configuration for subdirectory deployment + +When deploying to a subdirectory (e.g., `https://example.com/vite-test/`), the Vite `base` configuration is critical: + +1. **Required format**: Set `base` to an absolute path with leading and trailing slashes, matching the deployment path: + ```js + // vite.config.ts + export default defineConfig({ + base: '/vite-test/', // Correct: absolute path with leading and trailing slashes + }) + ``` + +2. **Forbidden patterns** - These will cause 404 errors when URL lacks trailing slash: + ```js + base: './', // WRONG: relative path breaks when URL is /vite-test (no trailing slash) + base: '', // WRONG: empty base means root deployment + base: 'vite-test', // WRONG: missing leading and trailing slashes + ``` + +3. **Why this matters**: When a user accesses `https://example.com/vite-test` (without trailing slash), relative paths like `./assets/index.js` resolve to `https://example.com/assets/index.js` instead of `https://example.com/vite-test/assets/index.js`, causing 404 errors. + +4. **Pre-deployment checklist**: + - [ ] `vite.config.ts` has `base: '/your-subdirectory/'` set correctly + - [ ] Build command has been re-run after config change + - [ ] Built `dist/index.html` references assets with correct paths (e.g., `/vite-test/assets/...`) + ## Routing and build defaults - Use the existing router if present; do not switch routing libraries without an explicit requirement. diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts index 9a68b50a..6a9d9958 100644 --- a/mcp/src/tools/hosting.ts +++ b/mcp/src/tools/hosting.ts @@ -258,10 +258,10 @@ export function registerHostingTools(server: ExtendedMcpServer) { "uploadFiles", { title: "上传静态文件", - description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。", + description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。\n\n⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认:\n1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404)\n2. 已重新构建(修改配置后必须重新 build)\n3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/')\n任何一项未通过时,禁止调用此工具。", inputSchema: { localPath: z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"), - cloudPath: z.string().optional().describe("静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。"), + cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认。云存储对象路径请改用 manageStorage。"), files: z.array(z.object({ localPath: z.string(), cloudPath: z.string() From 572d33ed63ee2a2d63fe4adb588305d5ddaf810d Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Sat, 9 May 2026 00:28:53 +0800 Subject: [PATCH 2/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/source/skills/web-development/SKILL.md | 9 +- doc/mcp-tools.md | 94 +++++++++++++------ mcp/src/tools/hosting.ts | 4 +- .../tools/storage-hosting-guidance.test.ts | 5 + scripts/tools.json | 4 +- 5 files changed, 81 insertions(+), 35 deletions(-) diff --git a/config/source/skills/web-development/SKILL.md b/config/source/skills/web-development/SKILL.md index d7af2120..98a8124f 100644 --- a/config/source/skills/web-development/SKILL.md +++ b/config/source/skills/web-development/SKILL.md @@ -169,7 +169,7 @@ Use this section only when the Web project needs CloudBase platform features. ### Static hosting defaults - Build before deployment -- Prefer relative asset paths for static hosting compatibility +- For root deployment, keep root-compatible asset paths; for subdirectory deployment, set `base` / `publicPath` / `assetPrefix` to the exact absolute deploy path and do not use `./`. - Use hash routing by default when the project lacks server-side route rewrites - If the user does not specify a root path, avoid deploying directly to the site root by default @@ -190,9 +190,14 @@ When deploying to a subdirectory (e.g., `/vite-test`), the `uploadFiles` tool re - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path - [ ] Build has been re-run after config change - [ ] Built `index.html` references assets with correct paths (not absolute root `/`) + - [ ] Upload target is the full build output directory contents (usually `dist/`), not only `index.html` - [ ] `cloudPath` has no leading `/` -4. **Common 404 cause**: Using `./` relative paths in build config. When accessing `https://env.tcloudbase.com/vite-test` (no trailing slash), relative paths resolve incorrectly. +4. **Mandatory post-upload verification**: + - Call `findFiles` with the deployment prefix and confirm uploaded files include `index.html` plus emitted JS/CSS/assets under that prefix + - Do not treat deployment as complete if only `index.html` exists or emitted assets are missing + +5. **Common 404 cause**: Using `./` relative paths in build config. When accessing `https://env.tcloudbase.com/vite-test` (no trailing slash), relative paths resolve incorrectly. ### CloudBase quick start diff --git a/doc/mcp-tools.md b/doc/mcp-tools.md index fabbadab..c6dc4d60 100644 --- a/doc/mcp-tools.md +++ b/doc/mcp-tools.md @@ -284,7 +284,7 @@ CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即 --- ### `writeNoSqlDatabaseStructure` -修改 NoSQL 数据库结构,支持创建/删除集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。 +创建、删除和管理 NoSQL 数据库集合(collection)。支持创建新集合、删除现有集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。当需要新建集合时,使用 action=createCollection。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。 #### 参数 @@ -423,7 +423,7 @@ CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即 --- ### `writeNoSqlDatabaseContent` -修改 NoSQL 数据库数据记录。⚠️ 本工具为服务端管理工具,用于管理端/运维端操作(如后台脚本、数据迁移、批量修改)。当任务要求编写客户端应用代码时(例如「用 JS SDK 登录并插入数据」、「在前端读写数据库」),不应使用本工具,而应在项目代码中编写 @cloudbase/js-sdk 客户端代码(如 app.database().collection().add()、db.collection().where().get() 等)。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 `$set`、`$inc`、`$push` 等更新操作符;如果直接传「字段到值的普通对象」这类内容,底层会把它当作替换内容,存在覆盖整条文档的风险。更新嵌套对象中的某个字段时必须使用点号路径,例如把 `address.city` 设为 `shenzhen`;如果把整个 `address` 对象作为 `$set` 的值传入,则整个 `address` 对象会被替换,同级其他字段将丢失。若集合中的角色/档案文档会在前端通过 `db.collection(...).doc(uid)` 读取,请确保文档 `_id` 就是该 `uid`;不要用按 `uid` 条件查询再配合 `upsert=true` 的方式去更新 `users` / `profiles`,否则经常会生成一个不同的 `_id`,导致后续 `doc(uid)` 读取命中不到。 +修改 NoSQL 数据库数据记录。⚠️ 本工具为服务端管理工具,用于管理端/运维端操作(如后台脚本、数据迁移、批量修改)。当任务要求编写客户端应用代码时(例如「用 JS SDK 登录并插入数据」、「在前端读写数据库」),不应使用本工具,而应在项目代码中编写 @cloudbase/js-sdk 客户端代码(如 app.database().collection().add()、db.collection().where().get() 等)。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 `$set`、`$inc`、`$push` 等更新操作符;如果直接传「字段到值的普通对象」这类内容,底层会把它当作替换内容,存在覆盖整条文档的风险。⚠️ 嵌套对象部分更新务必使用点号路径:要更新 `shipping.city`,应传 `$set: \{"shipping.city": "guangzhou"\}`,绝不能传 `$set: \{"shipping": \{"city": "guangzhou"\}\}`(后者会丢失 shipping 下的其他字段)。若集合中的角色/档案文档会在前端通过 `db.collection(...).doc(uid)` 读取,请确保文档 `_id` 就是该 `uid`;不要用按 `uid` 条件查询再配合 `upsert=true` 的方式去更新 `users` / `profiles`,否则经常会生成一个不同的 `_id`,导致后续 `doc(uid)` 读取命中不到。 #### 参数 @@ -459,7 +459,7 @@ CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即 { name: "update", type: "union", - description: `更新内容(对象或字符串,推荐对象)(update 操作必填)。按 MongoDB 更新语义传入 MgoUpdate:部分更新请使用 \`$set\`、\`$inc\`、\`$unset\`、\`$push\` 等操作符,例如使用 \`$set\` 更新 \`status\`;不要直接传“字段到值的普通对象”,否则可能替换整条文档。更新嵌套字段时必须使用点号路径,例如通过 \`$set\` 更新 \`address.city\`;不要把整个 \`address\` 对象作为 \`$set\` 的值传入,否则会替换整个 \`address\` 对象。`, + description: `更新内容(对象或字符串,推荐对象)(update 操作必填)。按 MongoDB 更新语义传入 MgoUpdate:部分更新请使用 \`$set\`、\`$inc\`、\`$unset\`、\`$push\` 等操作符,例如使用 \`$set\` 更新 \`status\`;不要直接传"字段到值的普通对象",否则可能替换整条文档。 ⚠️ 嵌套字段必须用点号路径(如 \`shipping.city\`),禁止整对象替换: - ❌ 错误:{ "$set": { "shipping": { "city": "guangzhou" } } } — shipping 被整块替换,原有 address/province 等字段全部丢失 - ✅ 正确:{ "$set": { "shipping.city": "guangzhou" } } — 仅更新 city,shipping 下其他字段保留`, }, { name: "isMulti", @@ -654,7 +654,20 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w --- ### `queryFunctions` -函数域统一只读入口。通过更自解释的 action 查询函数列表、函数详情、日志、层、触发器和代码下载地址。 +函数域统一只读入口。通过更自解释的 action 查询 CloudBase 云函数列表、函数详情、执行日志、层、触发器和代码下载地址。 + +**分页说明**:`listFunctions`、`listLayers` 支持 `limit` 和 `offset` 参数。 +- `limit`: 分页数量,默认值由后端决定 +- `offset`: 分页偏移,从 0 开始 +- 示例:`queryFunctions(action="listFunctions", offset=10, limit=10)` + +**查询 CloudBase 云函数日志**:使用 `action="listFunctionLogs"`,需要提供 `functionName` 参数。 +- 示例:`queryFunctions(action="listFunctionLogs", functionName="my-function")` +- 如需查看日志详情:`queryFunctions(action="getFunctionLogDetail", requestId="xxx")` + +**区分 `queryLogs` 工具**: +- 本工具用于查询特定 CloudBase 云函数的执行日志 +- `queryLogs` 工具用于搜索 CLS 日志服务(跨服务日志聚合) #### 参数 @@ -664,27 +677,27 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w name: "action", type: "string", required: true, - description: `只读操作类型,例如 listFunctions、getFunctionDetail、listFunctionLogs 可填写的值: "listFunctions", "getFunctionDetail", "listFunctionLogs", "getFunctionLogDetail", "listFunctionLayers", "listLayers", "listLayerVersions", "getLayerVersionDetail", "listFunctionTriggers", "getFunctionDownloadUrl"`, + description: `只读操作类型: - \`listFunctions\`: 列出所有 CloudBase 云函数 - \`getFunctionDetail\`: 获取 CloudBase 云函数详情(需要 functionName) - \`listFunctionLogs\`: 查询 CloudBase 云函数执行日志(需要 functionName) - \`getFunctionLogDetail\`: 获取日志详情(需要 requestId) - \`listFunctionLayers\`: 列出函数绑定的层 - \`listLayers\`: 列出所有层 - \`listLayerVersions\`: 列出层的版本 - \`getLayerVersionDetail\`: 获取层版本详情 - \`listFunctionTriggers\`: 列出函数触发器 - \`getFunctionDownloadUrl\`: 获取函数代码下载地址 可填写的值: "listFunctions", "getFunctionDetail", "listFunctionLogs", "getFunctionLogDetail", "listFunctionLayers", "listLayers", "listLayerVersions", "getLayerVersionDetail", "listFunctionTriggers", "getFunctionDownloadUrl"`, }, { name: "functionName", type: "string", - description: `函数名称。listFunctionLogs、getFunctionDetail、listFunctionLayers、listFunctionTriggers、getFunctionDownloadUrl 时必填`, + description: `CloudBase 云函数名称。\`getFunctionDetail\`、\`listFunctionLogs\`、\`listFunctionLayers\`、\`listFunctionTriggers\`、\`getFunctionDownloadUrl\` 时必填`, }, { name: "limit", type: "number", - description: `分页数量。列表类 action 可选`, + description: `分页数量(limit)。列表类 action 可选,默认值由后端决定`, }, { name: "offset", type: "number", - description: `分页偏移。列表类 action 可选`, + description: `分页偏移(offset)。列表类 action 可选,默认 0`, }, { name: "codeSecret", type: "string", - description: `代码保护密钥`, + description: `代码保护密钥,用于解密函数代码`, }, { name: "startTime", @@ -699,17 +712,17 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w { name: "requestId", type: "string", - description: `日志 requestId。获取日志详情时必填`, + description: `日志请求 ID。\`getFunctionLogDetail\` 操作必填,可从 \`listFunctionLogs\` 结果中获取`, }, { name: "qualifier", type: "string", - description: `函数版本,日志查询时可选`, + description: `函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选`, }, { name: "runtime", type: "string", - description: `层查询的运行时筛选`, + description: `层查询的运行时筛选,如 Nodejs18.15`, }, { name: "searchKey", @@ -719,12 +732,12 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w { name: "layerName", type: "string", - description: `层名称。层相关 action 必填`, + description: `层名称。\`listLayerVersions\`、\`getLayerVersionDetail\` 操作必填`, }, { name: "layerVersion", type: "number", - description: `层版本号。获取层版本详情时必填`, + description: `层版本号。\`getLayerVersionDetail\` 操作必填`, } ]} /> @@ -1012,7 +1025,14 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w --- ### `uploadFiles` -上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。 +上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。 + +⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认: +1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404) +2. 已重新构建(修改配置后必须重新 build) +3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/') +4. 将完整构建产物目录内容上传到目标子目录(通常是 dist/ 的全部内容),不要只上传 index.html +任何一项未通过时,禁止调用此工具。上传完成后,必须使用 findFiles 按目标前缀检查 index.html 和构建生成的资源文件是否都已存在。 #### 参数 @@ -1026,7 +1046,7 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w { name: "cloudPath", type: "string", - description: `静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。`, + description: `静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认,上传后再用 findFiles 验证 index.html 与构建资源文件已存在。云存储对象路径请改用 manageStorage。`, }, { name: "files", @@ -1239,7 +1259,7 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w --- ### `queryStorage` -查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。 +查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导出 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。 #### 参数 @@ -1268,7 +1288,7 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w --- ### `manageStorage` -管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。 +管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。 #### 参数 @@ -1314,7 +1334,7 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w 支持的模板: - react: React + CloudBase 全栈应用模板 - vue: Vue + CloudBase 全栈应用模板 -- miniprogram: 微信小程序 + 云开发模板 +- miniprogram: 微信小程序 + 云开发模板 - uniapp: UniApp + CloudBase 跨端应用模板 - rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置 @@ -1402,8 +1422,9 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w - 需要 auth-web 指南时:searchKnowledgeBase(mode=skill, skillName=auth-web) - 需要 cloudbase-agent 指南时:searchKnowledgeBase(mode=skill, skillName=cloudbase-agent) - 固定技能文档 (skill) 查询当前支持 23 个固定文档,分别是: - 文档名:ai-model-nodejs 文档介绍:Use this skill when developing Node.js backend services or CloudBase cloud functions (Express/Koa/NestJS, serverless, backend APIs) that need CloudBase AI managed model capabilities via @cloudbase/node-sdk ≥3.16.0. It covers generateText, streamText, and CloudBase-managed generateImage(); within the CloudBase SDK family, Node SDK is the only SDK that exposes managed image generation. Do NOT use this skill when the user explicitly wants to call Tencent Cloud / Hunyuan model APIs directly, use separate cloud-side quotas or billing, or avoid the CloudBase AI managed access layer. Not for browser/Web apps (use ai-model-web) or WeChat Mini Program (use ai-model-wechat). + 固定技能文档 (skill) 查询当前支持 25 个固定文档,分别是: + 文档名:skills 文档介绍:Unified CloudBase execution guide for all-in-one skill installs. Use this as the first entry point for CloudBase app tasks, especially existing applications that already contain TODOs, fixed pages, and active handlers. +文档名:ai-model-nodejs 文档介绍:Use this skill when developing Node.js backend services or CloudBase cloud functions (Express/Koa/NestJS, serverless, backend APIs) that need AI capabilities. Features text generation (generateText), streaming (streamText), AND image generation (generateImage) via @cloudbase/node-sdk ≥3.16.0. Built-in models include Hunyuan (hunyuan-2.0-instruct-20251111 recommended), DeepSeek (deepseek-v3.2 recommended), and hunyuan-image for images. This is the ONLY SDK that supports image generation. NOT for browser/Web apps (use ai-model-web) or WeChat Mini Program (use ai-model-wechat). 文档名:ai-model-web 文档介绍:Use this skill when developing browser/Web applications (React/Vue/Angular, static websites, SPAs) that need AI capabilities. Features text generation (generateText) and streaming (streamText) via @cloudbase/js-sdk. Built-in models include Hunyuan (hunyuan-2.0-instruct-20251111 recommended) and DeepSeek (deepseek-v3.2 recommended). NOT for Node.js backend (use ai-model-nodejs), WeChat Mini Program (use ai-model-wechat), or image generation (Node SDK only). 文档名:ai-model-wechat 文档介绍:Use this skill when developing WeChat Mini Programs (小程序, 企业微信小程序, wx.cloud-based apps) that need AI capabilities. Features text generation (generateText) and streaming (streamText) with callback support (onText, onEvent, onFinish) via wx.cloud.extend.AI. Built-in models include Hunyuan (hunyuan-2.0-instruct-20251111 recommended) and DeepSeek (deepseek-v3.2 recommended). API differs from JS/Node SDK - streamText requires data wrapper, generateText returns raw response. NOT for browser/Web apps (use ai-model-web), Node.js backend (use ai-model-nodejs), or image generation (not supported). 文档名:auth-nodejs 文档介绍:CloudBase Node SDK auth guide for server-side identity, user lookup, and custom login tickets. This skill should be used when Node.js code must read caller identity, inspect end users, or bridge an existing user system into CloudBase; not when configuring providers or building client login UI. @@ -1413,6 +1434,7 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w 文档名:cloud-functions 文档介绍:CloudBase function runtime guide for building, deploying, and debugging your own Event Functions or HTTP Functions. This skill should be used when users need application runtime code on CloudBase, not when they are merely calling CloudBase official platform APIs. 文档名:cloud-storage-web 文档介绍:Complete guide for CloudBase cloud storage using Web SDK (@cloudbase/js-sdk) - upload, download, temporary URLs, file management, and best practices. 文档名:cloudbase-agent 文档介绍:Build and deploy AI agents with CloudBase Agent SDK (TypeScript & Python). Implements the AG-UI protocol for streaming agent-UI communication. Use when deploying agent servers, using LangGraph/LangChain/CrewAI adapters, building custom adapters, understanding AG-UI protocol events, or building web/mini-program UI clients. Supports both TypeScript (@cloudbase/agent-server) and Python (cloudbase-agent-server via FastAPI). +文档名:cloudbase-cli 文档介绍:CloudBase CLI (tcb, 云开发CLI, Tencent CloudBase命令行) resource management skill. This skill should be used when users need to deploy cloud functions, manage CloudRun apps, upload files to storage, query NoSQL/MySQL databases, deploy static hosting, set access permissions, or configure CORS/domains/routing via tcb commands. Also use for CI/CD pipeline scripting, batch operations, terminal-based CloudBase management, or when the user prefers CLI over SDK/MCP. 文档名:cloudbase-platform 文档介绍:CloudBase platform overview and routing guide. This skill should be used when users need high-level capability selection, platform concepts, console navigation, or cross-platform best practices before choosing a more specific implementation skill. 文档名:cloudrun-development 文档介绍:CloudBase Run backend development rules (Function mode/Container mode). Use this skill when deploying backend services that require long connections, multi-language support, custom environments, or AI agent development. 文档名:data-model-creation 文档介绍:Optional advanced tool for complex data modeling. For simple table creation, use relational-database-tool directly with SQL statements. @@ -1428,11 +1450,11 @@ Manage SQL database resources. Supports MySQL provisioning, MySQL destruction, w 文档名:web-development 文档介绍:Use when users need to implement, integrate, debug, build, deploy, or validate a Web frontend after the product direction is already clear, especially for React, Vue, Vite, browser flows, or CloudBase Web integration. OpenAPI 文档 (openapi) 查询当前支持 5 个 API 文档,分别是: - API名:functions API介绍:Cloud Functions API - 云函数 HTTP API -API名:storage API介绍:Storage API - 云存储 HTTP API -API名:mysqldb API介绍:MySQL RESTful API - 云开发 MySQL 数据库 HTTP API + API名:mysqldb API介绍:MySQL RESTful API - 云开发 MySQL 数据库 HTTP API +API名:functions API介绍:Cloud Functions API - 云函数 HTTP API API名:auth API介绍:Authentication API - 身份认证 HTTP API API名:cloudrun API介绍:CloudRun API - 云托管服务 HTTP API +API名:storage API介绍:Storage API - 云存储 HTTP API #### 参数 @@ -1447,12 +1469,12 @@ API名:cloudrun API介绍:CloudRun API - 云托管服务 HTTP API { name: "skillName", type: "string", - description: `mode=skill 时指定。技能名称。 可填写的值: "ai-model-nodejs", "ai-model-web", "ai-model-wechat", "auth-nodejs", "auth-tool", "auth-web", "auth-wechat", "cloud-functions", "cloud-storage-web", "cloudbase-agent", "cloudbase-platform", "cloudrun-development", "data-model-creation", "http-api", "miniprogram-development", "no-sql-web-sdk", "no-sql-wx-mp-sdk", "ops-inspector", "relational-database-tool", "relational-database-web", "spec-workflow", "ui-design", "web-development"`, + description: `mode=skill 时指定。技能名称。 可填写的值: "skills", "ai-model-nodejs", "ai-model-web", "ai-model-wechat", "auth-nodejs", "auth-tool", "auth-web", "auth-wechat", "cloud-functions", "cloud-storage-web", "cloudbase-agent", "cloudbase-cli", "cloudbase-platform", "cloudrun-development", "data-model-creation", "http-api", "miniprogram-development", "no-sql-web-sdk", "no-sql-wx-mp-sdk", "ops-inspector", "relational-database-tool", "relational-database-web", "spec-workflow", "ui-design", "web-development"`, }, { name: "apiName", type: "string", - description: `mode=openapi 时指定。API 名称。 可填写的值: "functions", "storage", "mysqldb", "auth", "cloudrun"`, + description: `mode=openapi 时指定。API 名称。 可填写的值: "mysqldb", "functions", "auth", "cloudrun", "storage"`, }, { name: "action", @@ -2136,6 +2158,15 @@ API名:cloudrun API介绍:CloudRun API - 云托管服务 HTTP API ### `queryLogs` 日志域统一只读入口。支持检查日志服务状态并搜索 CLS 日志。 +**重要区分**: +- 查询云函数日志:使用 `queryFunctions(action="listFunctionLogs", functionName="xxx")` +- 查询 CLS 日志(跨服务日志聚合):使用本工具 `queryLogs(action="searchLogs")` + +**适用场景**: +- 检查 CLS 日志服务是否开通:`action="checkLogService"` +- 跨服务日志搜索(如搜索所有 ERROR 日志):`action="searchLogs"` +- 按 CLS 语法检索特定服务的日志:`action="searchLogs", service="tcb|tcbr"` + #### 参数 diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts index 6a9d9958..b77410cf 100644 --- a/mcp/src/tools/hosting.ts +++ b/mcp/src/tools/hosting.ts @@ -258,10 +258,10 @@ export function registerHostingTools(server: ExtendedMcpServer) { "uploadFiles", { title: "上传静态文件", - description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。\n\n⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认:\n1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404)\n2. 已重新构建(修改配置后必须重新 build)\n3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/')\n任何一项未通过时,禁止调用此工具。", + description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。\n\n⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认:\n1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404)\n2. 已重新构建(修改配置后必须重新 build)\n3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/')\n4. 将完整构建产物目录内容上传到目标子目录(通常是 dist/ 的全部内容),不要只上传 index.html\n任何一项未通过时,禁止调用此工具。上传完成后,必须使用 findFiles 按目标前缀检查 index.html 和构建生成的资源文件是否都已存在。", inputSchema: { localPath: z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"), - cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认。云存储对象路径请改用 manageStorage。"), + cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认,上传后再用 findFiles 验证 index.html 与构建资源文件已存在。云存储对象路径请改用 manageStorage。"), files: z.array(z.object({ localPath: z.string(), cloudPath: z.string() diff --git a/mcp/src/tools/storage-hosting-guidance.test.ts b/mcp/src/tools/storage-hosting-guidance.test.ts index 4ef1644e..8198daf8 100644 --- a/mcp/src/tools/storage-hosting-guidance.test.ts +++ b/mcp/src/tools/storage-hosting-guidance.test.ts @@ -106,6 +106,11 @@ describe("storage and hosting tool guidance", () => { expect(tools.uploadFiles.meta.description).toContain("仅用于 Web 站点部署"); expect(tools.uploadFiles.meta.description).toContain("manageStorage"); expect(tools.uploadFiles.meta.description).toContain("通常不需要调用此工具"); + expect(tools.uploadFiles.meta.description).toContain("子目录部署强制二次确认"); + expect(tools.uploadFiles.meta.description).toContain("不要只上传 index.html"); + expect(tools.uploadFiles.meta.description).toContain("findFiles"); + expect(tools.uploadFiles.meta.inputSchema.cloudPath.description).toContain("不要前导 '/'"); + expect(tools.uploadFiles.meta.inputSchema.cloudPath.description).toContain("findFiles"); expect(tools.uploadFiles.meta.inputSchema.cloudPath.description).toContain("云存储对象路径请改用 manageStorage"); expect(tools.manageStorage.meta.description).toContain("仅用于 COS/Storage 对象"); expect(tools.manageStorage.meta.description).toContain("不用于静态网站托管"); diff --git a/scripts/tools.json b/scripts/tools.json index 25de23b4..68737421 100644 --- a/scripts/tools.json +++ b/scripts/tools.json @@ -1037,7 +1037,7 @@ }, { "name": "uploadFiles", - "description": "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。", + "description": "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。\n\n⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认:\n1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404)\n2. 已重新构建(修改配置后必须重新 build)\n3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/')\n4. 将完整构建产物目录内容上传到目标子目录(通常是 dist/ 的全部内容),不要只上传 index.html\n任何一项未通过时,禁止调用此工具。上传完成后,必须使用 findFiles 按目标前缀检查 index.html 和构建生成的资源文件是否都已存在。", "inputSchema": { "type": "object", "properties": { @@ -1047,7 +1047,7 @@ }, "cloudPath": { "type": "string", - "description": "静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。" + "description": "静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认,上传后再用 findFiles 验证 index.html 与构建资源文件已存在。云存储对象路径请改用 manageStorage。" }, "files": { "type": "array", From 92ef8caf52c97e4e3d841fe68b14b36fb582a53d Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Sat, 9 May 2026 00:42:14 +0800 Subject: [PATCH 3/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/skill-quality-standards.test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/skill-quality-standards.test.js b/tests/skill-quality-standards.test.js index b5855249..29353ef6 100644 --- a/tests/skill-quality-standards.test.js +++ b/tests/skill-quality-standards.test.js @@ -57,6 +57,23 @@ describe('skill quality standards', () => { expect(raw).toContain('Browser origin `http://127.0.0.1:4173` -> whitelist entry `127.0.0.1:4173`'); }); + test('web-development documents subdirectory hosting checks before uploadFiles', () => { + const raw = readSourceSkill('web-development'); + const frameworks = readFile('config', 'source', 'skills', 'web-development', 'frameworks.md'); + + expect(raw).toContain("cloudPath: 'vite-test'"); + expect(raw).toContain("cloudPath: '/vite-test'"); + expect(raw).toContain("Forbidden: `base: './'`"); + expect(raw).toContain('Build has been re-run after config change'); + expect(raw).toContain('Call `findFiles` with the deployment prefix'); + expect(raw).toContain('not only `index.html`'); + + expect(frameworks).toContain("base: '/vite-test/'"); + expect(frameworks).toContain("base: './', // WRONG"); + expect(frameworks).toContain('Build command has been re-run after config change'); + expect(frameworks).toContain('without trailing slash'); + }); + test('cloudrun-development explains the MinNum cold-start tradeoff with a default of 1', () => { const raw = readSourceSkill('cloudrun-development'); From 5b06880eb3cf8633a18e3dab17f1d1a21b1c9c06 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Sat, 9 May 2026 20:55:41 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../source/skills/cloudbase-platform/SKILL.md | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/config/source/skills/cloudbase-platform/SKILL.md b/config/source/skills/cloudbase-platform/SKILL.md index a6f66e5c..0c66523b 100644 --- a/config/source/skills/cloudbase-platform/SKILL.md +++ b/config/source/skills/cloudbase-platform/SKILL.md @@ -149,7 +149,29 @@ Example structure for operation recording: - Combine with static hosting file paths to construct final access addresses - **Important**: If access address is a directory, it must end with `/` -3. **Cloud Storage Public URL**: +3. **Static Hosting Deployment (`uploadFiles`)**: + - Use `uploadFiles` tool to deploy Web sites to static hosting (not for cloud storage uploads) + - **`cloudPath` format**: Relative to hosting root, NO leading `/` + - Correct: `cloudPath: 'vite-test'` (deploys to `https://domain/vite-test/`) + - Wrong: `cloudPath: '/vite-test'` or `cloudPath: './vite-test'` + - **Subdirectory deployment requirements** (CRITICAL to avoid 404): + - Must set `base`/`publicPath`/`assetPrefix` in build config to match deployment path + - For Vite: `base: '/vite-test/'` (absolute path with leading AND trailing slashes) + - **Forbidden**: `base: './'` (causes 404 when URL lacks trailing slash) + - Must rebuild after changing config + - Must verify built `index.html` references assets with correct paths (not absolute root `/`) + - **Pre-deployment checklist** (must complete before calling `uploadFiles`): + - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path + - [ ] Project has been rebuilt after config change + - [ ] Built assets reference correct paths (not root `/`) + - [ ] `cloudPath` has no leading `/` + - **Post-upload verification** (mandatory): + - Call `findFiles` with prefix matching `cloudPath` + - Confirm `index.html` AND emitted JS/CSS/assets all exist under target prefix + - Do NOT treat deployment as complete if only `index.html` exists + - **Common 404 cause**: Using `./` relative paths in build config → when accessing `https://domain/vite-test` (no trailing slash), relative paths resolve incorrectly + +4. **Cloud Storage Public URL**: - **CRITICAL**: `manageStorage(action=upload)` and `queryStorage(action=url)` return `temporaryUrl` which is a temporary signed URL that expires (default 1 hour). Do NOT use this as a permanent public URL. - To get the permanent public access URL for a cloud storage object: 1. Call `envQuery(action=info)` to get environment details From 0f607d6c70b3c1ab65695e9ff691a50fc8cd1d31 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Sun, 10 May 2026 08:29:02 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcp/src/tools/hosting.ts | 64 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts index b77410cf..3e0dcf69 100644 --- a/mcp/src/tools/hosting.ts +++ b/mcp/src/tools/hosting.ts @@ -282,6 +282,16 @@ export function registerHostingTools(server: ExtendedMcpServer) { files?: Array<{ localPath: string; cloudPath: string }>; ignore?: string | string[] }) => { + // Validate cloudPath format: reject leading '/' + if (cloudPath && cloudPath.startsWith('/')) { + throw new Error( + `[uploadFiles] cloudPath 格式错误:不要前导 '/'。\n` + + `你传入的是: "${cloudPath}"\n` + + `正确格式: "${cloudPath.replace(/^\/+/, '')}"\n` + + `说明:cloudPath 是相对于托管根目录的路径,不要前导 '/'。例如部署到 /vite-test 子目录时,cloudPath 应为 "vite-test" 而非 "/vite-test"。` + ); + } + const cloudbase = await getManager() let result: unknown; try { @@ -303,6 +313,43 @@ export function registerHostingTools(server: ExtendedMcpServer) { const staticDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain; const accessUrl = buildHostingAccessUrl(staticDomain, cloudPath, localPath); + // 自动部署验证:上传完成后,使用 findFiles 验证文件已存在 + let verificationResult = null; + let verificationPassed = false; + const normalizedCloudPath = (cloudPath ?? '').trim().replace(/^\/+|\/+$/g, ''); + const verifyPrefix = normalizedCloudPath ? `${normalizedCloudPath}/` : ''; + + try { + verificationResult = await cloudbase.hosting.findFiles({ + prefix: verifyPrefix, + maxKeys: 50 + }); + + const filesResult = verificationResult as Record; + const filesList = (filesResult.Contents as Array>) || []; + + // 检查 index.html 是否存在 + const indexFile = filesList.find((f: Record) => { + const key = f.Key as string || ''; + return key.endsWith('/index.html') || key === 'index.html' || key === `${normalizedCloudPath}/index.html`; + }); + + verificationPassed = filesList.length > 0; + + logCloudBaseResult(server.logger, { + message: 'Deployment verification complete', + verifyPrefix, + fileCount: filesList.length, + indexHtmlFound: !!indexFile, + passed: verificationPassed + }); + } catch (verifyErr) { + logCloudBaseResult(server.logger, { + message: 'Deployment verification failed', + error: verifyErr instanceof Error ? verifyErr.message : String(verifyErr) + }); + } + // Send deployment notification to CodeBuddy IDE try { const envId = await getEnvId(cloudBaseOptions); @@ -340,6 +387,16 @@ export function registerHostingTools(server: ExtendedMcpServer) { // Error is already logged in sendDeployNotification } + // Build next_step based on verification result + const nextStep = !verificationPassed ? { + tool: "findFiles", + action: "verify", + suggested_args: { + prefix: verifyPrefix || '/' + }, + message: "部署验证未通过:未能在云端找到上传的文件。请使用 findFiles 工具检查文件是否已正确上传,并确认 cloudPath 格式是否正确(不要前导 '/')。" + } : undefined; + return { content: [ { @@ -347,8 +404,11 @@ export function registerHostingTools(server: ExtendedMcpServer) { text: JSON.stringify({ ...uploadResult, staticDomain, - message: "文件上传成功", - accessUrl: accessUrl + message: verificationPassed ? "文件上传成功,部署验证通过" : "文件上传成功,但部署验证未通过,请使用 findFiles 检查文件是否已正确上传", + accessUrl: accessUrl, + deploymentVerified: verificationPassed, + ...(verificationResult ? { verificationDetails: verificationResult } : {}), + ...(nextStep ? { next_step: nextStep } : {}) }, null, 2) } ] From 52a2f1025ed11e81864af9fd47fdfe83b758c914 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Mon, 11 May 2026 15:20:55 +0800 Subject: [PATCH 6/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcp/src/tools/hosting.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts index 3e0dcf69..ff6b5fa6 100644 --- a/mcp/src/tools/hosting.ts +++ b/mcp/src/tools/hosting.ts @@ -258,10 +258,10 @@ export function registerHostingTools(server: ExtendedMcpServer) { "uploadFiles", { title: "上传静态文件", - description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。\n\n⚠️ 子目录部署强制二次确认:若部署到子路径(如 /vite-test),调用前必须确认:\n1. 已在构建配置中设置 base/publicPath/assetPrefix(值必须与部署子目录一致,如部署到 /vite-test 则设为 '/vite-test/';禁止使用 './' 等相对路径,否则访问 URL 不带尾部斜杠时会导致资源 404)\n2. 已重新构建(修改配置后必须重新 build)\n3. 已验证构建产物中的资源引用路径已更新(非绝对根路径 '/')\n4. 将完整构建产物目录内容上传到目标子目录(通常是 dist/ 的全部内容),不要只上传 index.html\n任何一项未通过时,禁止调用此工具。上传完成后,必须使用 findFiles 按目标前缀检查 index.html 和构建生成的资源文件是否都已存在。", + description: "上传文件到静态网站托管,仅用于 Web 站点部署。云存储对象上传请使用 manageStorage。\n\n⚠️ 部署到子目录(如 /vite-test)前必须完成:\n1. 【修改配置】在 vite.config.ts 中设置 base: '/vite-test/'(必须带前后斜杠,禁止 './' 或 '')\n2. 【重新构建】运行 npm run build(修改配置后必须重新 build)\n3. 【验证产物】检查 dist/index.html 中资源引用路径是 /vite-test/... 而非 /... 或 ./...\n4. 【完整上传】上传整个 dist/ 目录内容,不要只上传 index.html\n5. 【cloudPath 格式】填 'vite-test'(不要前导 '/')\n未完成上述步骤前,禁止调用此工具。上传后必须用 findFiles 验证 index.html 和资源文件已存在。", inputSchema: { localPath: z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"), - cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/',例如 'vite-test' 而非 '/vite-test'。部署到子路径时请先完成上方强制二次确认,上传后再用 findFiles 验证 index.html 与构建资源文件已存在。云存储对象路径请改用 manageStorage。"), + cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/'。例: 部署到 /vite-test 则填 'vite-test'(不是 '/vite-test')。必须先完成: 1) vite.config.ts 中 base 设为 '/vite-test/' 2) 重新 build 3) 验证 dist/index.html 资源路径正确 4) 上传整个 dist/ 内容。未完成前禁止调用。"), files: z.array(z.object({ localPath: z.string(), cloudPath: z.string() From 5198d3d777b90fe8d0e51e439ca7fb3bccd231e5 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Mon, 11 May 2026 19:34:06 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/prompts/cloudbase-platform.mdx | 24 +++++++++++++++- doc/prompts/web-development.mdx | 28 ++++++++++++++++++- mcp/src/tools/hosting.ts | 6 ++-- .../tools/storage-hosting-guidance.test.ts | 2 +- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/doc/prompts/cloudbase-platform.mdx b/doc/prompts/cloudbase-platform.mdx index 52b7a076..1e5fbe7a 100644 --- a/doc/prompts/cloudbase-platform.mdx +++ b/doc/prompts/cloudbase-platform.mdx @@ -187,7 +187,29 @@ Example structure for operation recording: - Combine with static hosting file paths to construct final access addresses - **Important**: If access address is a directory, it must end with `/` -3. **Cloud Storage Public URL**: +3. **Static Hosting Deployment (`uploadFiles`)**: + - Use `uploadFiles` tool to deploy Web sites to static hosting (not for cloud storage uploads) + - **`cloudPath` format**: Relative to hosting root, NO leading `/` + - Correct: `cloudPath: 'vite-test'` (deploys to `https://domain/vite-test/`) + - Wrong: `cloudPath: '/vite-test'` or `cloudPath: './vite-test'` + - **Subdirectory deployment requirements** (CRITICAL to avoid 404): + - Must set `base`/`publicPath`/`assetPrefix` in build config to match deployment path + - For Vite: `base: '/vite-test/'` (absolute path with leading AND trailing slashes) + - **Forbidden**: `base: './'` (causes 404 when URL lacks trailing slash) + - Must rebuild after changing config + - Must verify built `index.html` references assets with correct paths (not absolute root `/`) + - **Pre-deployment checklist** (must complete before calling `uploadFiles`): + - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path + - [ ] Project has been rebuilt after config change + - [ ] Built assets reference correct paths (not root `/`) + - [ ] `cloudPath` has no leading `/` + - **Post-upload verification** (mandatory): + - Call `findFiles` with prefix matching `cloudPath` + - Confirm `index.html` AND emitted JS/CSS/assets all exist under target prefix + - Do NOT treat deployment as complete if only `index.html` exists + - **Common 404 cause**: Using `./` relative paths in build config → when accessing `https://domain/vite-test` (no trailing slash), relative paths resolve incorrectly + +4. **Cloud Storage Public URL**: - **CRITICAL**: `manageStorage(action=upload)` and `queryStorage(action=url)` return `temporaryUrl` which is a temporary signed URL that expires (default 1 hour). Do NOT use this as a permanent public URL. - To get the permanent public access URL for a cloud storage object: 1. Call `envQuery(action=info)` to get environment details diff --git a/doc/prompts/web-development.mdx b/doc/prompts/web-development.mdx index 3fb64436..b5f4001b 100644 --- a/doc/prompts/web-development.mdx +++ b/doc/prompts/web-development.mdx @@ -206,10 +206,36 @@ Use this section only when the Web project needs CloudBase platform features. ### Static hosting defaults - Build before deployment -- Prefer relative asset paths for static hosting compatibility +- For root deployment, keep root-compatible asset paths; for subdirectory deployment, set `base` / `publicPath` / `assetPrefix` to the exact absolute deploy path and do not use `./`. - Use hash routing by default when the project lacks server-side route rewrites - If the user does not specify a root path, avoid deploying directly to the site root by default +### Subdirectory deployment requirements + +When deploying to a subdirectory (e.g., `/vite-test`), the `uploadFiles` tool requires specific pre-deployment checks: + +1. **cloudPath format**: Relative to hosting root, no leading `/` + - Correct: `cloudPath: 'vite-test'` + - Wrong: `cloudPath: '/vite-test'` or `cloudPath: './vite-test'` + +2. **Build configuration**: Must set `base`/`publicPath`/`assetPrefix` to match the deployment path + - For Vite: `base: '/vite-test/'` (absolute path with leading and trailing slashes) + - Forbidden: `base: './'` (causes 404 when URL lacks trailing slash) + - See `frameworks.md` for detailed Vite base configuration guidance + +3. **Mandatory pre-deployment checklist**: + - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path + - [ ] Build has been re-run after config change + - [ ] Built `index.html` references assets with correct paths (not absolute root `/`) + - [ ] Upload target is the full build output directory contents (usually `dist/`), not only `index.html` + - [ ] `cloudPath` has no leading `/` + +4. **Mandatory post-upload verification**: + - Call `findFiles` with the deployment prefix and confirm uploaded files include `index.html` plus emitted JS/CSS/assets under that prefix + - Do not treat deployment as complete if only `index.html` exists or emitted assets are missing + +5. **Common 404 cause**: Using `./` relative paths in build config. When accessing `https://env.tcloudbase.com/vite-test` (no trailing slash), relative paths resolve incorrectly. + ### CloudBase quick start ```js diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts index ff6b5fa6..97004370 100644 --- a/mcp/src/tools/hosting.ts +++ b/mcp/src/tools/hosting.ts @@ -258,10 +258,10 @@ export function registerHostingTools(server: ExtendedMcpServer) { "uploadFiles", { title: "上传静态文件", - description: "上传文件到静态网站托管,仅用于 Web 站点部署。云存储对象上传请使用 manageStorage。\n\n⚠️ 部署到子目录(如 /vite-test)前必须完成:\n1. 【修改配置】在 vite.config.ts 中设置 base: '/vite-test/'(必须带前后斜杠,禁止 './' 或 '')\n2. 【重新构建】运行 npm run build(修改配置后必须重新 build)\n3. 【验证产物】检查 dist/index.html 中资源引用路径是 /vite-test/... 而非 /... 或 ./...\n4. 【完整上传】上传整个 dist/ 目录内容,不要只上传 index.html\n5. 【cloudPath 格式】填 'vite-test'(不要前导 '/')\n未完成上述步骤前,禁止调用此工具。上传后必须用 findFiles 验证 index.html 和资源文件已存在。", + description: "上传文件到静态网站托管,仅用于 Web 站点部署。云存储对象上传请使用 manageStorage。\n\n⚠️ 必须上传整个构建目录(如 dist/),不能只上传 index.html。\n\n⚠️ 【子目录部署强制二次确认】部署到子目录(如 /vite-test)前必须完成:\n1. 【修改配置】在 vite.config.ts 中设置 base: '/vite-test/'(必须带前后斜杠,禁止 './' 或 '')\n2. 【重新构建】运行 npm run build(修改配置后必须重新 build)\n3. 【验证产物】检查 dist/index.html 中资源引用路径是 /vite-test/... 而非 /... 或 ./...\n4. 【完整上传】上传整个 dist/ 目录内容,不要只上传 index.html\n5. 【cloudPath 格式】填 'vite-test'(不要前导 '/')\n未完成上述步骤前,禁止调用此工具。上传后必须用 findFiles 验证 index.html 和资源文件已存在。", inputSchema: { - localPath: z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"), - cloudPath: z.string().optional().describe("静态托管云端路径,相对于托管根目录,不要前导 '/'。例: 部署到 /vite-test 则填 'vite-test'(不是 '/vite-test')。必须先完成: 1) vite.config.ts 中 base 设为 '/vite-test/' 2) 重新 build 3) 验证 dist/index.html 资源路径正确 4) 上传整个 dist/ 内容。未完成前禁止调用。"), + localPath: z.string().optional().describe("本地构建输出目录的绝对路径(如 /path/to/project/dist)。⚠️ 必须上传整个构建目录,不能只上传 index.html。例如 Vite 项目:localPath='/path/to/dist',cloudPath='vite-test',这样会上传 dist/ 下所有文件到 /vite-test/ 前缀下。如果只传 index.html,JS/CSS 资源会缺失导致 404。"), + cloudPath: z.string().optional().describe("静态托管云端路径前缀,相对于托管根目录,不要前导 '/'。例: 部署到 /vite-test 则填 'vite-test'(不是 '/vite-test')。必须先完成: 1) vite.config.ts 中 base 设为 '/vite-test/' 2) 重新 build 3) 验证 dist/index.html 资源路径正确。未完成前禁止调用。上传后必须用 findFiles 验证文件已存在。云存储对象路径请改用 manageStorage。"), files: z.array(z.object({ localPath: z.string(), cloudPath: z.string() diff --git a/mcp/src/tools/storage-hosting-guidance.test.ts b/mcp/src/tools/storage-hosting-guidance.test.ts index 8198daf8..051b2982 100644 --- a/mcp/src/tools/storage-hosting-guidance.test.ts +++ b/mcp/src/tools/storage-hosting-guidance.test.ts @@ -105,7 +105,7 @@ describe("storage and hosting tool guidance", () => { expect(tools.uploadFiles.meta.description).toContain("仅用于 Web 站点部署"); expect(tools.uploadFiles.meta.description).toContain("manageStorage"); - expect(tools.uploadFiles.meta.description).toContain("通常不需要调用此工具"); + expect(tools.uploadFiles.meta.description).toContain("必须上传整个构建目录"); expect(tools.uploadFiles.meta.description).toContain("子目录部署强制二次确认"); expect(tools.uploadFiles.meta.description).toContain("不要只上传 index.html"); expect(tools.uploadFiles.meta.description).toContain("findFiles"); From 2c8617a32f7ec5e8c1d259ca322a938137114585 Mon Sep 17 00:00:00 2001 From: CodeBuddy Attribution Bot Date: Mon, 11 May 2026 19:51:50 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix(attribution):=20uploadFiles=20MCP=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=20cloudPath=20=E6=A0=BC=E5=BC=8F=E4=B8=8D?= =?UTF-8?q?=E6=B8=85=E4=B8=94=E7=BC=BA=E5=B0=91=E9=83=A8=E7=BD=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=89=98=E7=AE=A1=E9=83=A8=E7=BD=B2=E5=90=8E=20404=20?= =?UTF-8?q?(issue=5Fmoj04v8k=5Frh94tl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/source/skills/web-development/SKILL.md | 33 ++--- .../references/deployment-checklist.md | 126 ++++++++++++++++++ mcp/src/tools/hosting.ts | 2 +- tests/skill-quality-standards.test.js | 25 ++-- 4 files changed, 154 insertions(+), 32 deletions(-) create mode 100644 config/source/skills/web-development/references/deployment-checklist.md diff --git a/config/source/skills/web-development/SKILL.md b/config/source/skills/web-development/SKILL.md index 98a8124f..e8c4cc84 100644 --- a/config/source/skills/web-development/SKILL.md +++ b/config/source/skills/web-development/SKILL.md @@ -126,6 +126,9 @@ Use this skill for Web engineering work such as: - Use CloudBase Web SDK and static hosting guidance only when the project actually needs CloudBase platform features. - Reuse `auth-tool` and `auth-web` for login or provider readiness instead of re-describing those flows here. +**⚠️ CRITICAL: Subdirectory deployment (≠ root deployment)** +When deploying to a subdirectory (e.g., `/vite-test`), you MUST follow the mandatory checklist in `references/deployment-checklist.md` BEFORE calling `uploadFiles`. Skipping any step causes 404 errors. Read that file first if the task involves subdirectory deployment. + ## Core workflow ### 1. Choose the right engineering path @@ -173,31 +176,17 @@ Use this section only when the Web project needs CloudBase platform features. - Use hash routing by default when the project lacks server-side route rewrites - If the user does not specify a root path, avoid deploying directly to the site root by default -### Subdirectory deployment requirements - -When deploying to a subdirectory (e.g., `/vite-test`), the `uploadFiles` tool requires specific pre-deployment checks: - -1. **cloudPath format**: Relative to hosting root, no leading `/` - - Correct: `cloudPath: 'vite-test'` - - Wrong: `cloudPath: '/vite-test'` or `cloudPath: './vite-test'` - -2. **Build configuration**: Must set `base`/`publicPath`/`assetPrefix` to match the deployment path - - For Vite: `base: '/vite-test/'` (absolute path with leading and trailing slashes) - - Forbidden: `base: './'` (causes 404 when URL lacks trailing slash) - - See `frameworks.md` for detailed Vite base configuration guidance +### Subdirectory deployment (⚠️ MANDATORY checklist) -3. **Mandatory pre-deployment checklist**: - - [ ] Build config (`base`/`publicPath`/`assetPrefix`) matches deployment path - - [ ] Build has been re-run after config change - - [ ] Built `index.html` references assets with correct paths (not absolute root `/`) - - [ ] Upload target is the full build output directory contents (usually `dist/`), not only `index.html` - - [ ] `cloudPath` has no leading `/` +**⚠️ CRITICAL: You MUST follow the complete checklist in `references/deployment-checklist.md` before calling `uploadFiles` for subdirectory deployment.** -4. **Mandatory post-upload verification**: - - Call `findFiles` with the deployment prefix and confirm uploaded files include `index.html` plus emitted JS/CSS/assets under that prefix - - Do not treat deployment as complete if only `index.html` exists or emitted assets are missing +Quick reference: +- `cloudPath` format: no leading `/` (e.g., `'vite-test'`, NOT `'/vite-test'`) +- Build config: `base: '/vite-test/'` (absolute path, leading AND trailing slashes) +- Upload scope: entire `dist/` directory, not only `index.html` +- Post-upload: call `findFiles` to verify all files exist -5. **Common 404 cause**: Using `./` relative paths in build config. When accessing `https://env.tcloudbase.com/vite-test` (no trailing slash), relative paths resolve incorrectly. +**READ THIS FILE FIRST**: `references/deployment-checklist.md` (contains mandatory step-by-step workflow with confirmation requirements) ### CloudBase quick start diff --git a/config/source/skills/web-development/references/deployment-checklist.md b/config/source/skills/web-development/references/deployment-checklist.md new file mode 100644 index 00000000..54d16765 --- /dev/null +++ b/config/source/skills/web-development/references/deployment-checklist.md @@ -0,0 +1,126 @@ +# ⚠️ MANDATORY: Subdirectory Deployment Checklist + +**You MUST complete ALL steps below before calling `uploadFiles` for subdirectory deployment. Skipping any step will cause 404 errors.** + +--- + +## Pre-Deployment (MUST complete in order) + +### Step 1: Read and confirm build configuration +**Action**: Read `vite.config.ts` (or `next.config.js`, `vue.config.js`, `webpack.config.js`) + +**Check**: +- [ ] `base` (Vite) / `publicPath` (Webpack/Vue) / `assetPrefix` (Next.js) is set +- [ ] Value is `'/your-subdirectory/'` format (absolute path, leading AND trailing slashes) +- [ ] NOT `./`, NOT `''`, NOT `'your-subdirectory'` (missing slashes) + +**Example (Vite)**: +```ts +// vite.config.ts +export default defineConfig({ + base: '/vite-test/', // ✓ Correct + // base: './', // ✗ WRONG - causes 404 + // base: '', // ✗ WRONG - deploys to root + // base: 'vite-test', // ✗ WRONG - missing slashes +}) +``` + +**YOU MUST OUTPUT**: "Step 1 completed: read vite.config.ts, base is set to '/vite-test/'" + +--- + +### Step 2: Rebuild after config change +**Action**: Run build command (e.g., `npm run build`) + +**Check**: +- [ ] Build command was run AFTER changing config +- [ ] Build completed successfully (zero errors) +- [ ] `dist/` (or build output dir) contains updated files + +**YOU MUST OUTPUT**: "Step 2 completed: rebuilt project with `npm run build`" + +--- + +### Step 3: Verify build output paths +**Action**: Read `dist/index.html` (or equivalent build output) + +**Check**: +- [ ] Resource references use `/your-subdirectory/...` format +- [ ] Example: `