diff --git a/config/source/guideline/cloudbase/SKILL.md b/config/source/guideline/cloudbase/SKILL.md
index 731eceef..eedbd14c 100644
--- a/config/source/guideline/cloudbase/SKILL.md
+++ b/config/source/guideline/cloudbase/SKILL.md
@@ -291,6 +291,7 @@ Prefer long-term memory when available: write the scenarios and working rules th
- Refer to the `web-development` skill for deployment process
- `uploadFiles` is for static hosting only; if the task needs a COS object that must be queried or polled with the storage SDK, use `manageStorage` / `queryStorage`
- Remind users that CDN has a few minutes of cache after deployment
+- **Subdirectory deployment**: when deploying to a subdirectory (e.g., `/vite-test`), `base`/`publicPath`/`assetPrefix` must be set to the absolute subdirectory path (e.g., `'/vite-test/'`), NOT `'./'` or empty. After changing the build config, rebuild and verify asset paths in the output before calling `uploadFiles`. The `cloudPath` parameter must not start with `/` (use `'vite-test'`, not `'/vite-test'`).
**Backend Deployment:**
- **Cloud Functions**: Refer to the `cloud-functions` skill - Runtime cannot be changed after creation, must select correct runtime initially
diff --git a/config/source/skills/web-development/SKILL.md b/config/source/skills/web-development/SKILL.md
index f13b3ebf..eba79252 100644
--- a/config/source/skills/web-development/SKILL.md
+++ b/config/source/skills/web-development/SKILL.md
@@ -128,10 +128,24 @@ 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
+- Deploy the entire `dist/` directory contents, not just `index.html`
- 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 checklist
+
+When deploying to a subdirectory (e.g., `/vite-test`), the following must be verified **before calling `uploadFiles`**. If any item fails, stop and fix it first:
+
+1. **Build config `base`/`publicPath`/`assetPrefix` must be set to the absolute subdirectory path** (e.g., deploy target `/vite-test` → Vite `base: '/vite-test/'`, Webpack `publicPath: '/vite-test/'`). **Forbidden**: `'./'`, empty string, or `'/'` — these cause 404 for assets when the access URL lacks a trailing slash.
+2. **Rebuild after changing build config** — modifying `base`/`publicPath` without rebuilding leaves stale references in `dist/`.
+3. **Verify build output** — check that asset references in `dist/index.html` use the subdirectory prefix (e.g., `src="/vite-test/assets/..."`), not root-relative paths (`src="/assets/..."`).
+
+`uploadFiles` `cloudPath` format: relative to hosting root, **no leading `/`**. Example: `'vite-test'` (not `'/vite-test'`). The `localPath` should point to the `dist/` directory.
+
+### Root deployment
+
+When deploying to the site root (`/`), `cloudPath` can be omitted or left empty. For Vite projects, `base: '/'` (the default) works correctly.
+
### CloudBase quick start
```js
diff --git a/doc/mcp-tools.md b/doc/mcp-tools.md
index 926b8147..dd05678d 100644
--- a/doc/mcp-tools.md
+++ b/doc/mcp-tools.md
@@ -24,7 +24,7 @@
modifyDataModel | 基于Mermaid classDiagram创建数据模型。为保持兼容性,工具名仍为 modifyDataModel;当前仅支持创建新模型,不支持更新现有模型结构。内置异步任务监控,自动轮询直至完成或超时。 |
queryFunctions | 函数域统一只读入口。通过更自解释的 action 查询函数列表、函数详情、日志、层、触发器和代码下载地址。 |
manageFunctions | 函数域统一写入口。通过 action 管理函数创建、代码更新、配置更新、调用函数、触发器和层绑定。危险操作需要显式 confirm=true。 |
-uploadFiles | 上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。 |
+uploadFiles | 上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。⚠️ 子目录部署必检项(部署到非根路径时,调用 uploadFiles 前必须逐项确认):1. 构建配置中的 base/publicPath/assetPrefix 必须设为与部署目标一致的绝对路径(如部署到 /vite-test,则 base 设为 '/vite-test/',带前导和尾部斜杠)。禁止使用 './' 或空字符串,否则子目录部署后静态资源会因路径解析错误而 404。2. 修改构建配置后必须重新 build。3. 必须验证构建产物中的资源引用路径已更新为非绝对根路径 '/'。任何一项未通过,禁止调用 uploadFiles。 |
deleteFiles | 删除静态网站托管的文件或文件夹 |
findFiles | 搜索静态网站托管的文件 |
domainManagement | 统一的域名管理工具,支持绑定、解绑、查询和修改域名配置 |
@@ -418,7 +418,13 @@ classDiagram
---
### `uploadFiles`
-上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。
+上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。
+
+⚠️ 子目录部署必检项(部署到非根路径时,调用 uploadFiles 前必须逐项确认):
+1. 构建配置中的 base/publicPath/assetPrefix 必须设为与部署目标一致的绝对路径(如部署到 /vite-test,则 base 设为 '/vite-test/',带前导和尾部斜杠)。禁止使用 './' 或空字符串,否则子目录部署后静态资源会因路径解析错误而 404。
+2. 修改构建配置后必须重新 build。
+3. 必须验证构建产物中的资源引用路径已更新为非绝对根路径 '/'。
+任何一项未通过,禁止调用 uploadFiles。
#### 参数
@@ -426,7 +432,7 @@ classDiagram
| 参数名 | 类型 | 必填 | 说明 |
localPath | string | | 本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。 |
-cloudPath | string | | 静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。 |
+cloudPath | string | | 静态托管云端路径,相对于托管根目录,不要带前导 '/'。例如 'vite-test' 表示部署到 /vite-test/ 子目录,而非 '/vite-test'。若上传整个 dist 目录到子目录,cloudPath 填子目录名(如 'vite-test'),localPath 填 dist 绝对路径。云存储对象路径请改用 manageStorage。 |
files | array of object | | 多文件上传配置 默认值: [] |
files[].localPath | string | 是 | |
files[].cloudPath | string | 是 | |
diff --git a/doc/prompts/auth-tool.mdx b/doc/prompts/auth-tool.mdx
index 2ad5e451..8f714608 100644
--- a/doc/prompts/auth-tool.mdx
+++ b/doc/prompts/auth-tool.mdx
@@ -132,7 +132,7 @@ Recommended MCP request:
```json
{
"success": true,
- "envId": "env-xxx",
+ "envId": "your-full-env-id",
"loginMethods": {
"usernamePassword": true,
"email": true,
@@ -173,6 +173,7 @@ Parameter mapping for downstream Web auth code:
- `UserNameLogin` also enables the broader password-login surface exposed by `auth.signInWithPassword({ username|email|phone, password })`
- `SmsVerificationConfig.Type = "apis"` requires both `Name` and `Method`
- `EnvId` is always the CloudBase environment ID, not the publishable key
+- If the conversation only contains an environment alias, nickname, or other shorthand, resolve it to the canonical full `EnvId` first before generating auth config, SDK init examples, or console links
Internal behavior of `manageAppAuth(action="patchLoginStrategy")`:
diff --git a/doc/prompts/auth-web.mdx b/doc/prompts/auth-web.mdx
index a2d37f28..da477d4c 100644
--- a/doc/prompts/auth-web.mdx
+++ b/doc/prompts/auth-web.mdx
@@ -83,6 +83,7 @@ Keep local `references/...` paths for files that ship with the current skill dir
- Using `signInWithEmailAndPassword` or `signUpWithEmailAndPassword` for username-style accounts such as `admin` and `editor`.
- Keeping the login or register account input as `type="email"` when the task explicitly says the account identifier is a plain username string.
- Starting implementation before calling `queryAppAuth(action="getLoginConfig")` and enabling `usernamePassword` when it is still off.
+- **Treating `auth.getUser()` returning a user as proof of real login.** When the SDK is initialized with a `publishableKey` / `accessKey`, it may silently create an anonymous session. A route guard's `checkAuth()` must verify that the user actually signed in with username/password (e.g. check `session.loginType !== 'ANONYMOUS'` or that `user.user_metadata?.username` exists), not just that `getUser()` returns non-null. Otherwise unauthenticated visitors pass the guard, protected pages render without a real user, and role-based UI (edit / delete buttons gated on `currentUser.role`) breaks because `currentUser` has no role record.
## Overview
@@ -95,9 +96,8 @@ Keep local `references/...` paths for files that ship with the current skill dir
**Use Case**: Web frontend projects using `@cloudbase/js-sdk@2.24.0+` for user authentication
**Key Benefits**: Supabase-like Auth API shape, supports phone, email, anonymous, username/password, and third-party login methods
-**Official `@cloudbase/js-sdk` CDN**: `https://static.cloudbase.net/cloudbase-js-sdk/latest/cloudbase.full.js`
-Use the same CDN address as `web-development`. Prefer npm installation in modern bundler projects, and use the CDN form for static HTML, no-build demos, or low-friction examples.
+Use npm installation for modern Web projects. In React, Vue, Vite, and other bundler-based apps, install and import `@cloudbase/js-sdk` from the project dependencies instead of using a CDN script.
## Prerequisites
@@ -107,6 +107,7 @@ Use the same CDN address as `web-development`. Prefer npm installation in modern
### Parameter map
- For username-style identifiers, the required precondition is `loginMethods.usernamePassword === true` from `queryAppAuth(action="getLoginConfig")`. If it is false, enable it with `manageAppAuth(action="patchLoginStrategy", patch={ usernamePassword: true })` before wiring frontend auth code.
+- If the conversation only provides an environment alias, nickname, or other shorthand, resolve it with `envQuery(action="list", alias=..., aliasExact=true)` first and use the returned canonical full `EnvId` for SDK init, console links, and generated config. Do not pass alias-like short forms directly into `cloudbase.init({ env })`.
- Treat CloudBase Web Auth as **Supabase-like**, not “every `supabase-js` auth example is valid unchanged”
- When `queryAppAuth` / `manageAppAuth` returns `sdkStyle: "supabase-like"` and `sdkHints`, follow those method and parameter hints first
- `auth.signInWithOtp({ phone })` and `auth.signUp({ phone })` use the phone number in a `phone` field, not `phone_number`
@@ -121,10 +122,11 @@ Use the same CDN address as `web-development`. Prefer npm installation in modern
## Quick Start
```js
+// npm install @cloudbase/js-sdk
import cloudbase from '@cloudbase/js-sdk'
const app = cloudbase.init({
- env: `env`, // CloudBase environment ID
+ env: 'your-full-env-id', // Canonical full CloudBase environment ID resolved from envQuery or the console, not an alias or shorthand
region: `region`, // CloudBase environment Region, default 'ap-shanghai'
accessKey: 'publishable key', // required, get from auth-tool-cloudbase
auth: { detectSessionInUrl: true }, // required
@@ -141,8 +143,9 @@ If the current task has not retrieved a real Publishable Key, omit `accessKey` i
**1. Phone OTP (Recommended)**
- Automatically use `auth-tool-cloudbase` to turn on `SMS Login` through `manageAppAuth`
+- For phone registration, send the phone number to `auth.signUp({ phone, ... })` first, then call the returned `verifyOtp({ token })`. Do not swap the order.
```js
-const { data, error } = await auth.signInWithOtp({ phone: '13800138000' })
+const { data, error } = await auth.signUp({ phone: '13800138000' })
const { data: loginData, error: loginError } = await data.verifyOtp({ token:'123456' })
```
@@ -154,10 +157,35 @@ const { data: loginData, error: loginError } = await data.verifyOtp({ token: '65
```
**3. Password**
+
+All auth methods return `{ data, error }`. Always check `error` first:
```js
-const usernameLogin = await auth.signInWithPassword({ username: 'test_user', password: 'pass123' })
-const emailLogin = await auth.signInWithPassword({ email: 'user@example.com', password: 'pass123' })
-const phoneLogin = await auth.signInWithPassword({ phone: '13800138000', password: 'pass123' })
+// Login — returns { data: { user, session }, error: null } on success
+const { data, error } = await auth.signInWithPassword({ username: 'test_user', password: 'pass123' })
+if (error) {
+ // Handle login failure (wrong password, user not found, provider not enabled)
+ console.error('Login failed:', error.message)
+ return false
+}
+// data.user.id is the uid; data.session contains the active session
+const uid = data.user.id
+
+// Also works with email or phone:
+// await auth.signInWithPassword({ email: 'user@example.com', password: 'pass123' })
+// await auth.signInWithPassword({ phone: '13800138000', password: 'pass123' })
+```
+
+**Checking login state (for route guards / auth checks):**
+```js
+// Use auth.getLoginState() to get the current session.
+// IMPORTANT: uid alone is NOT enough — when the SDK is initialized with a
+// publishableKey it may create an anonymous session that also has a uid.
+// Route guards must reject anonymous sessions explicitly.
+const loginState = await auth.getLoginState()
+const isRealLogin = !!loginState
+ && !!loginState.uid
+ && loginState.loginType !== 'ANONYMOUS'
+// Use isRealLogin (not just !!uid) to gate protected routes.
```
**4. Registration**
@@ -181,7 +209,7 @@ const emailVerifyResult = await emailSignUp.data.verifyOtp({ token: '123456' })
// Phone Otp
// Use only when the task explicitly requires phone numbers.
// Phone Otp
-const phoneSignUp = await auth.signUp({ phone: '13800138000', nickname: 'User' })
+const phoneSignUp = await auth.signUp({ phone: '13800138000', password: 'pass123', nickname: 'User' })
const phoneVerifyResult = await phoneSignUp.data.verifyOtp({ token: '123456' })
```
@@ -200,11 +228,13 @@ const handleRegister = async () => {
}
const handleLogin = async () => {
- const { error } = await auth.signInWithPassword({
+ const { data, error } = await auth.signInWithPassword({
username,
password,
})
if (error) throw error
+ // Login succeeded — data.user.id is the uid
+ return true
}
```
@@ -214,11 +244,11 @@ Do not use email OTP or email-only helpers for these flows unless the task expli
const handleSendCode = async () => {
try {
const { data, error } = await auth.signUp({
- email,
- name: username || email.split('@')[0],
+ phone,
+ password: password || undefined,
})
if (error) throw error
- setSignUpData(data)
+ verifyOtpRef.current = data.verifyOtp
} catch (error) {
console.error('Failed to send sign-up code', error)
}
@@ -226,13 +256,9 @@ const handleSendCode = async () => {
const handleRegister = async () => {
try {
- if (!signUpData?.verifyOtp) throw new Error('Please send the code first')
+ if (!verifyOtpRef.current) throw new Error('Please send the code first')
- const { error } = await signUpData.verifyOtp({
- email,
- token: code,
- type: 'signup',
- })
+ const { error } = await verifyOtpRef.current({ token: code })
if (error) throw error
} catch (error) {
console.error('Failed to complete sign-up', error)
diff --git a/doc/prompts/cloud-functions.mdx b/doc/prompts/cloud-functions.mdx
index 7f104bb8..60fb178c 100644
--- a/doc/prompts/cloud-functions.mdx
+++ b/doc/prompts/cloud-functions.mdx
@@ -92,6 +92,8 @@ Keep local `references/...` paths for files that ship with the current skill dir
- Forgetting that runtime cannot be changed after creation.
- Using cloud functions as the first answer for Web login.
- Forgetting that HTTP Functions must ship `scf_bootstrap`, listen on port `9000`, and include dependencies.
+- Forgetting to configure function security rules after creating an HTTP Function. Default rules reject anonymous callers with `EXCEED_AUTHORITY`. Use `managePermissions(action="updateResourcePermission", resourceType="function")` to allow public access.
+- Mismatching the `scf_bootstrap` Node.js binary path with the function runtime (e.g. using `/var/lang/node18/bin/node` but setting `runtime: "Nodejs16.13"`).
### Minimal checklist
@@ -110,8 +112,25 @@ Use this skill when developing, deploying, and operating CloudBase cloud functio
- If the request is for SDK calls, timers, or event-driven workflows, write an **Event Function** with `exports.main = async (event, context) => {}`.
- If the request is for REST APIs, browser-facing endpoints, SSE, or WebSocket, write an **HTTP Function** with `req` / `res` on port `9000`.
+- For Node.js HTTP Functions, default to the native `http` module unless the user explicitly asks for Express, Koa, NestJS, or another framework.
- If the user mentions HTTP access for an existing Event Function, keep the Event Function code shape and add gateway access separately.
+## HTTP Function authoring contract
+
+Use these rules whenever you are writing the function code itself:
+
+- Do not write an HTTP Function as `exports.main(event, context)`. That is the Event Function contract.
+- Treat the function as a standard web server process that must listen on port `9000`.
+- With Node.js, prefer `http.createServer((req, res) => { ... })` by default so the runtime contract stays explicit.
+- With the Node.js native `http` module, do not assume Express-style helpers exist. `req.body`, `req.query`, and `req.params` are not provided for you.
+- For Node.js HTTP Functions, choose one module system up front and keep it consistent. Default to CommonJS for simple functions (`require(...)`, no `"type": "module"` in `package.json`) unless you explicitly want ES Modules.
+- If you do choose ES Modules (`"type": "module"` + `import ...`), do not mix in CommonJS-only globals or APIs such as `require(...)`, `module.exports`, or bare `__dirname`. In ESM, derive file paths from `import.meta.url` with `fileURLToPath(...)` only when needed.
+- With the native `http` module, parse `req.url` yourself with `new URL(...)`, collect the request body from the stream, and only then call `JSON.parse`. Empty bodies should be handled explicitly instead of assuming JSON is always present.
+- Return responses explicitly with `res.writeHead(...)` and `res.end(...)`, including `Content-Type` such as `application/json; charset=utf-8` for JSON APIs.
+- Keep routing and method handling explicit. Unknown paths should return `404`, and known paths with unsupported methods should normally return `405`.
+- Keep gateway setup and security-rule changes separate from the runtime code. They affect access, not the HTTP Function programming model.
+- Do not add HTTP access service configuration when the task is only to create an HTTP Function itself. Gateway paths or custom domains are separate access-layer work; anonymous or public invocation requirements should be handled through the function security rule workflow.
+
## Quick decision table
| Question | Choose |
@@ -137,7 +156,7 @@ Use this skill when developing, deploying, and operating CloudBase cloud functio
3. **Write code and deploy, do not stop at local files**
- Use `manageFunctions(action="createFunction")` for creation
- Use `manageFunctions(action="updateFunctionCode")` for code updates
- - Keep `functionRootPath` as the parent directory of the function folder
+ - Keep `functionRootPath` as the directory that directly contains function folders (e.g., `cloudfunctions/` or `functions/`), NOT the project root and NOT the function subdirectory itself
- Use CLI only as a fallback when MCP tools are unavailable
4. **Prefer doc-first fallbacks**
@@ -198,10 +217,21 @@ exports.main = async (event, context) => {
```js
const http = require("http");
+const { URL } = require("url");
+
+function sendJson(res, statusCode, data) {
+ res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
+ res.end(JSON.stringify(data));
+}
const server = http.createServer((req, res) => {
- res.writeHead(200, { "Content-Type": "application/json" });
- res.end(JSON.stringify({ ok: true, message: "hello from http function" }));
+ const url = new URL(req.url || "/", "http://127.0.0.1");
+
+ if (req.method === "GET" && url.pathname === "/") {
+ sendJson(res, 200, { ok: true, message: "hello from http function" });
+ } else {
+ sendJson(res, 404, { error: "Not Found" });
+ }
});
server.listen(9000);
@@ -214,6 +244,8 @@ server.listen(9000);
/var/lang/node18/bin/node index.js
```
+The `scf_bootstrap` binary path must match the runtime — see the full mapping table in `./references/http-functions.md`.
+
`cloudfunctions/hello-http/package.json`
```json
diff --git a/doc/prompts/cloudbase-platform.mdx b/doc/prompts/cloudbase-platform.mdx
index f7471725..3e14fd60 100644
--- a/doc/prompts/cloudbase-platform.mdx
+++ b/doc/prompts/cloudbase-platform.mdx
@@ -150,8 +150,10 @@ Use this skill for **CloudBase platform knowledge** when you need to:
1. **SDK Initialization**:
- CloudBase SDK initialization requires environment ID
- Can query environment ID via `envQuery` tool
+ - If the user only provides an environment alias, nickname, or other short form, resolve it with `envQuery(action="list", alias=..., aliasExact=true)` first and use the returned full `EnvId`
+ - Do not pass alias-like short forms directly into SDK init, `auth.set_env`, console URLs, or generated config files
- For Web, always initialize synchronously:
- - `import cloudbase from "@cloudbase/js-sdk"; const app = cloudbase.init({ env: "xxxx-yyy" });`
+ - `import cloudbase from "@cloudbase/js-sdk"; const app = cloudbase.init({ env: "your-full-env-id" });`
- Do **not** use dynamic imports like `import("@cloudbase/js-sdk")` or async wrappers such as `initCloudBase()` with internal `initPromise`
- Then proceed with login, for example using anonymous login
@@ -343,6 +345,7 @@ The CloudBase console is updated frequently. If a live, logged-in console shows
- **Base URL Pattern**: `https://tcb.cloud.tencent.com/dev?envId=${envId}#/{path}`
- **Replace Variables**: Always replace `${envId}` with the actual environment ID queried via `envQuery` tool
+- **Alias Handling**: If the conversation only contains an alias or shorthand, first resolve it with `envQuery(action="list", alias=..., aliasExact=true)` and use the returned `EnvId`; if the alias is ambiguous or missing, ask the user to confirm before generating links
- **Resource-Specific URLs**: For specific resources (collections, functions, models), replace resource name variables with actual values
- **Usage**: After creating/deploying resources, provide these console links to users for management operations
diff --git a/doc/prompts/no-sql-web-sdk.mdx b/doc/prompts/no-sql-web-sdk.mdx
index 25863221..bb64b63e 100644
--- a/doc/prompts/no-sql-web-sdk.mdx
+++ b/doc/prompts/no-sql-web-sdk.mdx
@@ -83,6 +83,8 @@ Keep local `references/...` paths for files that ship with the current skill dir
- Using `wx.cloud.database()` or Node SDK patterns in browser code.
- Initializing CloudBase lazily with dynamic imports instead of a shared synchronous app instance.
- Treating security rules as result filters rather than request validators.
+- **Expecting a `CUSTOM` security rule to take effect immediately after you call `managePermissions(updateResourcePermission)`.** The backend caches rule evaluators for **2–5 minutes**; first writes after a rule change may silently fail or be rejected with `DATABASE_PERMISSION_DENIED` even when the expression is correct. Either (a) wait a few minutes and retry the same write before assuming the rule is wrong, or (b) verify the rule is live by reading `result.code` / `result.message` on every write and by doing a `get()` round-trip on the just-written `_id`; do not treat a resolved promise as success. See `security-rules.md` → "Propagation And Verification" for the full pattern.
+- Misreading the return shape of `db.collection(...).add(...)`. In the CloudBase Web SDK, the created document ID is exposed at top-level `result._id`, not `result.id`, `result.data.id`, or `result.insertedId`.
- For CMS-style collections that need **app-level admin users** to edit/delete all records while editors can only edit/delete their own records, do not oversimplify the rule to `READONLY`. A validated pattern is a `CUSTOM` rule that reads role from `user_roles` by `auth.uid` and combines it with `doc.authorId == auth.uid`, while frontend writes can stay on `.doc(id).update()` / `.doc(id).remove()`.
- Forgetting pagination or indexes for larger collections.
@@ -155,6 +157,10 @@ Important rules:
- Database errors must become readable UI or application errors, not silent failures.
- For writes, do not treat a resolved promise as success by default. Check write result fields such as `updated` / `deleted` or surfaced `code` / `message`.
+5. **Persist IDs from create operations correctly**
+ - For Web SDK `.add(...)`, the newly created document ID is `result._id`.
+ - Do not look for the ID under `result.id`, `result.data`, or other driver-specific fields.
+
## Quick examples
### Simple query
@@ -165,6 +171,18 @@ const result = await db.collection("todos")
.get();
```
+### Create and capture document ID
+
+```javascript
+const result = await db.collection("posts").add({
+ title: "New article",
+ content: "...",
+ createdAt: new Date()
+});
+
+const articleId = result._id;
+```
+
### Ordered pagination
```javascript
diff --git a/doc/prompts/web-development.mdx b/doc/prompts/web-development.mdx
index f958379e..b7e07334 100644
--- a/doc/prompts/web-development.mdx
+++ b/doc/prompts/web-development.mdx
@@ -154,6 +154,7 @@ Use this section only when the Web project needs CloudBase platform features.
- Use the CDN only for static HTML pages, quick demos, embedded snippets, or README examples
- Only use documented CloudBase Web SDK APIs; do not invent methods or options
- Keep a shared `app` or `auth` instance instead of re-initializing on every call
+- If the user only provides an environment alias, nickname, or other shorthand, resolve it to the canonical full `EnvId` before writing SDK init code, console links, or config files. Do not pass alias-like short forms directly into `cloudbase.init({ env })`.
### Authentication boundary
@@ -164,17 +165,32 @@ 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
+- Deploy the entire `dist/` directory contents, not just `index.html`
- 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 checklist
+
+When deploying to a subdirectory (e.g., `/vite-test`), the following must be verified **before calling `uploadFiles`**. If any item fails, stop and fix it first:
+
+1. **Build config `base`/`publicPath`/`assetPrefix` must be set to the absolute subdirectory path** (e.g., deploy target `/vite-test` → Vite `base: '/vite-test/'`, Webpack `publicPath: '/vite-test/'`). **Forbidden**: `'./'`, empty string, or `'/'` — these cause 404 for assets when the access URL lacks a trailing slash.
+2. **Rebuild after changing build config** — modifying `base`/`publicPath` without rebuilding leaves stale references in `dist/`.
+3. **Verify build output** — check that asset references in `dist/index.html` use the subdirectory prefix (e.g., `src="/vite-test/assets/..."`), not root-relative paths (`src="/assets/..."`).
+
+`uploadFiles` `cloudPath` format: relative to hosting root, **no leading `/`**. Example: `'vite-test'` (not `'/vite-test'`). The `localPath` should point to the `dist/` directory.
+
+### Root deployment
+
+When deploying to the site root (`/`), `cloudPath` can be omitted or left empty. For Vite projects, `base: '/'` (the default) works correctly.
+
### CloudBase quick start
```js
+// npm install @cloudbase/js-sdk
import cloudbase from "@cloudbase/js-sdk";
const app = cloudbase.init({
- env: "xxxx-yyy",
+ env: "your-full-env-id", // Canonical full CloudBase environment ID resolved from envQuery or the console
});
const auth = app.auth();
diff --git a/mcp/src/tools/hosting.ts b/mcp/src/tools/hosting.ts
index 9a68b50a..7e1d1764 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⚠️ 子目录部署必检项(部署到非根路径时,调用 uploadFiles 前必须逐项确认):\n1. 构建配置中的 base/publicPath/assetPrefix 必须设为与部署目标一致的绝对路径(如部署到 /vite-test,则 base 设为 '/vite-test/',带前导和尾部斜杠)。禁止使用 './' 或空字符串,否则子目录部署后静态资源会因路径解析错误而 404。\n2. 修改构建配置后必须重新 build。\n3. 必须验证构建产物中的资源引用路径已更新为非绝对根路径 '/'。\n任何一项未通过,禁止调用 uploadFiles。",
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/ 子目录,而非 '/vite-test'。若上传整个 dist 目录到子目录,cloudPath 填子目录名(如 'vite-test'),localPath 填 dist 绝对路径。云存储对象路径请改用 manageStorage。"),
files: z.array(z.object({
localPath: z.string(),
cloudPath: z.string()
diff --git a/tests/issue-auto-processor.test.js b/tests/issue-auto-processor.test.js
index cb91dd05..0e05904f 100644
--- a/tests/issue-auto-processor.test.js
+++ b/tests/issue-auto-processor.test.js
@@ -182,6 +182,6 @@ test('workflow isolates batch iteration from CLI stdin consumption', () => {
expect(raw).toContain('mapfile -t issues < <(jq -c ".[]" .issue-auto-processor-issues.json)');
expect(raw).toContain('for issue in "${issues[@]}"; do');
- expect(raw).toContain('codebuddy -p "$(cat /tmp/codebuddy-prompt.txt)" -y --output-format json --permission-mode acceptEdits &1');
+ expect(raw).toContain('codebuddy -p "$(cat /tmp/codebuddy-prompt.txt)" -y --output-format json --permission-mode acceptEdits --model hy3-preview-ioa &1');
expect(raw).not.toContain('done < <(jq -c ".[]" .issue-auto-processor-issues.json)');
});