@lark-apaas/coding-steering 0.1.52 → 0.1.53-alpha.20260916160524
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/steering/nestjs-react-fullstack/skills/app-init-feasibility-guide/SKILL.md +1 -2
- package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md +0 -1
- package/steering/nestjs-react-fullstack/skills/client-builtins-user-service/SKILL.md +0 -2
- package/steering/nestjs-react-fullstack/skills/coding-guide/SKILL.md +14 -61
- package/steering/nestjs-react-fullstack/skills/plugin-guide/SKILL.md +1 -4
- package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md +5 -37
- package/steering/nestjs-react-fullstack/skills_common/mcp-guide/SKILL.md +376 -0
- package/steering/nestjs-react-fullstack/skills_local/client-builtins-user-service/SKILL.md +0 -2
- package/steering/nestjs-react-fullstack/skills_local/coding-guide/SKILL.md +13 -44
- package/steering/nestjs-react-fullstack/skills_local/plugin-guide/SKILL.md +1 -1
- package/steering/nestjs-react-fullstack/skills_local/plugin-guide/references/plugin-coding-guide.md +5 -37
- package/steering/vite-react/skills/plugin-guide/SKILL.md +5 -2
- package/steering/nestjs-react-fullstack/skills/design-guide/SKILL.md +0 -188
- package/steering/nestjs-react-fullstack/skills/design-guide/references/broadsheet.md +0 -311
- package/steering/nestjs-react-fullstack/skills/design-guide/references/claude-editorial-research.md +0 -375
- package/steering/nestjs-react-fullstack/skills/design-guide/references/corporate-blueprint.md +0 -252
- package/steering/nestjs-react-fullstack/skills/design-guide/references/crimson-frosted-glass.md +0 -221
- package/steering/nestjs-react-fullstack/skills/design-guide/references/cybernetic-vault-terminal.md +0 -74
- package/steering/nestjs-react-fullstack/skills/design-guide/references/dashboard.md +0 -233
- package/steering/nestjs-react-fullstack/skills/design-guide/references/digital-e-guide.md +0 -349
- package/steering/nestjs-react-fullstack/skills/design-guide/references/flowbite.md +0 -179
- package/steering/nestjs-react-fullstack/skills/design-guide/references/frontend-design.md +0 -66
- package/steering/nestjs-react-fullstack/skills/design-guide/references/handwritten-sketch.md +0 -211
- package/steering/nestjs-react-fullstack/skills/design-guide/references/industry.md +0 -346
- package/steering/nestjs-react-fullstack/skills/design-guide/references/metabase.md +0 -220
- package/steering/nestjs-react-fullstack/skills/design-guide/references/minimal-jade.md +0 -205
- package/steering/nestjs-react-fullstack/skills/design-guide/references/nebula-crimson.md +0 -306
- package/steering/nestjs-react-fullstack/skills/design-guide/references/nexuscore-analytics.md +0 -74
- package/steering/nestjs-react-fullstack/skills/design-guide/references/organic-minimalism.md +0 -251
- package/steering/nestjs-react-fullstack/skills/design-guide/references/phosphor-hud.md +0 -214
- package/steering/nestjs-react-fullstack/skills/design-guide/references/pm-spec.md +0 -233
- package/steering/nestjs-react-fullstack/skills/design-guide/references/pop-art.md +0 -264
- package/steering/nestjs-react-fullstack/skills/design-guide/references/token-mapping.md +0 -68
- package/steering/nestjs-react-fullstack/skills/design-guide/references/warm-elegance.md +0 -215
- package/steering/nestjs-react-fullstack/skills/design-guide/references/wild-orange.md +0 -203
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-steering",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53-alpha.20260916160524",
|
|
4
4
|
"description": "Stack-specific steering content for miaoda-coding templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"steering"
|
|
8
8
|
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
|
|
11
|
+
},
|
|
9
12
|
"devDependencies": {
|
|
10
13
|
"markdownlint-cli": "^0.47.0"
|
|
11
14
|
},
|
|
@@ -17,8 +20,5 @@
|
|
|
17
20
|
"miaoda",
|
|
18
21
|
"coding-steering"
|
|
19
22
|
],
|
|
20
|
-
"license": "MIT"
|
|
21
|
-
|
|
22
|
-
"lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -71,7 +71,7 @@ available-agents:
|
|
|
71
71
|
| 实时多人编辑、毫秒级协同 | 弱实时刷新、提交后同步 |
|
|
72
72
|
| 原生 App、桌面端、浏览器插件 | Web 应用、响应式页面 |
|
|
73
73
|
| 服务端持久写本地文件 | 平台文件服务、数据库、临时 `/tmp` |
|
|
74
|
-
|
|
|
74
|
+
| 服务启动时下载大文件、全量同步或批量迁移 | 采用异步处理,避免阻塞服务启动 |
|
|
75
75
|
| 无凭证调用受限第三方系统 | 要求用户提供 API、凭证或授权方式 |
|
|
76
76
|
| 自建账号体系绕过平台登录 | 使用平台内置身份与权限 |
|
|
77
77
|
| 多语言 i18n、深浅色主题切换(非平台内置) | 需自行实现并计入工作量;规格中先确认是否必要 |
|
|
@@ -113,7 +113,6 @@ available-agents:
|
|
|
113
113
|
- 角色权限:查看、创建、编辑、删除、导出、配置等操作的分配。
|
|
114
114
|
- 集成点:飞书、多维表格、AI、内网接口、OpenAPI、自动化触发。
|
|
115
115
|
- 边界与限制:不支持的能力、外部依赖、数据权限边界。
|
|
116
|
-
- 启动约束:若有下载大文件、全量同步或批量迁移,规格中明确由平台支持的独立异步任务或外部触发机制执行;不得在 `constructor`、`onModuleInit` 或 `onApplicationBootstrap` 中执行或直接发起,声明为 `async`、使用 `void Promise`、异步 IIFE 或定时器均不符合要求。
|
|
117
116
|
- 交给 Code agent 时标明需要加载的专项 skill(表格、表单、图表、插件、权限、文件、自动化)。
|
|
118
117
|
- 需要 native addon 时:确认它提供覆盖 linux-x64 / glibc 的预编译二进制并记录包名与版本;没有预编译二进制、装的时候要现场编译的不能用。
|
|
119
118
|
|
package/steering/nestjs-react-fullstack/skills/client-builtins-file-storage-service/SKILL.md
CHANGED
|
@@ -21,7 +21,6 @@ match-template-name: nestjs-react-fullstack
|
|
|
21
21
|
## 使用注意
|
|
22
22
|
- **入口边界**:本 SDK 是**应用前端代码**读写应用存储的唯一入口。Agent 自己在对话 / 开发中上传、灌数据或调试文件,先加载应用文件存储操作 skill(按「应用文件存储 / 文件上传 / 文件下载」召回),用其 CLI 命令操作,具体命令一律以该 skill 为准——二者操作同一个应用存储桶,但**该 CLI 命令只供 Agent 在沙箱终端使用,禁止写进页面代码**,页面里一律用本 SDK。
|
|
23
23
|
- @lark-apaas/client-toolkit/dataloom 这个SDK只适用于前端调用,禁止在服务端调用
|
|
24
|
-
- **沙箱 dev 限制**:`uploadFile` 打的 `/app/<appId>/__runtime__/api/v1/storage/object/<bucket>/pre_upload` **在沙箱 dev 下恒 404**,只有发布态可用。这是环境限制不是代码缺陷,按本 skill 写法即为正确,不要为它改代码;开发期要验证上传链路改走接口测试直接打后端接口,或复用库里已有的文件 URL。详见 coding-guide「沙箱 dev 不提供平台 runtime 接口」。
|
|
25
24
|
- 上传成功后,最重要的返回值是 `data.download_url`。需要将此URL保存到你的业务数据库中
|
|
26
25
|
- **⚠️ 场景区分(重要)**:`dataloom.storage` 仅适用于需要持久化存储文件或获取 `download_url` 保存到数据库的场景。如果文件仅作为插件输入(传给 `capabilityClient`),**必须直接传 File/Blob 对象,禁止先走 dataloom 上传再传 URL**;插件调用(capability)不属于 dataloom,详见 plugin-guide
|
|
27
26
|
- **download_url 格式说明**:`download_url` 返回的可能是相对路径(如 `/spark/app/.../storage/object/...`),这是正常行为。**禁止**在前面拼接 `window.location.origin` 或其他域名前缀,平台会自动解析相对路径。直接使用原始值即可。
|
|
@@ -14,8 +14,6 @@ match-template-name: nestjs-react-fullstack
|
|
|
14
14
|
|
|
15
15
|
> **边界说明**:`authClient.session` 仅用于用户登录/登出/获取用户信息等鉴权操作。**插件调用(capability)不属于账户 SDK**,须使用独立的 `capabilityClient`(参见 plugin-guide)。
|
|
16
16
|
>
|
|
17
|
-
> **沙箱 dev 限制**:本 skill 的能力底层打 `/app/<appId>/__runtime__/api/v1/account/*`,这类端点**在沙箱 dev 下打不通**(POST 404;GET 落 SPA fallback 返回 200 + index.html),只有发布态可用。`useCurrentUserProfile()` 打的是 `account/login/user`;UserSelect / DepartmentSelect / UserDisplay 打 `search_user` / `list_users` / `user_profile` / `search_department`。看到 404 或选人列表为空时,禁止改 `business-ui` 组件、禁止加兜底请求或换自研控件绕过;开发期要验证「按当前用户过滤 / 展示姓名」改由服务端出数据(`req.userContext` / `AuthNPaasService`)。详见 coding-guide「沙箱 dev 不提供平台 runtime 接口」。
|
|
18
|
-
>
|
|
19
17
|
> **运行时边界**:本 skill 所有能力(`authClient`、`useCurrentUserProfile`、UserSelect/UserDisplay 等)仅限前端代码使用,**严禁在 `server/**` 中 import**。服务端获取用户身份用 `req.userContext` / `AuthNPaasService`(见 `user-identity` skill),完整边界规则见 coding-guide。
|
|
20
18
|
|
|
21
19
|
## 怎么选(决策指引)
|
|
@@ -139,11 +139,6 @@ shared/ # 前后端共享的目录
|
|
|
139
139
|
- **import 类型前必须确认实际导出**:先读取文件确认类型名称存在且拼写一致,禁止臆造类型名
|
|
140
140
|
- 属性统一 camelCase(禁止 snake_case),server 端 schema 的 snake_case 不应泄漏到接口类型
|
|
141
141
|
- shared 目录是前后端共享文件,**禁止反向引用** `@server/*`、`@client/*` 等路径别名
|
|
142
|
-
- **请求 DTO 中的 shared 字符串联合别名**:`shared/api.interface.ts` 是前后端契约权威。带 `class-validator` 的 NestJS 请求 DTO 字段若对应 shared 字符串字面量联合 `type` alias,不把 imported alias 直接写在 DTO 属性上;改用等价 inline literal union + `@IsString()` + `@IsIn([...])`,DTO class 仍 `implements` shared 请求接口。
|
|
143
|
-
- ❌ `type!: ExpressionElementType`
|
|
144
|
-
- ✅ `@IsString()` + `@IsIn(['tile', 'operator'])` + `type!: 'tile' | 'operator'`
|
|
145
|
-
- **机械核对**:先数 shared alias 字面量个数 N,inline union 和 `@IsIn` 数组都必须恰好 N 个,且每个字面量完全一致;数量或名称不等就是错,不能只写“不得窄于”。
|
|
146
|
-
- 边界:shared 真实 `enum` 用 `@IsEnum(EnumName)`;嵌套 DTO 用 `@ValidateNested()` + `@Type(() => XxxDto)`;普通对象字段用 `@IsObject()`;Date 转换按 `@Type(() => Date)` 处理。禁止用 `@Type(() => String)` 只为绕过 lint。
|
|
147
142
|
|
|
148
143
|
## 代码质量约束
|
|
149
144
|
|
|
@@ -160,7 +155,7 @@ shared/ # 前后端共享的目录
|
|
|
160
155
|
|
|
161
156
|
## 依赖使用规范
|
|
162
157
|
|
|
163
|
-
1. **子包完整性检查**:部分库有多个子包(如 `@
|
|
158
|
+
1. **子包完整性检查**:部分库有多个子包(如 `@dnd-kit/core` + `@dnd-kit/sortable` + `@dnd-kit/utilities`),添加 import 后必须确认 package.json 中包含所有需要的子包
|
|
164
159
|
2. **禁止安装时需现场编译的 native addon**(`node-gyp rebuild`、`binding.gyp`、需要系统头文件或编译器)。依赖按平台内置能力 / 浏览器能力 → 纯 JS 或 WASM → 有预编译二进制的 native addon 的顺序选型
|
|
165
160
|
3. 用法不清时查看 readme,可进一步搜索或网页访问获取信息
|
|
166
161
|
|
|
@@ -185,46 +180,6 @@ shared/ # 前后端共享的目录
|
|
|
185
180
|
- `.eslintrc.js`、`.prettierrc`
|
|
186
181
|
- `node_modules/`、`.git/`、`dist/`、`build/`
|
|
187
182
|
|
|
188
|
-
### 沙箱 dev 不提供平台 runtime 接口(`__runtime__`)
|
|
189
|
-
|
|
190
|
-
dev server 只代理 `/api`、`/openapi`、`/__innerapi__`(外加 legacy 的 `/af/p/:appId/api`、`/af/p/:appId/__innerapi__`)。`/app/<appId>/__runtime__/*` 这一整类**平台 runtime 接口在沙箱 dev 下打不通**,只有发布态才由网关接管。
|
|
191
|
-
|
|
192
|
-
**症状分两种,按请求方法区分**:POST 直接 404;**GET 会落到 Vite 的 SPA fallback,拿回 200 + `index.html`**,业务侧表现为「返回了一坨 HTML,JSON 解析失败」——见下方「问题排查指引」里「API 测试返回 HTML 内容时」那条,两者是同一回事。
|
|
193
|
-
|
|
194
|
-
已知受影响端点:
|
|
195
|
-
|
|
196
|
-
| 端点 | 方法 | 谁会打到它 |
|
|
197
|
-
|------|------|-----------|
|
|
198
|
-
| `api/v1/permissions/roles` | GET | `AppContainer` **每次加载应用自动打**,不需要你写任何代码 |
|
|
199
|
-
| `api/v1/observability/{logs,traces,metrics}/collect`、`current_server_timestamp` | POST/GET | 平台埋点上报,**自动打** |
|
|
200
|
-
| `api/v1/account/login/user` | POST | `useCurrentUserProfile()` → `authClient.session.getUserInfo()` |
|
|
201
|
-
| `api/v1/account/search_user`、`list_users`、`user_profile`、`search_department`、`convert_lark_user` | POST / `user_profile` 是 GET | `business-ui` 的 UserSelect / DepartmentSelect / UserDisplay |
|
|
202
|
-
| `api/v1/account/search`、`api/v1/account/chat/list_chats` | POST | ChatSelect 选群组件 |
|
|
203
|
-
| `api/v1/studio/user/profile` | GET | `getUserProfile()` |
|
|
204
|
-
| `api/v1/storage/object/<bucket>/pre_upload` | POST | `dataloom.storage.uploadFile()` 前端上传 |
|
|
205
|
-
| `api/v1/studio/plugins/tmp_files/acquire_upload_url`、`acquire_download_url` | POST | `capabilityClient` 的文件类入参 |
|
|
206
|
-
|
|
207
|
-
> `useCurrentUserProfile()` 打的是 `account/login/user`,**不是** `user_profile`/`search_user` 那一排;它另外还打一个 `GET /api/authnpaas/lark-user-id`,那条走 `/api`、被代理、正常。
|
|
208
|
-
|
|
209
|
-
**这是环境限制,不是应用缺陷,也不是组件坏了。** 看到这些 404 / HTML 响应时:
|
|
210
|
-
|
|
211
|
-
- **禁止**改 `client/src/components/business-ui/**`(平台保护文件)、**禁止**给组件加兜底请求或改用自研选人控件绕过——发布后这些端点是正常的,绕过写法反而是错的
|
|
212
|
-
- 需要在开发期验证「按当前用户过滤」「展示用户姓名」这类逻辑:改由服务端出数据。服务端的 `req.userContext` 和 `AuthNPaasService` 由网关注入,**不受此限制**,始终可用(见 `user-identity` / `contacts-service` skill)。`useCurrentUserProfile()` 在这里长期停在加载态就是这个原因
|
|
213
|
-
- 需要在开发期验证上传链路:改用接口测试直接打后端接口,或复用库里已有的文件 URL,不要在浏览器里点上传
|
|
214
|
-
- 依赖上述端点的 E2E 用例不要写成浏览器交互 Case,改走接口层往返验证
|
|
215
|
-
|
|
216
|
-
另外,`UserSelect` 弹层是**搜索驱动**的:不输关键词时列表为空属预期行为,不是接口挂了。
|
|
217
|
-
|
|
218
|
-
## 应用访问入口(404 / 白屏先看这里)
|
|
219
|
-
|
|
220
|
-
沙箱内页面入口、API 入口、NestJS 端口不是一回事,禁止用 `5173/3000/8001/8080` 枚举猜测。
|
|
221
|
-
|
|
222
|
-
- **看页面 / 白屏 / 交互**:优先用截图、视觉检查或 E2E 工具,并只传相对路径;工具会打开沙箱真实预览入口(E2E 走 8080 nginx/openresty 鉴权代理)。不要用 `curl` 拿 HTML 判断页面是否渲染。
|
|
223
|
-
- **测后端接口**:优先用接口测试工具,只传以 `/api` 或 `/openapi` 开头的业务路径;工具会自动拼 `CLIENT_BASE_PATH`、选择正确 dev 端口,并补 CSRF 与用户身份。
|
|
224
|
-
- **必须手写 curl 调 `/api/*` 时**:打 client dev server,不要打 NestJS `SERVER_PORT`;URL 必须带 `$CLIENT_BASE_PATH`,并同时带 `Cookie: suda-csrf-token=<X>` 与 `X-Suda-Csrf-Token: <X>`(两值字面相等)。漏 basePath 是 404,漏 CSRF 是 403,都不是业务代码 bug,不要为此改 csrf 中间件或给接口加白名单。
|
|
225
|
-
- **不要直连 NestJS 端口**:会绕过 dev server / 网关注入,`x-larkgw-suda-webuser` 缺失后 `userContext.userId` 为空,依赖身份的接口会误判。
|
|
226
|
-
- **判断服务是否启动**:看 `client-devserver` / `server-devserver` 日志的 ready / 编译成功。裸端口访问返回 404 或连不上(只监听 IPv6 等)都不能当服务故障的依据;同一 URL 连续两次同状态码就换排查方向,不要 `sleep` + curl 重试。
|
|
227
|
-
|
|
228
183
|
## 质量保障流程
|
|
229
184
|
|
|
230
185
|
通用提交前检查(代码检查 / 接口测试 / 读日志确认无错误)见系统任务流程约束;本栈具体落点:
|
|
@@ -278,9 +233,10 @@ dev server 只代理 `/api`、`/openapi`、`/__innerapi__`(外加 legacy 的 `
|
|
|
278
233
|
- **三方集成**:调用第三方 API 需在后端实现,使用 @nestjs/axios
|
|
279
234
|
- **能力边界**:服务端不支持文件上传(FaaS 限制),前端用 dataloom SDK 上传,服务端仅保存元信息
|
|
280
235
|
- **环境判断**:`process.env.NODE_ENV === "production"` 表示生产环境
|
|
281
|
-
-
|
|
236
|
+
- **文件系统**:临时文件、下载内容和处理中间产物统一写入 `/tmp` 下的独立目录,用完后清理
|
|
237
|
+
- **启动关键路径**:`bootstrap`、`onModuleInit` 和 `OnApplicationBootstrap` 只做必须的同步装配,不得执行 DDL、全量数据同步、长轮询或无界外部请求。应用应尽早 `listen()`;非关键预热放到后台任务,外部调用必须有超时、错误日志和降级路径
|
|
282
238
|
- **服务端运行时资源文件**(字体 / 证书 / 模板 / wasm 等需在运行时读取的非代码文件,CRITICAL — 发布后静默失效根因):① 必须在 `nest-cli.json` 的 `assets` 中声明(如 `{"include": "assets/<dir>/**/*", "outDir": "dist/server"}`),否则不会进 `dist/` 构建产物;② 路径用 `__dirname` 相对**编译产物**定位(如 `path.join(__dirname, "../assets/...")`),**禁止 `process.cwd()` 相对源码路径**——dev 跑源码能命中、发布跑 `dist/` 会落空;③ 资源缺失或加载失败必须 **fail-loud**(抛错或明确错误日志),禁止 `catch` 后静默返回残缺产物
|
|
283
|
-
- **启动生命周期**:`
|
|
239
|
+
- **启动生命周期**:`constructor`、`onModuleInit` 和 `onApplicationBootstrap` 应快速完成,避免在其中下载大文件、全量同步或批量迁移;仅将钩子声明为 `async` 仍会被等待,无法缩短启动时间
|
|
284
240
|
|
|
285
241
|
## 日志约定
|
|
286
242
|
|
|
@@ -382,11 +338,7 @@ await db.select().from(users).where(eq(users.adminUser, userId));
|
|
|
382
338
|
- **Drizzle raw `sql` 参数不走列 encoder**:绑定 schema 列的 helper 会编码;raw `sql` 的 `${...}` 只是 driver 参数。Date / custom type 进 raw SQL 前先转 driver-safe 标量(时间用 ISO string)或改回 helper;`user_profile` 仍按上一小节专表处理。
|
|
383
339
|
- raw SQL 聚合、`filter (...)`、`CASE WHEN`、窗口函数或复杂 where 涉及 Date / custom type 时,按 `raw-sql-boundary-audit` 审计,并实际调用对应 API 验证 HTTP 200,避免隐藏 500。
|
|
384
340
|
|
|
385
|
-
-
|
|
386
|
-
- 列是 `Date`、DTO 给字符串 → 转成 `Date` 再写,非法值抛 `BadRequestException`,update 保留 `null`。
|
|
387
|
-
- 列是 `string` → 原样写,别转 `Date`。
|
|
388
|
-
- 读出 `Date` → 出口 `.toISOString()`;读出 `string` → 原样返回。`shared/api.interface.ts` 里两类都声明为 `string`。
|
|
389
|
-
- `toDriver` 收得比应用类型宽,但 `.values()` / `.set()` 只认 `$inferInsert`;别靠放宽 custom type 消错。
|
|
341
|
+
- **时间列跨网络后都是 string**:`date` 列 `$inferSelect` 就是 `string`(原样透传,禁止 `new Date()` 包装);`timestamp` / `timestamptz` 是 `Date`,**必须在 service 出口 `.toISOString()`**。两类在 `shared/api.interface.ts` 中统一声明为 `string`,前端需要 Date 时手动 `new Date(value)`。
|
|
390
342
|
|
|
391
343
|
- **禁止删类型注解消除报错**(`as unknown as T` 同禁):典型症状是「改完 service,错误跑到 controller 了」——错误没有转移,是校验点被往外推了一层;推到最外层删完,报错归零而契约失守。
|
|
392
344
|
|
|
@@ -608,9 +560,7 @@ async createArticle(@Req() req: Request, @Body() dto: CreateArticleDto) {
|
|
|
608
560
|
|
|
609
561
|
1. 使用 API 测试工具测试不依赖用户信息的接口
|
|
610
562
|
2. 积极使用 `read_logs` 工具,如无有效日志可增加 logger 打印
|
|
611
|
-
3. API 测试返回 HTML 内容时:检查模块注册顺序、路由顺序、请求路径。禁止修改内置 ViewController
|
|
612
|
-
4. 请求路径含 `__runtime__` 的 404(POST)或 200 + HTML(GET):属沙箱 dev 环境限制,不是应用缺陷,见「沙箱 dev 不提供平台 runtime 接口」
|
|
613
|
-
5. 服务端启动即崩、`/api` 全部 502 且报 `UnknownDependenciesException ... argument Function at index [0]`:查构造函数有没有空参 `@Inject()`(见 plugin-guide),不要先去改 Module 的 imports / providers
|
|
563
|
+
3. API 测试返回 HTML 内容时:检查模块注册顺序、路由顺序、请求路径。禁止修改内置 ViewController
|
|
614
564
|
|
|
615
565
|
---
|
|
616
566
|
|
|
@@ -631,8 +581,6 @@ async createArticle(@Req() req: Request, @Body() dto: CreateArticleDto) {
|
|
|
631
581
|
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
632
582
|
- **富文本**: `business-ui/tiptap-editor`(阅读 README.md)
|
|
633
583
|
- **Markdown 渲染**: `components/ui/streamdown`(内置 prose 排版)
|
|
634
|
-
{% else %}
|
|
635
|
-
- **Markdown 渲染**: `components/ui/markdown`(react-markdown + remark-gfm,内置 prose 排版)
|
|
636
584
|
{% endif %}
|
|
637
585
|
|
|
638
586
|
## API 请求
|
|
@@ -670,8 +618,6 @@ import { axiosForBackend } from '@lark-apaas/client-toolkit/utils/getAxiosForBac
|
|
|
670
618
|
{% if projectMeta['flags']['supportTiptapAndStreamdown'] %}
|
|
671
619
|
| TiptapEditorComplete | `business-ui/tiptap-editor` | 富文本编辑器 |
|
|
672
620
|
| Streamdown | `components/ui/streamdown` | Markdown/流式渲染 |
|
|
673
|
-
{% else %}
|
|
674
|
-
| Markdown | `components/ui/markdown` | Markdown 渲染(react-markdown + remark-gfm) |
|
|
675
621
|
{% endif %}
|
|
676
622
|
|
|
677
623
|
### 组件 Skill 召回规则(强制执行)
|
|
@@ -835,8 +781,15 @@ return <h1>{data?.title || '未知标题'}</h1>;
|
|
|
835
781
|
| 验证 | zod |
|
|
836
782
|
| 工具函数 | lodash |
|
|
837
783
|
| 样式 | clsx |
|
|
784
|
+
| Excel | xlsx(**仅前端实现,禁止服务端实现**。解析后将结构化数据传到服务端保存) |
|
|
785
|
+
| PDF 导出 | jspdf + html2canvas(**仅前端实现,禁止服务端实现**) |
|
|
786
|
+
| 文件上传 | react-dropzone |
|
|
787
|
+
| 二维码 | qrcode.react |
|
|
838
788
|
| 用户反馈 | sonner |
|
|
839
|
-
|
|
789
|
+
| 拖拽 | @dnd-kit/core |
|
|
790
|
+
| 数字动画 | react-countup |
|
|
791
|
+
| Base64 | js-base64 — `import { encode, decode } from 'js-base64'` |
|
|
792
|
+
| 3D 场景 | cobe |
|
|
840
793
|
|
|
841
794
|
## 滚动分页最佳实践
|
|
842
795
|
|
|
@@ -23,7 +23,6 @@ gate-tools:
|
|
|
23
23
|
| 长耗时 AI 结果 | 大体量/多字段/多份/多语言/文件或多模态串联等结构信号命中时,优先前端 `callStream` 渐进展示;需保存则流式结束后复用 CRUD 落库;仅在 Client 侧无法满足时使用后端任务记录 + 状态查询 + 结果读取;禁止单个 HTTP 请求等待完整结果后才返回 |
|
|
24
24
|
| capabilityClient 导入 | `import { capabilityClient } from '@lark-apaas/client-toolkit'` |
|
|
25
25
|
| CapabilityService 导入 | `import { CapabilityService } from '@lark-apaas/fullstack-nestjs-core';` |
|
|
26
|
-
| CapabilityService 注入 | `@Inject(CapabilityService) private readonly capabilityService: CapabilityService`(**禁止空参 `@Inject()`**,会导致启动崩溃 502;业务 Module 无需 imports) |
|
|
27
26
|
|
|
28
27
|
## 必读 references
|
|
29
28
|
|
|
@@ -147,9 +146,7 @@ const structured = await capabilityClient
|
|
|
147
146
|
|
|
148
147
|
生成内容后可继续接外部服务:如 `ai-text-generate` → `send-feishu-message`(报告→通知)、`ai-text-to-json` → `feishu-bitable`(结构化→入库)。
|
|
149
148
|
|
|
150
|
-
**创建 `ai-text-to-json`
|
|
151
|
-
|
|
152
|
-
**创建 `ai-image-to-json` 实例时**:必须一次性定义**所有**需提取字段(参考数据库 schema / 表单定义 / UI 设计),宁多勿漏;字段类型仅支持 String/Number/Boolean,最多 20 个;先调 `get_plugin_ai_json` 确认上游插件的 `outputSchema` 确保输入格式正确。
|
|
149
|
+
**创建 `ai-text-to-json` / `ai-image-to-json` 实例时**:必须一次性定义**所有**需提取字段(参考数据库 schema / 表单定义 / UI 设计),宁多勿漏;字段类型仅支持 String/Number/Boolean,最多 20 个;先调 `get_plugin_ai_json` 确认上游插件的 `outputSchema` 确保输入格式正确。
|
|
153
150
|
|
|
154
151
|
## 多维表格数据架构
|
|
155
152
|
|
package/steering/nestjs-react-fullstack/skills/plugin-guide/references/plugin-coding-guide.md
CHANGED
|
@@ -87,18 +87,12 @@
|
|
|
87
87
|
#### call / callStream 函数签名
|
|
88
88
|
|
|
89
89
|
```typescript
|
|
90
|
-
//
|
|
91
|
-
.
|
|
92
|
-
.callStream<T = unknown>(actionKey: string, params?: Record<string, unknown>): AsyncIterable<T> // 流式
|
|
90
|
+
.call(actionKey: string, input: object) // 非流式,返回 Promise<output>
|
|
91
|
+
.callStream(actionKey: string, input: object) // 流式,返回 AsyncIterable<chunk>
|
|
93
92
|
```
|
|
94
93
|
|
|
95
94
|
- **第一个参数 `actionKey`**:必须是字符串,值来自 `get_plugin_ai_json` 返回的 `actions[].key`(如 `'sendFeishuMessage'`、`'textGenerate'`)
|
|
96
|
-
- **第二个参数 `
|
|
97
|
-
- 上面是**前端**签名,带泛型。**服务端 `CapabilityService` 的 `call` / `callStream` / `callStreamWithEvents` 都没有泛型**,两者不通用,详见「Server 侧调用方式」
|
|
98
|
-
|
|
99
|
-
> **`Record<string, unknown>` 不接 `interface` 声明的对象**(interface 没有隐式索引签名),直接传会撞
|
|
100
|
-
> `Argument of type 'XxxInput' is not assignable to parameter of type 'Record<string, unknown>'. Index signature for type 'string' is missing in type 'XxxInput'.`
|
|
101
|
-
> 三种正确写法:① 直接传内联对象字面量;② 入参类型用 `type XxxInput = { ... }` 而非 `interface`(type 别名有隐式索引签名);③ 已有 interface 时在调用点显式 `input as unknown as Record<string, unknown>`。**不要为此改用 `as any`。**
|
|
95
|
+
- **第二个参数 `input`**:必须是对象,结构符合 `actions[].inputSchema`
|
|
102
96
|
|
|
103
97
|
```typescript
|
|
104
98
|
// ❌ 错误:把参数 JSON.stringify 后当作 actionKey
|
|
@@ -312,49 +306,23 @@ import { CapabilityService } from '@lark-apaas/fullstack-nestjs-core';
|
|
|
312
306
|
export class XxxService {
|
|
313
307
|
private readonly logger = new Logger(XxxService.name);
|
|
314
308
|
|
|
315
|
-
// 注入 token 必须显式写成 @Inject(CapabilityService)
|
|
316
309
|
constructor(
|
|
317
|
-
@Inject(
|
|
310
|
+
@Inject() private readonly capabilityService: CapabilityService,
|
|
318
311
|
) {}
|
|
319
312
|
}
|
|
320
313
|
```
|
|
321
314
|
|
|
322
|
-
**禁止空参 `@Inject()`**。本 stack 的 `nest-cli.json` 用 `builder: swc`,空参 `@Inject()` 会把注入 token 置为 undefined,Nest 回退读 `design:type` 拿到 `Function`,服务端启动即崩溃、所有 `/api` 请求返回 502:
|
|
323
|
-
|
|
324
|
-
```
|
|
325
|
-
UnknownDependenciesException: Nest can't resolve dependencies of the XxxService (?).
|
|
326
|
-
Please make sure that the argument Function at index [0] is available in the XxxModule context.
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
见到这段报错先查构造函数有没有空参 `@Inject()`,不要去改 Module 的 `imports` / `providers`。
|
|
330
|
-
|
|
331
|
-
**业务 Module 不需要注册 CapabilityModule**。`PlatformModule` 是 `@Global()` 且已把 `CapabilityModule` 放进 `exports`,`CapabilityService` 全局可注入。不要在业务 Module 里写 `imports: [CapabilityModule.forRoot(...)]` 或 `imports: [PlatformModule.forRoot()]`。
|
|
332
|
-
|
|
333
|
-
同一规则适用于从 `@lark-apaas/fullstack-nestjs-core` 注入的其他平台服务(`AuthNPaasService`、`FileService` 等)。
|
|
334
|
-
|
|
335
315
|
#### 3. 调用示例
|
|
336
316
|
|
|
337
|
-
服务端 `CapabilityExecutor` 的**三个方法全都没有泛型参数**(`@lark-apaas/nestjs-capability`):
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
call(actionName: string, input: unknown, context?: Partial<PluginActionContext>): Promise<unknown>;
|
|
341
|
-
callStream(actionName: string, input: unknown, context?): AsyncIterable<unknown>;
|
|
342
|
-
callStreamWithEvents(actionName: string, input: unknown, context?): AsyncIterable<StreamEvent<unknown>>;
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
写 `.call<T>(...)` / `.callStream<T>(...)` / `.callStreamWithEvents<T>(...)` 都会报「应有 0 个类型参数,但获得 1 个」。泛型只存在于前端 `capabilityClient`。注意服务端 `input` 是**必传**(没有 `?`),且类型是 `unknown` 而非前端的 `Record<string, unknown>`。服务端接 `unknown` 后在运行时收窄,禁止 `as any`:
|
|
346
|
-
|
|
347
317
|
```typescript
|
|
348
318
|
const inputParams = {
|
|
349
319
|
// 严格按 get_plugin_ai_json.actions[].inputSchema 构造
|
|
350
320
|
};
|
|
351
321
|
|
|
352
322
|
try {
|
|
353
|
-
const output
|
|
323
|
+
const output = await this.capabilityService
|
|
354
324
|
.load('')
|
|
355
325
|
.call('', inputParams);
|
|
356
|
-
// 运行时收窄后再取字段,例如:
|
|
357
|
-
// if (typeof output === 'object' && output !== null && 'content' in output) { ... }
|
|
358
326
|
return output;
|
|
359
327
|
} catch (error) {
|
|
360
328
|
this.logger.error('pluginInstance call failed', {
|