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 参数名类型必填说明 localPathstring本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。 -cloudPathstring静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。 +cloudPathstring静态托管云端路径,相对于托管根目录,不要带前导 '/'。例如 'vite-test' 表示部署到 /vite-test/ 子目录,而非 '/vite-test'。若上传整个 dist 目录到子目录,cloudPath 填子目录名(如 'vite-test'),localPath 填 dist 绝对路径。云存储对象路径请改用 manageStorage。 filesarray of object多文件上传配置 默认值: [] files[].localPathstring是 files[].cloudPathstring是 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)'); });