@lark-apaas/coding-miaoda-sandbox-skills 0.1.43 → 0.1.45
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/miaoda/import-migration/SKILL.md +159 -0
- package/miaoda/import-migration/references/gemini.md +60 -0
- package/miaoda/import-migration/references/html-migration.md +147 -0
- package/miaoda/import-migration/references/lovable.md +42 -0
- package/miaoda/import-migration/references/react-migration.md +310 -0
- package/miaoda/import-migration/references/replit.md +74 -0
- package/miaoda/lark-apps-db/SKILL.md +2 -0
- package/miaoda/lark-apps-ops/SKILL.md +3 -2
- package/miaoda/lark-apps-ops/references/lark-apps-features.md +50 -0
- package/miaoda/lark-apps-ops/references/lark-apps-local-dev.md +2 -1
- package/miaoda/testing-guide/SKILL.md +15 -9
- package/miaoda-modern/lark-apps-ops/SKILL.md +3 -2
- package/miaoda-modern/lark-apps-ops/references/lark-apps-features.md +50 -0
- package/miaoda-modern/lark-apps-ops/references/lark-apps-local-dev.md +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: import-migration
|
|
3
|
+
description: Use when the workspace was created by importing an external project (Lovable / Replit / Gemini / zip) and the imported code still needs to be migrated into the Miaoda template. 触发词:导入项目, 项目迁移, 完成项目迁移, 我已导入项目代码, lovable, replit, gemini, zip 导入
|
|
4
|
+
hook: SessionStart
|
|
5
|
+
workspace-contains:
|
|
6
|
+
- .spark/import/MIGRATION_PENDING
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 导入项目迁移
|
|
10
|
+
|
|
11
|
+
当前工程是「外部项目导入」创建的:平台先用妙搭模板初始化了工程(模板可正常运行),再把用户上传的项目按规则叠加进来。叠加只做了文件放置,**没有合并 package.json、没有安装依赖、没有改写任何代码**。你的任务是把用户项目迁移成能在妙搭模板上构建、运行的应用,保留用户项目的页面、交互与视觉风格。
|
|
12
|
+
|
|
13
|
+
`.spark/import/MIGRATION_PENDING` 存在即表示迁移尚未完成。用户的首条消息通常是「我已导入项目代码,请帮我完成项目迁移」——直接开始迁移,不要再向用户确认是否需要迁移。迁移范围只包括前端代码,服务端代码不迁移。
|
|
14
|
+
|
|
15
|
+
## 先读这些
|
|
16
|
+
|
|
17
|
+
| 文件 | 内容 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `.spark/import/manifest.json` | 导入记录:`platform`、文件放置统计、`dependencies`(依赖对比)、`excludedPaths`、`protectedConflicts`、`replacedTemplateFiles` |
|
|
20
|
+
| `source_package/` | 用户项目中未能直接放进 `client/`、`shared/` 的全部文件,原样保留:用户的 `package.json`、`vite.config.*`、`index.html`、入口 `App.tsx` / `main.tsx`、`index.css`、supabase 集成等 |
|
|
21
|
+
| `client/src/` | 模板前端;用户的 `src/` 已按平台规则叠加到这里 |
|
|
22
|
+
|
|
23
|
+
### 文件放在了哪里
|
|
24
|
+
|
|
25
|
+
| 平台 | 已放进工程的 | 留在 `source_package/` 的 |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| lovable / zip | `src/**` → `client/src/**`;`public/**` → `client/public/**` | `src/App.tsx`、`src/main.tsx`、`src/index.css`、`src/hooks/use-mobile.tsx`、supabase / `src/db`、`src/integrations` 等,以及根目录其他文件 |
|
|
28
|
+
| replit | `client/src/**`、`shared/**` 原位;`attached_assets/**` → `client/public/assets/**` | `client/src/App.tsx`、`main.tsx`、`index.css`,以及 `server/` 等其他文件 |
|
|
29
|
+
| gemini | `components/`、`services/`、`types.ts`、`constants.ts` → `client/src/` 下同名位置 | 其余全部(含 `App.tsx`、`index.html`) |
|
|
30
|
+
|
|
31
|
+
- 会覆盖模板托管文件的导入文件(根 `package.json` / 构建配置 / `client/src/index.tsx` 等)一律改放 `source_package/`,见 manifest `protectedConflicts`。
|
|
32
|
+
- 用户同名 UI 组件会覆盖模板 `client/src/components/ui/` 下的组件;zip 导入的同名 JS / JSX 版 UI 组件例外:保留模板 TS / TSX 版,源文件放在 `source_package/`,见 `protectedConflicts`。
|
|
33
|
+
|
|
34
|
+
## 迁移流程
|
|
35
|
+
|
|
36
|
+
用 TodoWrite 按以下步骤建立任务,逐项推进并如实更新状态。
|
|
37
|
+
|
|
38
|
+
### Step 1:按平台与项目类型选指南
|
|
39
|
+
|
|
40
|
+
按 manifest 的 `platform` 读对应指南(平台文件只写差异,必须与通用指南一起读):
|
|
41
|
+
|
|
42
|
+
| `platform` | 必读 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `lovable` | `references/react-migration.md` + `references/lovable.md` |
|
|
45
|
+
| `replit` | `references/react-migration.md` + `references/replit.md` |
|
|
46
|
+
| `gemini` | `references/react-migration.md` + `references/gemini.md` |
|
|
47
|
+
| `zip` | 先识别项目类型(下表) |
|
|
48
|
+
|
|
49
|
+
`zip` 的项目类型:
|
|
50
|
+
|
|
51
|
+
| 条件 | 类型 | 必读 |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `source_package/package.json` 含 `react` 依赖,或 `client/src/` 下已有用户的 `.tsx` / `.jsx` | React | `references/react-migration.md` |
|
|
54
|
+
| 存在 `source_package/*.html` 且无 React | HTML | `references/html-migration.md` |
|
|
55
|
+
| 都不是 | 其他 | 参考 `source_package/` 内容,在模板中按需求重新实现,并向用户说明 |
|
|
56
|
+
|
|
57
|
+
### Step 2:按 manifest 处理依赖
|
|
58
|
+
|
|
59
|
+
依赖没有合并进工程 `package.json`。按 manifest `dependencies[].status` 处理:
|
|
60
|
+
|
|
61
|
+
| status | 含义 | 处理 |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| `missing` | 模板没有 | 迁移后的代码确实 import 了才安装,用 `package_manager` 工具安装;没用到的不装 |
|
|
64
|
+
| `present` | 模板已有(`templateRange` 为模板版本) | 沿用模板版本,不装用户版本;API 不兼容时改用户代码 |
|
|
65
|
+
| `build_chain` | 构建链 / 工具链(vite 插件、eslint、lovable-tagger 等) | 不装,模板有自己的构建体系;相关代码直接删除 |
|
|
66
|
+
| `platform_managed` | 平台包 `@lark-apaas/*` | 不装,以模板为准 |
|
|
67
|
+
|
|
68
|
+
安装遇到 peer dependency 冲突时:
|
|
69
|
+
|
|
70
|
+
- 先尝试换用支持 React 19 的版本或替代库;仍失败就放弃该依赖,改写相关代码。
|
|
71
|
+
- **禁止** `--force`、`--legacy-peer-deps`。
|
|
72
|
+
- **禁止**降级模板核心依赖(React 19、Tailwind 4、vite 等)来迁就用户代码。
|
|
73
|
+
- 妙搭不支持 Supabase:不要安装 `@supabase/*`,按迁移指南移除相关代码。
|
|
74
|
+
|
|
75
|
+
### Step 3:按迁移指南迁移代码
|
|
76
|
+
|
|
77
|
+
按 Step 1 选定的指南执行。共通要点:
|
|
78
|
+
|
|
79
|
+
- 迁移后保留的业务代码统一使用 TS / TSX:将 `client/src/`、`shared/` 中的业务 JS / JSX 原地转换,从 `source_package/` 补入的代码也按此处理,见 `react-migration.md` 4.3。HTML 项目转换出的组件和逻辑同样使用 TS / TSX。
|
|
80
|
+
- 主题:把源项目的 CSS 变量融合进 `client/src/tailwind-theme.css`(Tailwind 3 → 4 语法)。
|
|
81
|
+
- 入口:参考源项目的 App 入口(实际路径和扩展名以导入结果为准)在 `client/src/pages/` 下创建 TSX 页面,并在 `client/src/app.tsx` 的路由中替换模板的 `Welcome` 首页。
|
|
82
|
+
- 导入路径统一改为 `@/` 别名(指向 `client/src/`)。
|
|
83
|
+
- `public` 资源路径拼接 `process.env.CLIENT_BASE_PATH`。
|
|
84
|
+
- 环境变量 `import.meta.env.VITE_*` 改为 `process.env.*`。
|
|
85
|
+
|
|
86
|
+
#### 保留源项目的资源引用
|
|
87
|
+
|
|
88
|
+
迁移的目标是还原源项目,不是按新建应用的规范重做。以下内容原样保留:
|
|
89
|
+
|
|
90
|
+
- 外链图片(`<img src="https://...">`、头像、配图)和 CSS 背景图(`url(...)`)。
|
|
91
|
+
- 字体:字体族和字重都要保留,字体链接 URL 不删减。
|
|
92
|
+
- emoji 和图标:不把 emoji 换成 lucide 等图标,不删减源项目已有的图标。
|
|
93
|
+
|
|
94
|
+
具体规则:
|
|
95
|
+
|
|
96
|
+
- 不替换成 `generate_image` 生成图、模板内置头像(`img-resources/avatar-placeholders`)或其他占位图;coding-guide 中的「图片资源优先级」只适用于迁移完成后新增的内容。
|
|
97
|
+
- 外链资源即使已失效(404)或每次随机(如不带 seed 的 `picsum.photos/...?random=N`),也保持原状,不自行「修复」或补一个新效果——源页面在浏览器里就是这样显示的,替换反而会让页面与源项目不一致。在收尾说明里列出失效的资源,由用户决定是否替换。
|
|
98
|
+
- 例外:CDN `<script src>` 引入的 JS 库和 Tailwind CDN 仍按迁移指南改为 npm 包 / 模板样式体系(预览 CSP 会拦截 CDN 脚本)。
|
|
99
|
+
|
|
100
|
+
### Step 4:类型检查验证
|
|
101
|
+
|
|
102
|
+
先实际运行文件残留检查:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
find client/src shared -type f \( -name '*.js' -o -name '*.jsx' \)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
这些源码目录中保留的业务 JS / JSX 必须全部转换,不能仅凭类型检查通过就认为转换完成。`source_package/` 中的参考文件和模板配置、脚本不在转换范围内。
|
|
109
|
+
|
|
110
|
+
运行 `npm run type:check`,按报错修复,直到通过。常见错误见迁移指南末尾。不要为了让检查通过而修改 `tsconfig*.json` 等配置,也不要关闭 strict。
|
|
111
|
+
|
|
112
|
+
预览由开发服务器实时提供,改完代码即生效,不需要构建。**禁止**在沙箱里运行 `npm run build`、`build:client`、`build:prod`、`vite build`:生产构建会覆盖预览页使用的 `dist/client/index.html`,导致预览白屏。
|
|
113
|
+
|
|
114
|
+
### Step 5:收尾
|
|
115
|
+
|
|
116
|
+
1. 收尾前逐项自查,确认首页样式已生效、不是无样式的单列布局:
|
|
117
|
+
- 源项目 `tailwind.config` 的自定义主题已迁移(含 `container` 宽度),见 `react-migration.md` 1.3。
|
|
118
|
+
- 字体已迁移,见 1.4。
|
|
119
|
+
- `App.tsx` 里的全部 Provider 和 Toaster 已迁移,见第 8 节。
|
|
120
|
+
2. **必须实际运行**下面的残留检查,不能凭印象跳过:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
grep -rnE 'hsl\(var\(--|-opacity-[0-9]|-\[--' client/src
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
每一处命中都按 `react-migration.md` 修掉(`hsl(var(--x))` 见 1.1,包括 SVG 的 `stroke` / `fill` 属性;`bg-opacity-*` 等见 4.2;`-[--x]` 见 4.1),改完重跑,直到没有命中。模板自带的 `components/ui/sidebar.tsx`、`toggle-group.tsx` 中的命中(没被用户文件覆盖时)可以忽略。这几类写法在 Tailwind 4 下不报错,但颜色或透明度会静默失效(如半透明白底变成不透明白块、盖住文字)。
|
|
127
|
+
3. 删除 `.spark/import/MIGRATION_PENDING`(删除后本 skill 不再加载)。**只有业务 JS / JSX 已全部转换、类型检查通过、样式残留检查无命中、首页能展示迁移后的内容才删除**;迁移未完成时保留它,下次会话继续。
|
|
128
|
+
4. 提交全部改动(包括删除标记),确认 `git status` 没有未提交的文件。
|
|
129
|
+
5. 向用户简要说明:迁移了哪些页面与功能、未迁移或改为演示数据的部分(如 Supabase 数据、服务端逻辑)及原因、源项目中已失效的外链资源、仍需用户确认的事项。
|
|
130
|
+
|
|
131
|
+
## 约束
|
|
132
|
+
|
|
133
|
+
| 允许 | 禁止 |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| 修改 `client/src/`、`shared/` 下的代码与样式;在 `client/src/index.tsx` 的 `MainApp` 中补充源项目需要的 Provider | 修改 `vite.config.*`、`tsconfig*.json`、`eslint.config.*`、`postcss` / tailwind 配置 |
|
|
136
|
+
| 通过 `package_manager` 工具安装 Step 2 判定需要的依赖 | 手工编辑根 `package.json` 合并用户依赖 |
|
|
137
|
+
| 读取、参考 `source_package/` 中的任何文件 | 在 `source_package/` 里构建或运行,或把它作为运行时依赖 |
|
|
138
|
+
| 按需从 `source_package/` 补齐缺失的 UI 组件 | 删除 `client/src/components/ui/` 中已有组件 |
|
|
139
|
+
| | 删除或改写 `.spark/import/manifest.json`;自行执行 `miaoda app import` |
|
|
140
|
+
|
|
141
|
+
## Common Mistakes
|
|
142
|
+
|
|
143
|
+
| 错误 | 正确做法 |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| 照着 `source_package/package.json` 全量安装依赖 | 按 manifest status 逐个判断,只装代码真正用到的 `missing` 依赖 |
|
|
146
|
+
| 把 vite / `@vitejs/plugin-react-swc` / lovable-tagger 装回来 | `build_chain` 一律不装,删掉相关引用 |
|
|
147
|
+
| 用 `--legacy-peer-deps` 硬装 | 换兼容版本或放弃该依赖,改写代码 |
|
|
148
|
+
| 只复制组件、没接路由,首页仍是模板 Welcome | 创建页面并在 `client/src/app.tsx` 注册首页路由 |
|
|
149
|
+
| 跑 `npm run build` / `build:client` 验证 | 用 `npm run type:check`;生产构建会覆盖预览页导致白屏 |
|
|
150
|
+
| 把源项目的外链图片换成生成图、内置头像或占位图 | 原样保留源项目的资源引用 |
|
|
151
|
+
| 源项目的外链资源已失效,自己补一个效果(如另写噪点背景) | 保持原状,收尾时告诉用户 |
|
|
152
|
+
| 业务 JS / JSX 未转完或类型检查没过就删 `MIGRATION_PENDING` | 完成转换、类型检查通过、首页可展示后再删除 |
|
|
153
|
+
| 只迁了 `index.css`,漏了 `tailwind.config` 的 animation / colors / fontFamily | 按 `react-migration.md` 1.3 逐项迁到 `@theme` |
|
|
154
|
+
| 变量包了 `hsl()`,代码里仍写 `hsl(var(--x))` | 改为 `var(--x)` |
|
|
155
|
+
| 认为模板已有 Toaster,没挂 shadcn 的 `@/components/ui/toaster` | 模板只有 sonner,shadcn toaster 要另外挂载 |
|
|
156
|
+
| JSX 项目页面无样式时扩大 tailwind 扫描范围 | 将业务 JS / JSX 转为 TS / TSX,修复引用并检查样式,见 `react-migration.md` 4.3 |
|
|
157
|
+
| 把 emoji 换成图标、删减字体字重 | 原样保留 |
|
|
158
|
+
| 删了标记却没提交,或留下未提交文件 | 提交全部改动,`git status` 干净 |
|
|
159
|
+
| 迁移前反复向用户确认 | 直接开始迁移,完成后再说明结果 |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Gemini 平台差异
|
|
2
|
+
|
|
3
|
+
与 `react-migration.md` 一起使用:通用步骤按该文件执行,本文件只列 Gemini 特有的部分。
|
|
4
|
+
|
|
5
|
+
## 环境差异
|
|
6
|
+
|
|
7
|
+
| 项 | Gemini | 妙搭模板 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| React | 19 | 19 |
|
|
10
|
+
| Tailwind | CDN + `index.html` 内联 `tailwind.config` | 4 |
|
|
11
|
+
| 目录结构 | 根目录平铺(无 `src/`) | `client/src/` |
|
|
12
|
+
| AI 能力 | `@google/genai` | 妙搭 AI 插件 |
|
|
13
|
+
|
|
14
|
+
## 文件位置
|
|
15
|
+
|
|
16
|
+
| Gemini 源路径 | 当前位置 | 处理 |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `components/**` | `client/src/components/**` | 直接可用,不要重新创建 |
|
|
19
|
+
| `services/**` | `client/src/services/**` | 含 `geminiService`,需迁移为 AI 插件 |
|
|
20
|
+
| `types.ts`、`constants.ts` | `client/src/types.ts`、`client/src/constants.ts` | 直接可用 |
|
|
21
|
+
| `App.tsx` | `source_package/App.tsx` | 参考创建页面 |
|
|
22
|
+
| `index.tsx` | `source_package/index.tsx` | 不需要 |
|
|
23
|
+
| `index.html` | `source_package/index.html` | 提取主题、字体、内联样式 |
|
|
24
|
+
| 其他 | `source_package/` | 按需参考 |
|
|
25
|
+
|
|
26
|
+
## 特有步骤
|
|
27
|
+
|
|
28
|
+
1. **主题**:`source_package/index.html` 中 `<script>tailwind.config = {...}</script>` 的颜色、字体写进 `client/src/tailwind-theme.css`:
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
:root {
|
|
32
|
+
--brand-accent: #00dc82;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@theme inline {
|
|
36
|
+
--color-brand-accent: var(--brand-accent);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`<head>` 中的 Google Fonts `<link>` 复制到 `client/index.html`;`<style>` 中的 CSS(字体、滚动条等)追加到 `client/src/index.css`。删除 Tailwind CDN script,`client/index.html` 中不能保留 `<script src="https://...">`。
|
|
41
|
+
|
|
42
|
+
2. **入口**:参考 `source_package/App.tsx` 创建 `client/src/pages/Home/Home.tsx`,复制完整 JSX,**保留外层 div 及其类名**(如 `min-h-screen flex flex-col`,是布局必需的);把 `App.tsx` 中的 state 一并迁过来。在 `client/src/app.tsx` 注册首页路由。
|
|
43
|
+
|
|
44
|
+
3. **导入路径**:组件中的 `../constants`、`../types`、`../services/...` 改为 `@/constants`、`@/types`、`@/services/...`。
|
|
45
|
+
|
|
46
|
+
4. **`@google/genai` → 妙搭 AI 插件(关键)**:不要安装 `@google/genai`。先读 `plugin-guide` skill,再按其流程:
|
|
47
|
+
1. 分析 `client/src/services/geminiService.ts` 中每个函数的用途,对应到 AI 智能生文 / 生图 / 图片理解等插件。
|
|
48
|
+
2. 复用 `available_plugin_instances` 中合适的实例;没有就用 `plugin_instance` 工具创建,禁止直接改 `server/capabilities/`。
|
|
49
|
+
3. 对实例调用 `get_plugin_ai_json` 拿到 actions、inputSchema、outputSchema、outputMode,严格按 schema 构造参数,不要猜。
|
|
50
|
+
4. 默认在 Client 侧调用:`unary` 用 `capabilityClient.load(id).call(actionKey, input)`,`stream` 用 `callStream`;在 `client/src/services/aiService.ts` 中实现并替换原调用。
|
|
51
|
+
|
|
52
|
+
5. **环境变量**:`process.env.API_KEY` / `GEMINI_API_KEY` 随 `@google/genai` 一起移除,插件调用不需要用户自带 key。
|
|
53
|
+
|
|
54
|
+
## 常见编译错误
|
|
55
|
+
|
|
56
|
+
| 错误 | 修复 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `Cannot find module '@google/genai'` | 按第 4 步迁移为 AI 插件 |
|
|
59
|
+
| `Cannot find module '../constants'` / `'../types'` | 改为 `@/constants` / `@/types` |
|
|
60
|
+
| `bg-brand-accent` 等类无效 | 主题变量未写入 `tailwind-theme.css` |
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# HTML 项目迁移指南
|
|
2
|
+
|
|
3
|
+
适用于不含 React 的普通 zip 包:`*.html` + `css/` + `js/` + 静态资源。
|
|
4
|
+
|
|
5
|
+
## 文件位置
|
|
6
|
+
|
|
7
|
+
| 源路径 | 当前位置 | 说明 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `*.html` | `source_package/*.html` | 转换为 React 页面 |
|
|
10
|
+
| `css/**`、`js/**` | `source_package/` | 参考转换 |
|
|
11
|
+
| `src/**`、`public/**` | `client/src/**`、`client/public/**` | 已放进工程 |
|
|
12
|
+
| 其他(`images/`、`assets/`、`fonts/` 等) | `source_package/` | 需要的搬到 `client/public/` |
|
|
13
|
+
|
|
14
|
+
## 迁移顺序
|
|
15
|
+
|
|
16
|
+
1. H0:外部依赖扫描,CDN 改 npm(**必须最先做**)
|
|
17
|
+
2. H1:分析 HTML 结构
|
|
18
|
+
3. 主题与样式迁移
|
|
19
|
+
4. H2:HTML 转 React 组件
|
|
20
|
+
5. H3:CSS 迁移
|
|
21
|
+
6. H4:JS 逻辑转 React state
|
|
22
|
+
7. 静态资源与路径修正
|
|
23
|
+
8. H5:创建页面并配置路由
|
|
24
|
+
9. `npm run type:check` 类型检查验证(不要运行 `npm run build`,见 SKILL.md Step 4)
|
|
25
|
+
|
|
26
|
+
## H0. 外部依赖扫描,CDN 改 npm
|
|
27
|
+
|
|
28
|
+
CDN `<script src>` 引入的全局变量(`d3`、`gsap`、`am5` 等)在 React 组件里不存在。先转 React 会写出 `declare const xxx: any` 兜底,这是错误用法。先把 CDN 换成 npm 包。
|
|
29
|
+
|
|
30
|
+
扫描:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
grep -n -E 'src="https?://[^"]+\.(js|css)"' source_package/*.html
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
逐条按规则归类:
|
|
37
|
+
|
|
38
|
+
| 来源 | 处理 |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| 模板 `package.json` 已有 | 改 ES `import`,删 CDN 引用 |
|
|
41
|
+
| 有官方 npm 包 | 用 PackageManager 安装(dependencies,不是 dev),改 `import`,删 CDN 引用 |
|
|
42
|
+
| 无 npm 等价(极少见) | 保持原状,并在收尾说明中列出 |
|
|
43
|
+
| Tailwind / Bootstrap 等 CSS 框架 | 删 CDN,样式按「主题与样式迁移」处理 |
|
|
44
|
+
|
|
45
|
+
示例(多数库 npm 包名就是库名):
|
|
46
|
+
|
|
47
|
+
| CDN 库 | 处理 |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| ECharts | 先查模板是否已有 `echarts` / `echarts-for-react`,有就不要重装 |
|
|
50
|
+
| Chart.js | 优先改用 echarts;不可行再装 `chart.js` |
|
|
51
|
+
| amCharts 5 | `@amcharts/amcharts5`,地图数据 `@amcharts/amcharts5-geodata` |
|
|
52
|
+
| Three.js | `three`,React 集成 `@react-three/fiber` |
|
|
53
|
+
|
|
54
|
+
H0 完成检查:
|
|
55
|
+
|
|
56
|
+
- [ ] 扫描出的所有库都已处理。
|
|
57
|
+
- [ ] 最终 `client/index.html` 中没有任何 `<script src="https://...">`(预览 CSP 会拦截 CDN,导致白屏)。
|
|
58
|
+
- [ ] 没有 `declare const xxx: any` 这类全局变量兜底声明。
|
|
59
|
+
|
|
60
|
+
## H1. 分析 HTML 结构
|
|
61
|
+
|
|
62
|
+
分析 `source_package/*.html`,识别页面结构(header / main / footer)和可复用的部分(导航栏、卡片、表单等)。
|
|
63
|
+
|
|
64
|
+
## 主题与样式迁移
|
|
65
|
+
|
|
66
|
+
读取源 CSS(`source_package/css/*.css` 或 `<style>` 块)里的颜色、字体等设计 token,融合到 `client/src/tailwind-theme.css`。颜色变量统一写成 `hsl()` 形式,并在 `@theme inline` 中映射:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
:root {
|
|
70
|
+
--primary: hsl(210 100% 50%);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@theme inline {
|
|
74
|
+
--color-primary: var(--primary);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## H2. HTML 转 React 组件
|
|
79
|
+
|
|
80
|
+
| HTML | JSX |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `class` | `className` |
|
|
83
|
+
| `for` | `htmlFor` |
|
|
84
|
+
| `onclick="..."` | `onClick={handler}` |
|
|
85
|
+
| `style="color: red"` | `style={{ color: 'red' }}` |
|
|
86
|
+
| `<img>`、`<input>`、`<br>` | 自闭合 `/>` |
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// client/src/components/HeroSection.tsx
|
|
90
|
+
const BASE_PATH = process.env.CLIENT_BASE_PATH || '';
|
|
91
|
+
|
|
92
|
+
export const HeroSection = () => {
|
|
93
|
+
const handleClick = () => {
|
|
94
|
+
/* 原 JS 逻辑 */
|
|
95
|
+
};
|
|
96
|
+
return (
|
|
97
|
+
<section className="hero">
|
|
98
|
+
<h1 className="title">Welcome</h1>
|
|
99
|
+
<button className="btn" onClick={handleClick}>Get Started</button>
|
|
100
|
+
<img src={`${BASE_PATH}/images/hero.png`} alt="Hero" />
|
|
101
|
+
</section>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## H3. CSS 迁移
|
|
107
|
+
|
|
108
|
+
- 推荐:转为 Tailwind 类(`padding: 16px` → `p-4`,`display:flex; justify-content:center` → `flex justify-center`)。
|
|
109
|
+
- 复杂或量大的样式可保留原 CSS:放到 `client/src/styles/legacy.css`,在页面或 `client/src/index.css` 中引入。
|
|
110
|
+
|
|
111
|
+
## H4. JS 逻辑转 React
|
|
112
|
+
|
|
113
|
+
| 原生写法 | React 写法 |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `document.getElementById('x').classList.toggle('hidden')` | `const [visible, setVisible] = useState(false)` + 条件 className |
|
|
116
|
+
| 表单 `input.value` 读写 | 受控组件 `value` + `onChange` |
|
|
117
|
+
| `setInterval` / 事件监听 | `useEffect` 中注册,并在 cleanup 中清除 |
|
|
118
|
+
| jQuery | 移除,改 React state 与事件 |
|
|
119
|
+
| Bootstrap JS | 移除,改用模板 `client/src/components/ui/` 中的组件 |
|
|
120
|
+
|
|
121
|
+
## 静态资源与路径
|
|
122
|
+
|
|
123
|
+
- 被引用的 `images/`、`fonts/` 等资源搬到 `client/public/` 下同名目录。
|
|
124
|
+
- 引用时拼接 `process.env.CLIENT_BASE_PATH`;CSS 里的背景图改成内联 style 拼路径,或搬到 `client/src/assets/` 用 import。
|
|
125
|
+
|
|
126
|
+
## H5. 页面与路由
|
|
127
|
+
|
|
128
|
+
- 每个 HTML 文件对应一个 React 页面,放在 `client/src/pages/<Name>/<Name>.tsx`。
|
|
129
|
+
- 首页(通常是 `index.html`)在 `client/src/app.tsx` 中替换模板的 `Welcome`;其他页面注册对应路由。
|
|
130
|
+
- 内部链接 `<a href="about.html">` 改为 `<Link to="/about">`;外部链接保留 `<a target="_blank" rel="noopener noreferrer">`。
|
|
131
|
+
|
|
132
|
+
## 常见编译错误
|
|
133
|
+
|
|
134
|
+
| 错误 | 原因 | 修复 |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| `class` / `for` 属性无效 | HTML 属性 | 改为 `className` / `htmlFor` |
|
|
137
|
+
| JSX must have one parent | 多个根元素 | 用 `<>...</>` 包裹 |
|
|
138
|
+
| `xxx is not defined` / 预览白屏 | CDN 全局变量 | 按 H0 改 npm |
|
|
139
|
+
| 图片 404 | 未拼接路径 | 拼接 `CLIENT_BASE_PATH` |
|
|
140
|
+
| `Parameter implicitly has an 'any' type` | 缺类型 | 补 TypeScript 类型 |
|
|
141
|
+
|
|
142
|
+
## 验收
|
|
143
|
+
|
|
144
|
+
1. `npm run type:check` 通过。
|
|
145
|
+
2. 首页路由展示迁移后的内容,各 HTML 页面都有对应路由。
|
|
146
|
+
3. `client/index.html` 中没有 CDN script。
|
|
147
|
+
4. 主要交互可响应,样式与源页面基本一致。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Lovable 平台差异
|
|
2
|
+
|
|
3
|
+
与 `react-migration.md` 一起使用:通用步骤按该文件执行,本文件只列 Lovable 特有的部分。
|
|
4
|
+
|
|
5
|
+
## 环境差异
|
|
6
|
+
|
|
7
|
+
| 项 | Lovable | 妙搭模板 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| React | 18 | 19 |
|
|
10
|
+
| Tailwind | 3(裸 HSL 变量) | 4(`hsl()` + `@theme inline`) |
|
|
11
|
+
| 后端 | Supabase | 不支持,需移除 |
|
|
12
|
+
|
|
13
|
+
## 文件位置
|
|
14
|
+
|
|
15
|
+
| Lovable 源路径 | 当前位置 | 处理 |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `src/components/**` | `client/src/components/**` | 直接可用;移除其中的 Supabase 依赖 |
|
|
18
|
+
| `src/pages/**` | `client/src/pages/**` | 直接可用 |
|
|
19
|
+
| `src/hooks/**` | `client/src/hooks/**` | 直接可用(`use-mobile.tsx` 已排除,用模板的 `use-mobile.ts`) |
|
|
20
|
+
| `src/lib/**` | `client/src/lib/**` | 直接可用(`supabase.ts` 已排除) |
|
|
21
|
+
| `src/integrations/**`、`src/lib/supabase.ts`、`supabase/**` | `source_package/` | 不迁移 |
|
|
22
|
+
| `src/App.tsx` | `source_package/src/App.tsx` | 参考创建页面与路由 |
|
|
23
|
+
| `src/main.tsx` | `source_package/src/main.tsx` | 不需要 |
|
|
24
|
+
| `src/index.css` | `source_package/src/index.css` | 提取主题变量与自定义样式 |
|
|
25
|
+
| `public/**` | `client/public/**` | 路径不变,引用需拼 `CLIENT_BASE_PATH` |
|
|
26
|
+
|
|
27
|
+
## 特有步骤
|
|
28
|
+
|
|
29
|
+
1. **主题**:`source_package/src/index.css` 是 Tailwind 3 写法,`:root` / `.dark` 中的裸 HSL 值(如 `--primary: 210 100% 50%`)全部包裹成 `hsl()` 后融合进 `client/src/tailwind-theme.css`;`@layer`、`@keyframes` 等自定义样式追加到 `client/src/index.css`。代码里的 `hsl(var(--x))` 同步改为 `var(--x)`;`tailwind.config.ts` 的 `keyframes` / `animation`(Lovable 常见 `fade-in`、`slide-up` 等)按 `react-migration.md` 1.3 迁到 `@theme`。
|
|
30
|
+
2. **Tailwind 变量语法**:Lovable 组件大量使用 `w-[--sidebar-width]` 这类写法,必须全量改为 `w-[var(--sidebar-width)]`(grep `-[--`)。
|
|
31
|
+
3. **Supabase**:Lovable 项目几乎都带 Supabase 认证与数据读写,按 `react-migration.md` 第 5 节逐项移除(`supabase.auth.*` → 演示登录态,`supabase.from()` → 本地 state + 静态数据)。
|
|
32
|
+
4. **lovable-tagger**:删除 `componentTagger` / `ComponentTagger` 的 import 与使用;该依赖在 manifest 中为 `build_chain`,不要安装。
|
|
33
|
+
5. **入口**:参考 `source_package/src/App.tsx` 创建 `client/src/pages/Home/Home.tsx`,复制 JSX 结构(去掉外层 Router / Provider),并在 `client/src/app.tsx` 注册路由。
|
|
34
|
+
6. **Toaster**:Lovable 的 `App.tsx` 通常同时挂 `@/components/ui/toaster`(shadcn toast)和 `@/components/ui/sonner`。模板只挂了 sonner,shadcn 的 `Toaster` 要按 `react-migration.md` 第 8 节加到 `MainApp`。
|
|
35
|
+
|
|
36
|
+
## 常见编译错误
|
|
37
|
+
|
|
38
|
+
| 错误 | 修复 |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `Cannot find module 'lovable-tagger'` | 删除相关代码 |
|
|
41
|
+
| `Cannot find module '@/integrations/supabase/client'` | 按第 3 步移除 Supabase |
|
|
42
|
+
| `bg-primary` 无效 | 变量值包裹 `hsl()` |
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# React 项目迁移指南
|
|
2
|
+
|
|
3
|
+
适用于 Lovable、Replit、Gemini 导出的 React 项目,以及含 React 的普通 zip 包。这里是各平台共通的步骤;来自 Lovable / Replit / Gemini 的项目还要同时读对应的平台文件(`lovable.md` / `replit.md` / `gemini.md`),两者冲突时以平台文件为准。
|
|
4
|
+
|
|
5
|
+
## 环境差异
|
|
6
|
+
|
|
7
|
+
| 项 | 源项目(常见) | 妙搭模板 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| React | 18 | 19 |
|
|
10
|
+
| Tailwind | 3 | 4 |
|
|
11
|
+
| 构建 | 自带 vite 配置 | 模板 vite 配置(不可改) |
|
|
12
|
+
| 前端根目录 | `src/` | `client/src/`(`@/` 别名指向这里) |
|
|
13
|
+
|
|
14
|
+
## 迁移顺序(严格遵守)
|
|
15
|
+
|
|
16
|
+
源项目含 JS / JSX 时,先按 4.3 转为 TS / TSX,再执行以下步骤。
|
|
17
|
+
|
|
18
|
+
1. 主题与样式迁移
|
|
19
|
+
2. 修正导入路径
|
|
20
|
+
3. 修正图片与静态资源引用
|
|
21
|
+
4. 修复组件兼容问题(Tailwind 4 语法、缺失组件)
|
|
22
|
+
5. 移除 Supabase / 服务端逻辑
|
|
23
|
+
6. 删除平台特定代码(如 lovable-tagger)
|
|
24
|
+
7. 环境变量适配
|
|
25
|
+
8. 创建页面入口
|
|
26
|
+
9. 更新路由
|
|
27
|
+
10. `npm run type:check` 类型检查验证(不要运行 `npm run build`,见 SKILL.md Step 4)
|
|
28
|
+
|
|
29
|
+
## 1. 主题与样式迁移
|
|
30
|
+
|
|
31
|
+
### 1.1 CSS 颜色变量
|
|
32
|
+
|
|
33
|
+
读取源项目样式入口(Lovable / zip:`source_package/src/index.css`;Replit:`source_package/client/src/index.css`)中 `:root`、`.dark` 的颜色变量,融合到 `client/src/tailwind-theme.css`。同名变量直接覆盖,不要为源项目新增一套平行变量。
|
|
34
|
+
|
|
35
|
+
Tailwind 3 → 4 的 HSL 语法变化:
|
|
36
|
+
|
|
37
|
+
```css
|
|
38
|
+
/* 源:Tailwind 3 裸 HSL 值 */
|
|
39
|
+
:root {
|
|
40
|
+
--primary: 210 100% 50%;
|
|
41
|
+
--background: 0 0% 100%;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* 目标:必须包裹 hsl() */
|
|
45
|
+
:root {
|
|
46
|
+
--primary: hsl(210 100% 50%);
|
|
47
|
+
--background: hsl(0 0% 100%);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@theme inline {
|
|
51
|
+
--color-primary: var(--primary);
|
|
52
|
+
--color-background: var(--background);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
变量值包上 `hsl()` 后,代码里原来的 `hsl(var(--x))` 就成了 `hsl(hsl(...))`,颜色失效(SVG 描边、图表配色消失)。全量 grep `hsl(var(--`,包括 TSX 里的 `stroke` / `fill` 属性(如 `stroke="hsl(var(--secondary))"`)、内联 style、图表配置和 Tailwind 任意值,统一改为 `var(--x)`;带透明度的 `hsl(var(--x) / 0.5)` 改为 `color-mix(in srgb, var(--x) 50%, transparent)`。
|
|
57
|
+
|
|
58
|
+
### 1.2 自定义样式
|
|
59
|
+
|
|
60
|
+
源样式入口里的自定义样式(`@layer`、`@keyframes`、全局类等)追加到 `client/src/index.css` 末尾。
|
|
61
|
+
|
|
62
|
+
### 1.3 tailwind.config 中的自定义主题
|
|
63
|
+
|
|
64
|
+
Tailwind 4 不读源项目的 `tailwind.config.*`。打开 `source_package/tailwind.config.*`,把 `theme.extend` 中每一项迁到 `client/src/tailwind-theme.css` 的 `@theme`:
|
|
65
|
+
|
|
66
|
+
| 源配置 | 迁移为 |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `keyframes` + `animation` | `@keyframes slide-up { ... }` 写进 `client/src/index.css`,`@theme` 中加 `--animate-slide-up: slide-up 0.5s ease-out forwards;` |
|
|
69
|
+
| `colors` | `--color-<name>: ...;`(嵌套色阶展开为 `--color-<name>-<shade>`) |
|
|
70
|
+
| `fontFamily` | `--font-<name>: ...;` |
|
|
71
|
+
| `boxShadow` / `backgroundImage` / `borderRadius` | `--shadow-*` / 写成 `@utility` / `--radius-*` |
|
|
72
|
+
| `theme.container`(如 `center: true, padding: '2rem', screens: { '2xl': '1400px' }`) | 在 `client/src/index.css` 用 `@utility container` 还原,见下方示例 |
|
|
73
|
+
|
|
74
|
+
`container` 不迁移时,Tailwind 4 会按默认断点把宽度卡在 1280px,比源项目窄。Lovable 常见配置 `center: true, padding: '2rem', screens: { '2xl': '1400px' }` 对应:
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
@utility container {
|
|
78
|
+
margin-inline: auto;
|
|
79
|
+
padding-inline: 2rem;
|
|
80
|
+
@media (width >= 40rem) { max-width: none; }
|
|
81
|
+
@media (width >= 1400px) { max-width: 1400px; }
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`max-width: none` 用来取消 Tailwind 4 默认的分档宽度,第二个 `@media` 写源项目 `screens` 里的值。
|
|
86
|
+
|
|
87
|
+
遗漏的后果很隐蔽:`animate-*` 动画类不存在时,带 `opacity-0` 初始态的元素会一直不可见。迁移后 grep 代码里用到的 `animate-`、`font-` 和自定义颜色类,逐个确认都有定义。
|
|
88
|
+
|
|
89
|
+
### 1.4 字体
|
|
90
|
+
|
|
91
|
+
源项目的字体引入(`index.html` 里的 Google Fonts `<link>`、`index.css` 里的 `@import url(...)`)原样复制,不要删减 URL 中的字重和字体族:只剩一个字重时,正文会整体变细或变粗。复制到 `client/index.html` 的 `<head>`。再按源项目设置 `body` 或 `--font-sans` 的 `font-family`,不要让页面回落到模板默认字体。
|
|
92
|
+
|
|
93
|
+
## 2. 修正导入路径
|
|
94
|
+
|
|
95
|
+
组件已放进 `client/src/`,内部相对路径改为 `@/` 别名:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// 源
|
|
99
|
+
import { Button } from '../components/ui/button';
|
|
100
|
+
import { cn } from '../lib/utils';
|
|
101
|
+
|
|
102
|
+
// 目标
|
|
103
|
+
import { Button } from '@/components/ui/button';
|
|
104
|
+
import { cn } from '@/lib/utils';
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Replit 项目里 `@shared/*` 指向的 `shared/` 已原位放置,按模板 `tsconfig` 的别名修正引用;不要改 tsconfig。
|
|
108
|
+
|
|
109
|
+
## 3. 图片与静态资源引用
|
|
110
|
+
|
|
111
|
+
外链资源(`https://...` 图片、背景图、字体)原样保留,不替换成生成图或占位图,失效的也不修,见 SKILL.md「保留源项目的资源引用」。下面只处理本地资源。
|
|
112
|
+
|
|
113
|
+
应用可能部署在子路径下,引用 `client/public/` 下的资源必须拼接 `process.env.CLIENT_BASE_PATH`:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
const BASE_PATH = process.env.CLIENT_BASE_PATH || '';
|
|
117
|
+
|
|
118
|
+
// ❌ 子路径部署时失效
|
|
119
|
+
<img src="/logo.png" />
|
|
120
|
+
|
|
121
|
+
// ✅
|
|
122
|
+
<img src={`${BASE_PATH}/logo.png`} />
|
|
123
|
+
<div style={{ backgroundImage: `url('${BASE_PATH}/images/bg.jpg')` }} />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`src/assets/` 下的图片已在 `client/src/assets/`,用 import 引用(构建工具自动处理路径):
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import heroImage from '@/assets/hero.png';
|
|
130
|
+
<img src={heroImage} />
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
运行时 `fetch` 加载 public 下的 JSON / CSV 也会因子路径错位,报 404 或 `Unexpected token '<'`:
|
|
134
|
+
|
|
135
|
+
- 静态数据优先搬到 `client/src/data/` 改用 `import`(编译期内联)。
|
|
136
|
+
- 体积大(> 100KB)或必须动态加载的,拼 `BASE_PATH` 再 `fetch`。
|
|
137
|
+
|
|
138
|
+
检查清单:
|
|
139
|
+
|
|
140
|
+
1. 搜索 `src="/`、`url('/`、`fetch('/`、`fetch('./`,改为拼接 `BASE_PATH` 或 import。
|
|
141
|
+
2. 搜索 import 图片扩展名(`.png` `.jpg` `.svg` `.gif` `.webp`),改为 `@/assets/` 路径,并确认文件存在。
|
|
142
|
+
|
|
143
|
+
## 4. 组件兼容修复
|
|
144
|
+
|
|
145
|
+
### 4.1 Tailwind 4 CSS 变量语法(必须执行)
|
|
146
|
+
|
|
147
|
+
Tailwind 3 允许直接写 CSS 变量名,Tailwind 4 要求 `var()` 包裹:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// ❌ Tailwind 3
|
|
151
|
+
className="w-[--sidebar-width] h-[--header-height]"
|
|
152
|
+
|
|
153
|
+
// ✅ Tailwind 4
|
|
154
|
+
className="w-[var(--sidebar-width)] h-[var(--header-height)]"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
用 grep 搜索 `-[--` 找出全部旧写法,改完再搜一次确认没有遗漏。
|
|
158
|
+
|
|
159
|
+
### 4.2 Tailwind 3 → 4 移除或改名的写法(必须执行)
|
|
160
|
+
|
|
161
|
+
| Tailwind 3 | Tailwind 4 |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| `bg-opacity-50`、`text-opacity-*`、`border-opacity-*`、`ring-opacity-*` 等 | 已移除,改为颜色斜杠写法:`bg-black bg-opacity-50` → `bg-black/50` |
|
|
164
|
+
| `border` 不带颜色(v3 默认 gray-200) | v4 默认 `currentColor`;源项目依赖默认色时补 `border-border` 或具体颜色 |
|
|
165
|
+
| `ring`(v3 默认 3px 蓝色) | v4 默认 1px `currentColor`;改为 `ring-3 ring-blue-500/50` 等显式写法 |
|
|
166
|
+
| `flex-shrink-*` / `flex-grow-*` | `shrink-*` / `grow-*` |
|
|
167
|
+
| `outline-none` | `outline-hidden` |
|
|
168
|
+
|
|
169
|
+
grep 每一行左列的写法,改完再 grep 一次确认。
|
|
170
|
+
|
|
171
|
+
### 4.3 JS / JSX 项目
|
|
172
|
+
|
|
173
|
+
源项目是 `.js` / `.jsx`(如 NoCode)时,将迁移后保留的业务代码统一转换为 TS / TSX,适配模板现有的 `ts,tsx,css` 扫描范围,不修改 tailwind 配置:
|
|
174
|
+
|
|
175
|
+
- CLI 已直接放进 `client/src/`、`shared/` 的业务文件原地转换;从 `source_package/` 补入的代码也要转换,原始参考文件保留。
|
|
176
|
+
- 含 JSX 的 `.jsx` 或 `.js` 改为 `.tsx`,不含 JSX 的业务 `.js` 改为 `.ts`。补齐必要的 Props、事件、ref、state 等类型;不要用 `@ts-nocheck` 或批量 `any` 绕过检查,也不要修改 tsconfig。
|
|
177
|
+
- 同步修复带扩展名的 import / export、动态 import 和文件匹配规则;确认转换后的代码被实际引用,删除旧 JS / JSX 文件。已有同名 TS / TSX 文件时先核对用途,不能直接覆盖或留下两份实现。
|
|
178
|
+
- zip 导入中与模板同名的 JS 版 UI 组件放在 `source_package/src/components/ui/`(manifest `protectedConflicts`),工程里保留的是模板 TS 版。业务代码直接用模板版本,props 不兼容时改调用方,不要把源组件转换后覆盖回去;模板缺失的组件按 4.4 补齐并转成 TS / TSX。
|
|
179
|
+
- 只处理业务代码,不批量改名 `source_package/`、模板配置或运行脚本。用户放在 public 中参与运行的业务 JS 也应迁入源码目录转换,不能用静态脚本加载来绕过迁移。
|
|
180
|
+
|
|
181
|
+
### 4.4 缺失组件
|
|
182
|
+
|
|
183
|
+
报 `Cannot find module '@/components/ui/xxx'` 时:
|
|
184
|
+
|
|
185
|
+
1. 先看 `client/src/components/ui/` 是否已有该组件(模板自带一套 shadcn 组件)。
|
|
186
|
+
2. 没有再从 `source_package/` 中找到源组件迁移过来,内部路径改为 `@/`。
|
|
187
|
+
3. 不要删除模板已有的 UI 组件。
|
|
188
|
+
|
|
189
|
+
### 4.5 React 19 兼容
|
|
190
|
+
|
|
191
|
+
- 删除 `React.FC` 隐式 `children` 依赖,显式声明 `children` 类型。
|
|
192
|
+
- `ReactDOM.render` / `react-dom/test-utils` 等已移除的 API 不要再用(入口由模板 `client/src/index.tsx` 负责)。
|
|
193
|
+
- 依赖库不支持 React 19 时,换支持的版本或替代库(见 SKILL.md Step 2)。
|
|
194
|
+
|
|
195
|
+
## 5. 移除 Supabase 与服务端逻辑
|
|
196
|
+
|
|
197
|
+
妙搭不支持 Supabase。supabase 集成文件已放在 `source_package/`,需要从业务代码中移除引用。
|
|
198
|
+
|
|
199
|
+
| 搜索关键词 | 处理 |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| `@supabase`、`@/integrations/supabase`、`@/lib/supabase`、`@/db/` | 删除导入 |
|
|
202
|
+
| `supabase.auth.` | 换成简化的演示登录态(本地 state) |
|
|
203
|
+
| `supabase.from(` | 换成本地 state + 静态示例数据,增删改操作改本地 state |
|
|
204
|
+
| `supabase.channel(`、`supabase.storage.` | 删除,或改用静态资源 |
|
|
205
|
+
| `useAuth` | 确认与简化后的 AuthContext 兼容 |
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
// ✅ 本地 state + 静态数据示例
|
|
209
|
+
const [items, setItems] = useState([
|
|
210
|
+
{ id: 1, name: '示例数据1', status: 'active' },
|
|
211
|
+
{ id: 2, name: '示例数据2', status: 'pending' },
|
|
212
|
+
]);
|
|
213
|
+
const handleAdd = (item: Item) => setItems((prev) => [...prev, { ...item, id: Date.now() }]);
|
|
214
|
+
const handleDelete = (id: number) => setItems((prev) => prev.filter((i) => i.id !== id));
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Replit 的 `server/`、Gemini 的后端调用同理:不迁移服务端,前端调用改为静态数据或本地逻辑。用户需要真实数据能力时,在收尾说明里建议后续用妙搭的数据库与接口能力实现,不在本次迁移中自行搭建。
|
|
218
|
+
|
|
219
|
+
## 6. 删除平台特定代码
|
|
220
|
+
|
|
221
|
+
- Lovable:删除 `lovable-tagger` 的 import 与使用(`componentTagger` / `ComponentTagger`)。
|
|
222
|
+
- Replit:删除 `@replit/*` 插件、`replit` 运行时 banner 等引用。
|
|
223
|
+
- 其他平台的开发期插件同理,以 manifest 中 `build_chain` 的依赖为线索。
|
|
224
|
+
|
|
225
|
+
## 7. 环境变量
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import.meta.env.VITE_API_URL → process.env.VITE_API_URL
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
源项目依赖的密钥类变量(如 Supabase key、第三方 API key)不要写进代码,删除相关逻辑或在收尾时提示用户在妙搭环境变量中配置。
|
|
232
|
+
|
|
233
|
+
## 8. 创建页面入口
|
|
234
|
+
|
|
235
|
+
参考源入口(Lovable / zip:`source_package/src/App.tsx`;Replit:`source_package/client/src/App.tsx`;Gemini:`source_package/App.tsx`;这些是 TS 示例路径,源入口也可能为 `.jsx` / `.js`,或已放在 `client/src/`,按 manifest 和实际文件定位):
|
|
236
|
+
|
|
237
|
+
1. 读懂源 `App.tsx` 的页面结构与路由。
|
|
238
|
+
2. 首页创建为 `client/src/pages/Home/Home.tsx`,复制 JSX 结构,去掉外层 Router / Provider(模板入口已提供)。
|
|
239
|
+
3. 源项目有多个页面的,已在 `client/src/pages/` 下的直接复用;只在 `App.tsx` 内联的拆成独立页面。
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
// client/src/pages/Home/Home.tsx
|
|
243
|
+
import { HeroSection } from '@/components/HeroSection';
|
|
244
|
+
import { Features } from '@/components/Features';
|
|
245
|
+
|
|
246
|
+
const Home = () => (
|
|
247
|
+
<>
|
|
248
|
+
<HeroSection />
|
|
249
|
+
<Features />
|
|
250
|
+
</>
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
export default Home;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
源 `App.tsx` / `main.tsx` 里挂载的每个 Provider 和全局组件都要逐个对照处理,漏掉一个就会让相关功能静默失效:
|
|
257
|
+
|
|
258
|
+
- 模板已有的不重复添加:`BrowserRouter`、`AppContainer`、`ErrorBoundary`,以及 **sonner** 的 `Toaster`(`@/components/ui/sonner`)。
|
|
259
|
+
- shadcn 旧版 toast(`@/components/ui/toaster` + `useToast` / `@/hooks/use-toast`)是另一套,模板没有挂载。源项目用到它时,必须在 `MainApp` 里挂上 `@/components/ui/toaster` 的 `Toaster`(和 sonner 的同名,import 时起别名)。否则所有 `toast(...)` 都不显示。
|
|
260
|
+
- 其余(`QueryClientProvider`、`TooltipProvider`、主题 / Auth Context 等)加到 `client/src/index.tsx` 的 `MainApp` 中,包在模板已有 Provider 的内层。
|
|
261
|
+
|
|
262
|
+
`Layout.tsx` 只是 Outlet 容器,不放 Provider。
|
|
263
|
+
|
|
264
|
+
## 9. 更新路由
|
|
265
|
+
|
|
266
|
+
修改 `client/src/app.tsx`,用迁移后的首页替换模板的 `Welcome`,并补齐源项目的其他路由:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import Home from './pages/Home/Home';
|
|
270
|
+
|
|
271
|
+
const RoutesComponent = () => (
|
|
272
|
+
<Routes>
|
|
273
|
+
<Route element={<Layout />}>
|
|
274
|
+
<Route index element={<Home />} />
|
|
275
|
+
{/* 源项目的其他路由 */}
|
|
276
|
+
</Route>
|
|
277
|
+
<Route path="*" element={<NotFound />} />
|
|
278
|
+
</Routes>
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- 路由定义写在 `RoutesComponent` 中,`Layout.tsx` 只是 Outlet 容器。
|
|
283
|
+
- 模板入口已用 `BrowserRouter basename={CLIENT_BASE_PATH}`,源项目里的 `BrowserRouter` / `HashRouter` 不要再包一层。
|
|
284
|
+
|
|
285
|
+
## 10. 常见编译错误
|
|
286
|
+
|
|
287
|
+
| 错误 | 原因 | 修复 |
|
|
288
|
+
| --- | --- | --- |
|
|
289
|
+
| `Cannot find module 'lovable-tagger'` | 平台特定依赖 | 删除相关代码 |
|
|
290
|
+
| `Cannot find module '../lib/utils'` | 路径变化 | 改为 `@/lib/utils` |
|
|
291
|
+
| `Cannot find module '@/...'` | 文件不在 `client/src/` | 检查是否留在 `source_package/`,按需迁移 |
|
|
292
|
+
| `Cannot find module '@supabase/...'` | 未移除 Supabase | 按第 5 节移除 |
|
|
293
|
+
| `bg-primary` 等颜色无效 | HSL 未包裹 | 变量值包裹 `hsl()` |
|
|
294
|
+
| SVG 描边 / 图表颜色消失 | `hsl(var(--x))` 双重包裹 | 改为 `var(--x)`,见 1.1 |
|
|
295
|
+
| 元素一直透明、动画不播放 | `tailwind.config` 的 animation 未迁移 | 见 1.3 |
|
|
296
|
+
| 整页无样式、网格塌成单列 | 业务 JS / JSX 未完成转换,类名不在模板扫描范围中 | 按 4.3 转为 TS / TSX 并检查引用和样式 |
|
|
297
|
+
| 半透明背景变成实色 | `bg-opacity-*` 已移除 | 见 4.2 |
|
|
298
|
+
| `toast(...)` 无提示 | shadcn toaster 未挂载 | 见第 8 节 |
|
|
299
|
+
| `Property 'VITE_*' does not exist` | 环境变量写法 | 改为 `process.env.*` |
|
|
300
|
+
| `Unexpected token '<'` / JSON 404 | public 资源路径错位 | 改 import 或拼 `BASE_PATH` |
|
|
301
|
+
| `Parameter implicitly has an 'any' type` | 缺类型 | 补 TypeScript 类型 |
|
|
302
|
+
| 依赖的类型缺失 | 缺 `@types/*` | 安装对应 `@types/*`,不要关 strict |
|
|
303
|
+
|
|
304
|
+
## 验收
|
|
305
|
+
|
|
306
|
+
1. 按 SKILL.md Step 4 实际检查业务 JS / JSX 残留,完成 TS / TSX 转换,`npm run type:check` 通过。
|
|
307
|
+
2. 首页路由展示迁移后的内容,不再是模板 Welcome。
|
|
308
|
+
3. 主要交互可响应(数据可为演示数据)。
|
|
309
|
+
4. 样式与源项目基本一致,Tailwind 颜色生效;源项目的外链图片、背景图未被替换。
|
|
310
|
+
5. 源项目 `tailwind.config` 的自定义主题(含 `container`)、字体(字体族与字重)、Provider 均已迁移;`grep -rnE 'hsl\(var\(--|-opacity-[0-9]|-\[--' client/src` 无残留。
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Replit 平台差异
|
|
2
|
+
|
|
3
|
+
与 `react-migration.md` 一起使用:通用步骤按该文件执行,本文件只列 Replit 特有的部分。
|
|
4
|
+
|
|
5
|
+
## 环境差异
|
|
6
|
+
|
|
7
|
+
| 项 | Replit | 妙搭模板 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| React | 18 | 19 |
|
|
10
|
+
| Tailwind | 4 | 4 |
|
|
11
|
+
| 路由库 | wouter | react-router-dom |
|
|
12
|
+
| 后端 | `server/`(Express 等) | 不迁移 |
|
|
13
|
+
|
|
14
|
+
## 文件位置
|
|
15
|
+
|
|
16
|
+
| Replit 源路径 | 当前位置 | 处理 |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `client/src/components/**`、`pages/**`、`hooks/**`、`lib/**` | 原位 `client/src/**` | 直接可用(`hooks/use-mobile.tsx` 已排除,用模板的 `use-mobile.ts`) |
|
|
19
|
+
| `client/src/App.tsx` | `source_package/client/src/App.tsx` | 参考迁移路由与 Provider |
|
|
20
|
+
| `client/src/main.tsx` | `source_package/client/src/main.tsx` | 不需要 |
|
|
21
|
+
| `client/src/index.css` | `source_package/client/src/index.css` | 提取主题 |
|
|
22
|
+
| `shared/**` | 原位 `shared/**` | 直接可用 |
|
|
23
|
+
| `attached_assets/**` | `client/public/assets/**` | 引用路径需转换(见下) |
|
|
24
|
+
| `server/**` 及其他 | `source_package/` | 不迁移 |
|
|
25
|
+
|
|
26
|
+
## 特有步骤
|
|
27
|
+
|
|
28
|
+
1. **主题**:读取 `source_package/client/src/index.css`。Replit 已是 Tailwind 4,但常把颜色直接写在 `@theme inline` 里;改为先在 `client/src/tailwind-theme.css` 的 `:root` 定义 `--primary: hsl(...)` 等变量,再在 `@theme inline` 中 `--color-primary: var(--primary)` 映射。同名变量直接覆盖;自定义工具类(如 `.bg-grain`)放进 `@layer utilities`。
|
|
29
|
+
|
|
30
|
+
2. **wouter → react-router-dom**:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// 源:wouter
|
|
34
|
+
import { Switch, Route } from 'wouter';
|
|
35
|
+
<Switch>
|
|
36
|
+
<Route path="/" component={Home} />
|
|
37
|
+
<Route component={NotFound} />
|
|
38
|
+
</Switch>;
|
|
39
|
+
|
|
40
|
+
// 目标:在 client/src/app.tsx 的 RoutesComponent 中
|
|
41
|
+
<Route element={<Layout />}>
|
|
42
|
+
<Route index element={<Home />} />
|
|
43
|
+
</Route>
|
|
44
|
+
<Route path="*" element={<NotFound />} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`Switch` → `Routes`,`component={X}` → `element={<X />}`,兜底路由加 `path="*"`;组件内 `useLocation` / `useRoute` / `Link` 也换成 react-router-dom 的对应 API,删除所有 wouter import。
|
|
48
|
+
|
|
49
|
+
3. **Provider**:`source_package/client/src/App.tsx` 里的 Provider(`QueryClientProvider`、`TooltipProvider` 等)加到 `client/src/index.tsx` 的 `MainApp` 中,包在模板已有的 `BrowserRouter` / `AppContainer` 内层;模板只挂了 sonner 的 `Toaster`;源项目用 `@/components/ui/toaster`(shadcn toast)时要另外挂上,见 `react-migration.md` 第 8 节。`Layout.tsx` 只是 Outlet 容器,不放 Provider。
|
|
50
|
+
|
|
51
|
+
4. **`@assets` 别名**:模板没有该别名。图片已在 `client/public/assets/`,把 import 改为 public 路径并拼接 `CLIENT_BASE_PATH`:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// 源
|
|
55
|
+
import heroBg from '@assets/generated_images/hero.png';
|
|
56
|
+
<img src={heroBg} />
|
|
57
|
+
|
|
58
|
+
// 目标(删除 import)
|
|
59
|
+
<img src={`${process.env.CLIENT_BASE_PATH || ''}/assets/generated_images/hero.png`} />
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`@/`、`@shared/` 两个别名模板已配置,保持不变。
|
|
63
|
+
|
|
64
|
+
5. **字体**:Replit 常在 `index.html` 引入 Google Fonts,把对应 `<link>` 原样复制到 `client/index.html` 的 `<head>`,不要删减 URL 里的字重。再按源项目设置 `font-family`,见 `react-migration.md` 1.4。
|
|
65
|
+
|
|
66
|
+
6. **依赖**:`framer-motion`、`lucide-react`、`@tanstack/react-query`、`react-router-dom`、`recharts`、`sonner`、`zod` 等模板已内置(manifest 中为 `present`),沿用模板版本;`wouter` 不要安装。
|
|
67
|
+
|
|
68
|
+
## 常见编译错误
|
|
69
|
+
|
|
70
|
+
| 错误 | 修复 |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `Cannot find module 'wouter'` | 换成 react-router-dom |
|
|
73
|
+
| `Cannot find module '@assets/...'` | 改为 public 路径并拼 `CLIENT_BASE_PATH` |
|
|
74
|
+
| `Cannot find name 'Switch'` / `Property 'component' does not exist` | wouter 语法未转换,见第 2 步 |
|
|
@@ -26,6 +26,7 @@ gate-tools:
|
|
|
26
26
|
- **app_id 走环境变量**:所有 `+db-*` 都必填 `--app-id "$app_id"`;应用已存在、id 在环境变量 `app_id` 里,不要自行获取、解析或选择。
|
|
27
27
|
- **鉴权自动**:apps 域请求由运行环境自动打到 innerAPI 并注入鉴权头。**不要** `auth login` / `config init` / `--as`。
|
|
28
28
|
- **不要裸连数据库**:不要从环境变量里取连接串直连数据库;本地调试同样走 `+db-execute`。
|
|
29
|
+
- **首次用库前先确认已开通**:首次建表、写用库的后端代码、灌数据之前,先 `lark-cli apps +features --app-id "$app_id"`;`database` 为 `enabled=false` 时,先按 [lark-apps-ops](../lark-apps-ops/SKILL.md) 路由表「查询或开通应用数据库」一行开通,再继续。新应用会预建空库,`+db-table-list`、`+db-execute` 能成功**不代表已开通**——开关没开时运行时不注入数据库,预览和发布都会起不来。不要改平台环境文件、不要在代码里自行注册数据库模块或读连接串来绕过开关。
|
|
29
30
|
- **失败处理**:命令失败时把 `error.hint` 转述给用户,别原样甩 envelope JSON。
|
|
30
31
|
|
|
31
32
|
### 高风险写操作审批(exit 10)
|
|
@@ -43,6 +44,7 @@ gate-tools:
|
|
|
43
44
|
|
|
44
45
|
| 场景 | 首选命令 | 关键规则 |
|
|
45
46
|
|---|---|---|
|
|
47
|
+
| 首次用库前确认已开通 | `lark-cli apps +features --app-id "$app_id"` | `database` 为 `enabled=false` 时先 `+feature-enable --feature database --app-id "$app_id"`(见 [lark-apps-ops](../lark-apps-ops/SKILL.md)),再建表、写代码 |
|
|
46
48
|
| 列表 / 查表结构 | `lark-cli apps +db-table-list --app-id "$app_id"` / `+db-table-get --app-id "$app_id" --table <table>` | 禁止用 `information_schema` / `pg_indexes` 模拟常规结构查询 |
|
|
47
49
|
| 执行 DDL / DML / SELECT | `+db-execute --app-id "$app_id" --sql "<query>" --yes` | 不自动包事务,原子性自己写 `BEGIN…COMMIT` |
|
|
48
50
|
| 批量导入 CSV / JSON | `+db-data-import --app-id "$app_id" --file ./<file> --table <table> --yes` | 目标表已存在;表头 / key 与列名完全一致;≤1 MB / ≤5000 行;无 upsert |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lark-apps-ops
|
|
3
|
-
description: "Use when 在妙搭沙箱里用 `lark-cli apps +<cmd>` 操作【当前这个已存在的】妙搭应用:本地开发与部署发布上线(+release-*)、导出应用源码为 zip(+export)、环境变量管理(+env-*)、线上日志/Trace/监控指标/PV-UV 查询(+log-*/+trace-*/+metric-list/+analytics-list)、运行时可见范围(+access-scope-*)、应用协作者与协作权限设置(+member-*)、AI 与飞书平台能力插件安装/卸载(+plugin-*)、改应用名或描述(+update)、开放 API Key 管理(+openapi-key-*)、应用 MCP 连接与凭证(+mcp-get/+mcp-key-create)、运行时缓存调试(+cache-get/+cache-delete/+cache-clear)、妙搭 user_id 与飞书 open_id/union_id/user_id 互转(+user-id-convert
|
|
3
|
+
description: "Use when 在妙搭沙箱里用 `lark-cli apps +<cmd>` 操作【当前这个已存在的】妙搭应用:本地开发与部署发布上线(+release-*)、导出应用源码为 zip(+export)、环境变量管理(+env-*)、线上日志/Trace/监控指标/PV-UV 查询(+log-*/+trace-*/+metric-list/+analytics-list)、运行时可见范围(+access-scope-*)、应用协作者与协作权限设置(+member-*)、AI 与飞书平台能力插件安装/卸载(+plugin-*)、改应用名或描述(+update)、开放 API Key 管理(+openapi-key-*)、应用 MCP 连接与凭证(+mcp-get/+mcp-key-create)、运行时缓存调试(+cache-get/+cache-delete/+cache-clear)、妙搭 user_id 与飞书 open_id/union_id/user_id 互转(+user-id-convert)、查询与开通应用数据库能力(+features/+feature-enable,首次建表或写用库代码前必查);本 skill 也是 apps 命令族的意图路由入口——应用数据库走 lark-apps-db、应用文件存储走 lark-apps-file、角色与权限走 lark-apps-authz。触发词:部署, 上线, 发布, 导出源码, 源码快照, 下载源码, zip, 环境变量, 线上日志, 接口请求量, 错误量, 延迟, CPU, 内存, PV, UV, 访问量, 可见范围, 分享链接, 协作者, 开发权限, 谁能改这个应用, 外部协作, 插件, 改名, API Key, MCP, MCP URL, MCP 凭证, 缓存, cache, 清缓存, 缓存没更新, ID 转换, open_id 转 user_id, 开通数据库, 开启数据库, 数据库未开通, 要存数据, DRIZZLE_DATABASE, +features, +feature-enable, +release-, +export, +env-, +access-scope-, +member-, +plugin-, +openapi-key-, +mcp-, +cache-, +user-id-convert. NOT for 新建应用/应用列表/初始化/HTML 发布/会话/自动化等未开放命令(见「能力边界」),以及非 apps 域的飞书操作(走 lark-cli skill)。"
|
|
4
4
|
metadata:
|
|
5
5
|
requires:
|
|
6
6
|
bins: ["lark-cli"]
|
|
@@ -39,7 +39,8 @@ control-by-feature-ab: true
|
|
|
39
39
|
| 管理应用环境变量(查看/设置/删除) | `+env-list`、`+env-set`、`+env-delete` | [env](references/lark-apps-env.md) |
|
|
40
40
|
| 导出应用源码为 zip(要一份源码快照:读代码/审计/归档/静态分析;或取**别人分享给你的**创意应用源码,你对其仓库无权限)。仅取快照、不继续开发 | `+export`(下载 zip,只读;`--app-id` 与 `--meta-token` 恰传其一) | [export](references/lark-apps-export.md) |
|
|
41
41
|
| 查线上日志、Trace、请求数、错误率、延迟、CPU、memory、PV/UV/访问量 | `+log-list`、`+log-get`、`+trace-list`、`+trace-get`、`+metric-list`、`+analytics-list` | [observability](references/lark-apps-observability.md) |
|
|
42
|
-
|
|
|
42
|
+
| **查询或开通应用数据库**:要保存业务数据、**首次建表 / 写用库的后端代码 / 灌数据之前**必须先查;后端报 `DRIZZLE_DATABASE` 注入失败时也先查这里。库能查能建表不代表已开通,不要改环境文件或自行接连接串绕过开关 | `+features --app-id "$app_id"`;`database` 为 `enabled=false` 时 `+feature-enable --feature database --app-id "$app_id"` | [features](references/lark-apps-features.md) |
|
|
43
|
+
| 应用数据库:看表/改表、执行 SQL、导入导出、多环境发布、审计、时间点恢复、DB 用量(首次用库前先按上一行确认已开通) | `+db-*` 命令族 | **→ [lark-apps-db](../lark-apps-db/SKILL.md)** |
|
|
43
44
|
| 应用文件存储:上传/下载/列出/删除文件、临时分享链接、存储用量 | `+file-*` 命令族 | **→ [lark-apps-file](../lark-apps-file/SKILL.md)** |
|
|
44
45
|
| 角色与权限:角色/角色成员管理、查用户命中角色、权限点位表维护 | `+role-*` 命令族 | **→ [lark-apps-authz](../lark-apps-authz/SKILL.md)** |
|
|
45
46
|
| 设置或查看运行时可见范围(谁能打开应用) | `+access-scope-set`、`+access-scope-get` | [access-scope-set](references/lark-apps-access-scope-set.md)、[access-scope-get](references/lark-apps-access-scope-get.md) |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# apps +features / +feature-enable
|
|
2
|
+
|
|
3
|
+
查询和启用应用业务能力。本期只有 `database`(应用的数据库,PostgreSQL)。运行时命令事实以 `lark-cli apps +features --help`、`lark-cli apps +feature-enable --help` 为准。
|
|
4
|
+
|
|
5
|
+
## 心智模型
|
|
6
|
+
|
|
7
|
+
- `full_stack` 是工程与发布契约,**不等于已有数据库**。应用可以先没有数据库,需要保存业务数据时再显式启用。
|
|
8
|
+
- `+features` 只读(`spark:app:read`):每个能力只返回 `name` 和 `enabled`。不申请资源、不返回连接串。
|
|
9
|
+
- `+feature-enable --feature database` 是服务端写操作(声明 `spark:app:read` + `spark:app:write`):只需 `--app-id` 和 `--feature database`(本期只支持 `database`),CLI 自动生成 `idempotent_key` 并只提交它。成功只表示**服务端能力已开启**;在妙搭沙箱里由平台自动生效,之后要再做一次真实读写验证。
|
|
10
|
+
- 它不改业务代码、不拆已有数据库、不推代码、不发布。
|
|
11
|
+
|
|
12
|
+
## 何时用
|
|
13
|
+
|
|
14
|
+
- 应用要开始保存业务数据:先 `+features`,`database` 为 `enabled=false` 时执行 `+feature-enable --feature database`。
|
|
15
|
+
- `+features` 没列出 `database`、命令报错,或 `+feature-enable` 被服务端拒绝:该应用不走本入口,转述错误信息;需要数据库时引导用户在妙搭 web 端处理(不要替用户拼站点地址)。
|
|
16
|
+
|
|
17
|
+
## 示例
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
lark-cli apps +features --app-id "$app_id"
|
|
21
|
+
|
|
22
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id" --dry-run
|
|
23
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id"
|
|
24
|
+
|
|
25
|
+
# 上一次失败/超时后,用错误提示里的原 idempotent_key(示例值需替换)续同一次请求或查询
|
|
26
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id" --idempotent-key b3d93913-e777-4968-a00c-afd0a1c7ce44
|
|
27
|
+
lark-cli apps +features --app-id "$app_id" --operation-ref b3d93913-e777-4968-a00c-afd0a1c7ce44
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 结果判定
|
|
31
|
+
|
|
32
|
+
| 结果 | 含义 | 下一步 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `ok:true`,`feature.enabled=true` | 服务端已开启数据库(含早已开启、无 `operation` 的情况) | 妙搭沙箱里平台自动生效,稍候即可;之后验证一次真实读写 |
|
|
35
|
+
| exit 1,`subtype=failed_precondition`,提示应用不是新契约应用 | 该应用不能通过 CLI 开数据库 | 转述错误,引导用户在妙搭 web 端处理(不要替用户拼站点地址);重试本命令没有用 |
|
|
36
|
+
| exit 1,`subtype=failed_precondition`,提示 `idempotent_key` 已不可用 | 该 key 已作废:被后续操作取代、不属于当前调用者或不存在 | 先 `+features` 看当前状态,已开启就不用再开;仍需开启就不带 `--idempotent-key` 重新执行(CLI 生成新 key);不要用同一个 key 重试 |
|
|
37
|
+
| exit 1,`subtype=conflict` | 能力被并发修改、key 属于其他请求,或另一次开启仍在进行 | 转述 `error.message` / `hint`;重新 `+features` 看当前状态再决定,CLI 不会自动覆盖 |
|
|
38
|
+
| exit 1,`subtype=rate_limit` | 被限流,本次请求未执行。首次请求被限流,或服务端已确认这个 key 没有操作时,没有可续跑的东西;网络重放或带原 key 续跑时被限流,这个 key 可能已有操作 | 稍等约一分钟后按 `hint` 处理:`hint` 要求不带 `--idempotent-key` 重新执行时就换新 key;`hint` 给出原 key 时,先用 `--operation-ref` 查询,再用原 `--idempotent-key` 续跑,不要换新 key |
|
|
39
|
+
| exit 1,`subtype=feature_not_available`,提示服务端没有运行应用能力接口 | 当前服务端还不支持应用能力:网关内部错误 2200,或网关还没有这条路由、直接回 HTTP 404(`404 page not found`),只读复查结果相同;只有开通请求被回 404 时也按这一行处理 | 不要重试;转述错误,引导用户在妙搭 web 端处理,或等服务端升级 |
|
|
40
|
+
| exit 3,身份或授权失效(如 `token_expired`;妙搭沙箱里用户凭证过期返回 221801,凭证无效时代理直接回 HTTP 401) | 当前用户凭证不可用,任何 key 都不会成功 | 转述 `hint`,本沙箱不需要也不要自行登录或初始化;不要用同一个 `--idempotent-key` 反复重试 |
|
|
41
|
+
| exit 1 / 4 / 5,结果失败、未知或无法核实 | 请求可能已执行,也可能没有 | 按 `hint` 先用 `--operation-ref <idempotent_key>` 查询;要续同一次请求用原 `--idempotent-key`;结果未知时不要换新 key 重新申请 |
|
|
42
|
+
|
|
43
|
+
带 `--idempotent-key` 时,CLI 先按同一 key 查询服务端保留的操作:已成功、结果未知或冲突直接报告,进行中则等待,只有可重试的失败才用同一 key 重发;只有服务端明确答复不保留该 key(`operation_unavailable`)时,才报错并提示先看 `+features`、确有需要再发起新操作;查询本身失败(权限、服务端错误、网络)时保留实际错误,结果按未知处理,只能用同一 key 重试或查询,不要换新 key。两种情况都不会自动重发或换 key。
|
|
44
|
+
|
|
45
|
+
`running` 只在服务端确实异步接手时出现,CLI 最多等待 60 秒;`conflicted` 等终态立即返回,不会继续等待。超时或中断不会取消或回滚服务端操作。错误的 `hint` 会给出原 `idempotent_key` 与查询命令,不含连接信息。
|
|
46
|
+
|
|
47
|
+
## 参考
|
|
48
|
+
|
|
49
|
+
- [lark-apps-local-dev](lark-apps-local-dev.md) — 本地开发与部署流程
|
|
50
|
+
- [lark-apps-db](../../lark-apps-db/SKILL.md) — 数据库命令与多环境
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lark-apps 本地开发
|
|
2
2
|
|
|
3
|
-
适用:当前妙搭应用(full_stack 全栈或 frontend 纯前端)的源码已在工作区,用本地 code agent/IDE
|
|
3
|
+
适用:当前妙搭应用(full_stack 全栈或 frontend 纯前端)的源码已在工作区,用本地 code agent/IDE 开发、调试数据库(只适用于全栈应用,且 `+features` 里 `database` 为 `enabled=true`;frontend 应用没有数据库),再部署上线。
|
|
4
4
|
|
|
5
5
|
## 改完代码后部署上线
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- pull/push/diff/log 都用原生 git;云端 `sprint/default` 比本地新时,先 `git pull --rebase origin sprint/default`,解决冲突后再 push 和 publish。
|
|
21
21
|
- 环境变量由脚手架在本地启动时处理。
|
|
22
22
|
- frontend(纯前端,vite-react)应用的开发与部署流程和全栈一致,只是没有数据库、跳过一切 `+db-*` 步骤。后续要加数据库/后端能力时,本地链路不提供类型升级:引导用户到妙搭 web 的云端会话里用自然语言提出(如「给这个应用加登录和数据存储」)即可触发升级,无需特殊指令,也不要替用户拼站点地址。
|
|
23
|
+
- 全栈应用的数据库默认可能未开启。**首次建表、写用库代码之前**:`+features --app-id "$app_id"` 看 `database` → `enabled=false` 时 `+feature-enable --feature database --app-id "$app_id"`(见 [features](lark-apps-features.md))→ 平台自动生效,不需要 `+env-pull`、不需要手动重启 → 建表并做一次真实读写验证。库能查能建表不代表已开通(以 `+features` 为准),不要改环境文件或自行接连接串绕过开关。
|
|
23
24
|
- DB 调试用 `+db-table-list` / `+db-table-get` / `+db-execute`(见 lark-apps-db skill);不要裸连数据库或自行拼连接串。
|
|
24
25
|
- DB 分 `dev` / `online`;日常调试优先 `--environment dev`。dev 的库结构变更要上线时,仍按应用发布链路走 `+release-create`,不要另造“数据库发布”步骤。
|
|
25
26
|
- 存量单库应用需要 dev/online 多环境时,用 `+db-env-create --environment dev`。这是不可逆 high-risk 操作。
|
|
@@ -44,11 +44,14 @@ Task({
|
|
|
44
44
|
|
|
45
45
|
{% endif %}本轮跑哪些用例只认结构化字段,写在 `prompt` 里的自然语言范围不生效:刚写好或刚改过的计划首次验收带 `agent_options.retest_all: true`,跑全部用例;修复后复验带 `agent_options.retest_case_ids: [1, 2]`,只列失败或受这次修复影响的用例(编号取计划文件里的 `id`,必须是正整数,不是 `"case-1"` 这类字符串)。
|
|
46
46
|
|
|
47
|
+
复验前必须重新读取磁盘上的计划文件:如果失败暴露的是用例本身有误(前置条件缺失、持久化数据重名、步骤或断言与当前产品不符),先编辑原计划并读回确认,再派发;**禁止只在 `prompt` 里写“已存在就不要新增”等补丁来覆盖错误计划**。计划改过后,本轮选择的每条 Case 必须能从当前应用状态独立完成;有依赖就把准备动作写进该 Case,或把依赖 Case 的完整闭包一并放进 `retest_case_ids`。
|
|
48
|
+
|
|
47
49
|
## 验收要求
|
|
48
50
|
|
|
49
51
|
- 检查视觉的用例写清可观察结果,例如「首页标题完整可见、卡片两列排列、主图不是裂图」,不要只写「看起来正常」。
|
|
50
52
|
- 功能验收必须走到终态:提交后记录出现、跳转后内容正确、删除后记录消失或 AI 输出出现;不要停在弹窗、按钮或表单字段出现。
|
|
51
53
|
- 写操作和资源加载要核对网络结果;非 2xx、空结果、错误兜底或白屏都应报失败。
|
|
54
|
+
- 涉及 localStorage、数据库或其他跨轮持久化状态的写操作必须可重复执行:使用本轮唯一数据,或在 Case 内先查找并复用 / 重置 / 清理同名数据;不能固定创建同名记录后把“已存在时跳过”只写在 `prompt` 里。
|
|
52
55
|
- 派发前备齐每条用例依赖的数据、应用账号和角色,一次派发就测完,不靠跳过后再派:数据先经应用界面新建或调用应用接口准备,且对该用例的测试身份可见;应用自建账号体系的,把测试账号写进用例;{% if permissionIdentityEnabled %}需要特定角色或登录状态的,按上文给用例写 identity{% else %}需要特定角色的,先确认当前登录身份具备该角色{% endif %}。准备不了(或会违反数据相关规则)的用例不派(不写进计划,或不列入本轮 `retest_case_ids`)。
|
|
53
56
|
- {% if permissionIdentityEnabled %}不测登录流程本身(登录页、注册、OAuth、CAPTCHA),也不测会造成真实外部副作用的行为;需要以某角色或未登录状态验证的用例用 identity 声明。{% else %}不测试登录、认证、CAPTCHA,或会造成真实外部副作用的行为。{% endif %}
|
|
54
57
|
{% else %}
|
|
@@ -108,6 +111,7 @@ Task({
|
|
|
108
111
|
1. **先看已有的**:`glob .spark/e2e-test/*.json` 和历史目录 `.spark/e2e-test/standard/*.json` → **读**可能覆盖本需求的那份(没读过不要传)
|
|
109
112
|
2. **改还是新建**:已有计划沿用原路径(包括 standard/ 下的,不复制、不搬迁),同需求用例有变化 → 编辑那份;没有对应计划的新功能 / 页面 → 新建 `.spark/e2e-test/<语义名>.json`
|
|
110
113
|
3. **派发**:跑哪些用例只认结构化字段,写在 `prompt` 里的自然语言范围不生效:刚写好或刚改过的卷首次验收带 `agent_options.retest_all: true`,跑全部用例;修复后复验带 `agent_options.retest_case_ids: [1, 2]`,只列失败或受这次修复影响的用例(编号取计划文件里的 `id`,必须是正整数,不是 `"case-1"` 这类字符串)
|
|
114
|
+
4. **复验前修计划**:重新读取磁盘上的计划;若失败来自前置条件缺失、持久化数据重名、步骤或断言错误,先编辑原计划并读回确认,再派发。禁止只改 `prompt` 来覆盖计划语义
|
|
111
115
|
|
|
112
116
|
格式(一条用例一行,优先级小写,每卷 ≤10 条):
|
|
113
117
|
|
|
@@ -122,6 +126,8 @@ Task({
|
|
|
122
126
|
|
|
123
127
|
{% endif %}- **用例正文只写在计划文件里**:本模式下 `prompt` 不写 `## 测试用例`(下面示例里的用例正文改为写进卷),只留应用结构与本次范围——两处都写会让计划外用例一条不跑、报告却全绿
|
|
124
128
|
- **复测只选编号、不删用例**:「只传 failed + 未完成的 Case」指用 `retest_case_ids` 列出这些编号,不是从卷里删掉已通过的用例
|
|
129
|
+
- **依赖必须闭包**:本轮选择的每条 Case 要能从当前应用状态独立执行;依赖其他 Case 造数时,把准备动作并入本 Case,或把全部依赖 Case 编号一并加入 `retest_case_ids`,不能只选下游 Case 后靠历史轮次遗留状态
|
|
130
|
+
- **持久化写操作要幂等**:localStorage / 数据库中的记录会跨轮保留。创建类 Case 使用本轮唯一数据,或在 Case 内先查找并复用 / 重置 / 清理同名数据;禁止固定创建同名记录,再在 `prompt` 里补一句“已存在就不要新增”
|
|
125
131
|
|
|
126
132
|
### 调用结构
|
|
127
133
|
|
|
@@ -137,30 +143,28 @@ task({
|
|
|
137
143
|
- <页面名> (<路由>) — <布局/职责>
|
|
138
144
|
- ...
|
|
139
145
|
|
|
140
|
-
##
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
2. 断言:<可观察现象>
|
|
144
|
-
...
|
|
146
|
+
## 本次范围
|
|
147
|
+
- 执行计划文件中 agent_options 选中的 Case 编号
|
|
148
|
+
- 不新增、改写或跳过计划内步骤
|
|
145
149
|
`,
|
|
146
150
|
// ...其他参数:按 Task 工具的实际签名如实补全,别只填上面示例里的字段
|
|
147
151
|
})
|
|
148
152
|
```
|
|
149
153
|
|
|
150
|
-
### prompt
|
|
154
|
+
### prompt 两段式
|
|
151
155
|
|
|
152
156
|
| 段落 | 内容 | 作用 |
|
|
153
157
|
|------|------|------|
|
|
154
158
|
| 概述 | 一句话说明被测应用是什么 | 让 agent 建立全局认知 |
|
|
155
159
|
| `## 应用结构` | 每个页面的路由 + 布局/职责 | 告诉 agent 去哪、看什么 |
|
|
156
|
-
| `## 测试用例` | 多个 Case,每个含编号步骤 | agent 逐条执行并断言 |
|
|
157
160
|
|
|
158
161
|
### Case 编写规则
|
|
159
162
|
|
|
160
163
|
- **标题**:`### Case N: <模块> - <场景>`,一个 Case 一个独立场景
|
|
161
164
|
- **动作步 / 断言步交替**:动作步动词开头(访问 / 点击 / 输入 / 切换);断言步以「断言:」开头
|
|
162
165
|
- **可定位 + 二元判定**:动作指到具体对象(可见文本,如「发布新帖」按钮);断言给非黑即白判据(出现 / 消失 / 等于 / ≥N / 含某文案),不写"正常""是否合理 / 美观"(见下方对照)
|
|
163
|
-
-
|
|
166
|
+
- **独立可重入**:每条 Case 都从明确前置状态开始;有依赖就内置准备动作或在复测选择中包含依赖闭包。删除 / 提交等副作用操作验证后还原,不污染后续 Case
|
|
167
|
+
- **持久化写操作幂等**:localStorage / 数据库记录会跨轮保留;创建类 Case 使用本轮唯一数据,或先查找并复用 / 重置 / 清理同名数据,重复执行不得因固定名称冲突而失败
|
|
164
168
|
- **派发前备齐前置条件**:用例依赖的数据、应用账号和角色在派发前就绪,一次派发就测完,不靠跳过后再派——数据先经应用界面新建或调用应用接口准备,且对该用例的测试身份可见;应用自建账号体系的,把测试账号写进 Case;{% if permissionIdentityEnabled %}需要特定角色或登录状态的,按上文给用例写 identity{% else %}需要特定角色的,先确认当前登录身份具备该角色{% endif %}。准备不了(或会违反数据相关规则)就不写这条 Case
|
|
165
169
|
|
|
166
170
|
### Case 复杂度约束(必跑到终态)
|
|
@@ -245,7 +249,7 @@ E2E agent 只在离散时刻抓快照 / 截图,**看不到瞬时过程、不
|
|
|
245
249
|
|
|
246
250
|
E2E 派遣是**昂贵操作**(每次消耗大量 token + 时间)。E2E agent 只负责暴露问题,**不负责定位根因**。
|
|
247
251
|
|
|
248
|
-
|
|
252
|
+
**每个用户任务**的 E2E 总派遣上限是绝对 **3 轮**。每次 `Task(subagent_type="E2E")` 调用都计 1 轮;修改计划、修改代码、超时、重启 agent,以及切换 `retest_all` / `retest_case_ids` 都不会重置计数。禁止把同一用户任务拆成多个描述或子任务规避上限:
|
|
249
253
|
|
|
250
254
|
| 轮次 | testRequirements 内容 | 说明 |
|
|
251
255
|
|------|----------------------|------|
|
|
@@ -255,6 +259,8 @@ E2E 派遣是**昂贵操作**(每次消耗大量 token + 时间)。E2E agent
|
|
|
255
259
|
| 第 4 轮起 | 禁止再派遣 E2E | 转为根因分析模式 |
|
|
256
260
|
|
|
257
261
|
**定向复测规则**:
|
|
262
|
+
- 派发前重新读取计划;若失败原因是计划缺陷,必须先修改原计划并读回确认,再选择 Case。`prompt` 不能覆盖计划中的步骤、断言或数据语义
|
|
263
|
+
- 每个被选 Case 必须独立可执行,或连同它依赖的全部 Case 一起选择;不要假设上轮一定留下了可用状态
|
|
258
264
|
- 已通过的 Case 最多复测 1 次:担心修复引入回归时可纳入复测,同一 Case 累计通过 2 次后不再纳入
|
|
259
265
|
- 如果上一轮因超时中断,E2E 会返回"已测通过/已测失败/未测"三组——已测通过的结果可信不需重测,只测"失败"和"未测"的
|
|
260
266
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lark-apps-ops
|
|
3
|
-
description: "Use when 在妙搭沙箱里用 `lark-cli apps +<cmd>` 操作【当前这个已存在的】妙搭应用:本地开发与部署发布上线(+release-*)、导出应用源码为 zip(+export)、环境变量管理(+env-*)、线上日志/Trace/监控指标/PV-UV 查询(+log-*/+trace-*/+metric-list/+analytics-list)、运行时可见范围(+access-scope-*)、应用协作者与协作权限设置(+member-*)、AI 与飞书平台能力插件安装/卸载(+plugin-*)、改应用名或描述(+update)、开放 API Key 管理(+openapi-key-*)、应用 MCP 连接与凭证(+mcp-get/+mcp-key-create)、运行时缓存调试(+cache-get/+cache-delete/+cache-clear)、妙搭 user_id 与飞书 open_id/union_id/user_id 互转(+user-id-convert
|
|
3
|
+
description: "Use when 在妙搭沙箱里用 `lark-cli apps +<cmd>` 操作【当前这个已存在的】妙搭应用:本地开发与部署发布上线(+release-*)、导出应用源码为 zip(+export)、环境变量管理(+env-*)、线上日志/Trace/监控指标/PV-UV 查询(+log-*/+trace-*/+metric-list/+analytics-list)、运行时可见范围(+access-scope-*)、应用协作者与协作权限设置(+member-*)、AI 与飞书平台能力插件安装/卸载(+plugin-*)、改应用名或描述(+update)、开放 API Key 管理(+openapi-key-*)、应用 MCP 连接与凭证(+mcp-get/+mcp-key-create)、运行时缓存调试(+cache-get/+cache-delete/+cache-clear)、妙搭 user_id 与飞书 open_id/union_id/user_id 互转(+user-id-convert)、查询与开通应用数据库能力(+features/+feature-enable,首次建表或写用库代码前必查);本 skill 也是 apps 命令族的意图路由入口——应用数据库走 lark-apps-db、应用文件存储走 lark-apps-file、角色与权限走 lark-apps-authz。触发词:部署, 上线, 发布, 导出源码, 源码快照, 下载源码, zip, 环境变量, 线上日志, 接口请求量, 错误量, 延迟, CPU, 内存, PV, UV, 访问量, 可见范围, 分享链接, 协作者, 开发权限, 谁能改这个应用, 外部协作, 插件, 改名, API Key, MCP, MCP URL, MCP 凭证, 缓存, cache, 清缓存, 缓存没更新, ID 转换, open_id 转 user_id, 开通数据库, 开启数据库, 数据库未开通, 要存数据, DRIZZLE_DATABASE, +features, +feature-enable, +release-, +export, +env-, +access-scope-, +member-, +plugin-, +openapi-key-, +mcp-, +cache-, +user-id-convert. NOT for 新建应用/应用列表/初始化/HTML 发布/会话/自动化等未开放命令(见「能力边界」),以及非 apps 域的飞书操作(走 lark-cli skill)。"
|
|
4
4
|
metadata:
|
|
5
5
|
requires:
|
|
6
6
|
bins: ["lark-cli"]
|
|
@@ -39,7 +39,8 @@ control-by-feature-ab: true
|
|
|
39
39
|
| 管理应用环境变量(查看/设置/删除) | `+env-list`、`+env-set`、`+env-delete` | [env](references/lark-apps-env.md) |
|
|
40
40
|
| 导出应用源码为 zip(要一份源码快照:读代码/审计/归档/静态分析;或取**别人分享给你的**创意应用源码,你对其仓库无权限)。仅取快照、不继续开发 | `+export`(下载 zip,只读;`--app-id` 与 `--meta-token` 恰传其一) | [export](references/lark-apps-export.md) |
|
|
41
41
|
| 查线上日志、Trace、请求数、错误率、延迟、CPU、memory、PV/UV/访问量 | `+log-list`、`+log-get`、`+trace-list`、`+trace-get`、`+metric-list`、`+analytics-list` | [observability](references/lark-apps-observability.md) |
|
|
42
|
-
|
|
|
42
|
+
| **查询或开通应用数据库**:要保存业务数据、**首次建表 / 写用库的后端代码 / 灌数据之前**必须先查;后端报 `DRIZZLE_DATABASE` 注入失败时也先查这里。库能查能建表不代表已开通,不要改环境文件或自行接连接串绕过开关 | `+features --app-id "$app_id"`;`database` 为 `enabled=false` 时 `+feature-enable --feature database --app-id "$app_id"` | [features](references/lark-apps-features.md) |
|
|
43
|
+
| 应用数据库:看表/改表、执行 SQL、导入导出、多环境发布、审计、时间点恢复、DB 用量(首次用库前先按上一行确认已开通) | `+db-*` 命令族 | **→ [lark-apps-db](../lark-apps-db/SKILL.md)** |
|
|
43
44
|
| 应用文件存储:上传/下载/列出/删除文件、临时分享链接、存储用量 | `+file-*` 命令族 | **→ [lark-apps-file](../lark-apps-file/SKILL.md)** |
|
|
44
45
|
| 角色与权限:角色/角色成员管理、查用户命中角色、权限点位表维护 | `+role-*` 命令族 | **→ [lark-apps-authz](../lark-apps-authz/SKILL.md)** |
|
|
45
46
|
| 设置或查看运行时可见范围(谁能打开应用) | `+access-scope-set`、`+access-scope-get` | [access-scope-set](references/lark-apps-access-scope-set.md)、[access-scope-get](references/lark-apps-access-scope-get.md) |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# apps +features / +feature-enable
|
|
2
|
+
|
|
3
|
+
查询和启用应用业务能力。本期只有 `database`(应用的数据库,PostgreSQL)。运行时命令事实以 `lark-cli apps +features --help`、`lark-cli apps +feature-enable --help` 为准。
|
|
4
|
+
|
|
5
|
+
## 心智模型
|
|
6
|
+
|
|
7
|
+
- `full_stack` 是工程与发布契约,**不等于已有数据库**。应用可以先没有数据库,需要保存业务数据时再显式启用。
|
|
8
|
+
- `+features` 只读(`spark:app:read`):每个能力只返回 `name` 和 `enabled`。不申请资源、不返回连接串。
|
|
9
|
+
- `+feature-enable --feature database` 是服务端写操作(声明 `spark:app:read` + `spark:app:write`):只需 `--app-id` 和 `--feature database`(本期只支持 `database`),CLI 自动生成 `idempotent_key` 并只提交它。成功只表示**服务端能力已开启**;在妙搭沙箱里由平台自动生效,之后要再做一次真实读写验证。
|
|
10
|
+
- 它不改业务代码、不拆已有数据库、不推代码、不发布。
|
|
11
|
+
|
|
12
|
+
## 何时用
|
|
13
|
+
|
|
14
|
+
- 应用要开始保存业务数据:先 `+features`,`database` 为 `enabled=false` 时执行 `+feature-enable --feature database`。
|
|
15
|
+
- `+features` 没列出 `database`、命令报错,或 `+feature-enable` 被服务端拒绝:该应用不走本入口,转述错误信息;需要数据库时引导用户在妙搭 web 端处理(不要替用户拼站点地址)。
|
|
16
|
+
|
|
17
|
+
## 示例
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
lark-cli apps +features --app-id "$app_id"
|
|
21
|
+
|
|
22
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id" --dry-run
|
|
23
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id"
|
|
24
|
+
|
|
25
|
+
# 上一次失败/超时后,用错误提示里的原 idempotent_key(示例值需替换)续同一次请求或查询
|
|
26
|
+
lark-cli apps +feature-enable --feature database --app-id "$app_id" --idempotent-key b3d93913-e777-4968-a00c-afd0a1c7ce44
|
|
27
|
+
lark-cli apps +features --app-id "$app_id" --operation-ref b3d93913-e777-4968-a00c-afd0a1c7ce44
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 结果判定
|
|
31
|
+
|
|
32
|
+
| 结果 | 含义 | 下一步 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `ok:true`,`feature.enabled=true` | 服务端已开启数据库(含早已开启、无 `operation` 的情况) | 妙搭沙箱里平台自动生效,稍候即可;之后验证一次真实读写 |
|
|
35
|
+
| exit 1,`subtype=failed_precondition`,提示应用不是新契约应用 | 该应用不能通过 CLI 开数据库 | 转述错误,引导用户在妙搭 web 端处理(不要替用户拼站点地址);重试本命令没有用 |
|
|
36
|
+
| exit 1,`subtype=failed_precondition`,提示 `idempotent_key` 已不可用 | 该 key 已作废:被后续操作取代、不属于当前调用者或不存在 | 先 `+features` 看当前状态,已开启就不用再开;仍需开启就不带 `--idempotent-key` 重新执行(CLI 生成新 key);不要用同一个 key 重试 |
|
|
37
|
+
| exit 1,`subtype=conflict` | 能力被并发修改、key 属于其他请求,或另一次开启仍在进行 | 转述 `error.message` / `hint`;重新 `+features` 看当前状态再决定,CLI 不会自动覆盖 |
|
|
38
|
+
| exit 1,`subtype=rate_limit` | 被限流,本次请求未执行。首次请求被限流,或服务端已确认这个 key 没有操作时,没有可续跑的东西;网络重放或带原 key 续跑时被限流,这个 key 可能已有操作 | 稍等约一分钟后按 `hint` 处理:`hint` 要求不带 `--idempotent-key` 重新执行时就换新 key;`hint` 给出原 key 时,先用 `--operation-ref` 查询,再用原 `--idempotent-key` 续跑,不要换新 key |
|
|
39
|
+
| exit 1,`subtype=feature_not_available`,提示服务端没有运行应用能力接口 | 当前服务端还不支持应用能力:网关内部错误 2200,或网关还没有这条路由、直接回 HTTP 404(`404 page not found`),只读复查结果相同;只有开通请求被回 404 时也按这一行处理 | 不要重试;转述错误,引导用户在妙搭 web 端处理,或等服务端升级 |
|
|
40
|
+
| exit 3,身份或授权失效(如 `token_expired`;妙搭沙箱里用户凭证过期返回 221801,凭证无效时代理直接回 HTTP 401) | 当前用户凭证不可用,任何 key 都不会成功 | 转述 `hint`,本沙箱不需要也不要自行登录或初始化;不要用同一个 `--idempotent-key` 反复重试 |
|
|
41
|
+
| exit 1 / 4 / 5,结果失败、未知或无法核实 | 请求可能已执行,也可能没有 | 按 `hint` 先用 `--operation-ref <idempotent_key>` 查询;要续同一次请求用原 `--idempotent-key`;结果未知时不要换新 key 重新申请 |
|
|
42
|
+
|
|
43
|
+
带 `--idempotent-key` 时,CLI 先按同一 key 查询服务端保留的操作:已成功、结果未知或冲突直接报告,进行中则等待,只有可重试的失败才用同一 key 重发;只有服务端明确答复不保留该 key(`operation_unavailable`)时,才报错并提示先看 `+features`、确有需要再发起新操作;查询本身失败(权限、服务端错误、网络)时保留实际错误,结果按未知处理,只能用同一 key 重试或查询,不要换新 key。两种情况都不会自动重发或换 key。
|
|
44
|
+
|
|
45
|
+
`running` 只在服务端确实异步接手时出现,CLI 最多等待 60 秒;`conflicted` 等终态立即返回,不会继续等待。超时或中断不会取消或回滚服务端操作。错误的 `hint` 会给出原 `idempotent_key` 与查询命令,不含连接信息。
|
|
46
|
+
|
|
47
|
+
## 参考
|
|
48
|
+
|
|
49
|
+
- [lark-apps-local-dev](lark-apps-local-dev.md) — 本地开发与部署流程
|
|
50
|
+
- [lark-apps-db](../../lark-apps-db/SKILL.md) — 数据库命令与多环境
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lark-apps 本地开发
|
|
2
2
|
|
|
3
|
-
适用:当前妙搭应用(full_stack 全栈或 frontend 纯前端)的源码已在工作区,用本地 code agent/IDE
|
|
3
|
+
适用:当前妙搭应用(full_stack 全栈或 frontend 纯前端)的源码已在工作区,用本地 code agent/IDE 开发、调试数据库(只适用于全栈应用,且 `+features` 里 `database` 为 `enabled=true`;frontend 应用没有数据库),再部署上线。
|
|
4
4
|
|
|
5
5
|
## 改完代码后部署上线
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- pull/push/diff/log 都用原生 git;云端 `sprint/default` 比本地新时,先 `git pull --rebase origin sprint/default`,解决冲突后再 push 和 publish。
|
|
21
21
|
- 环境变量由脚手架在本地启动时处理。
|
|
22
22
|
- frontend(纯前端,vite-react)应用的开发与部署流程和全栈一致,只是没有数据库、跳过一切 `+db-*` 步骤。后续要加数据库/后端能力时,本地链路不提供类型升级:引导用户到妙搭 web 的云端会话里用自然语言提出(如「给这个应用加登录和数据存储」)即可触发升级,无需特殊指令,也不要替用户拼站点地址。
|
|
23
|
+
- 全栈应用的数据库默认可能未开启。**首次建表、写用库代码之前**:`+features --app-id "$app_id"` 看 `database` → `enabled=false` 时 `+feature-enable --feature database --app-id "$app_id"`(见 [features](lark-apps-features.md))→ 平台自动生效,不需要 `+env-pull`、不需要手动重启 → 建表并做一次真实读写验证。库能查能建表不代表已开通(以 `+features` 为准),不要改环境文件或自行接连接串绕过开关。
|
|
23
24
|
- DB 调试用 `+db-table-list` / `+db-table-get` / `+db-execute`(见 lark-apps-db skill);不要裸连数据库或自行拼连接串。
|
|
24
25
|
- DB 分 `dev` / `online`;日常调试优先 `--environment dev`。dev 的库结构变更要上线时,仍按应用发布链路走 `+release-create`,不要另造“数据库发布”步骤。
|
|
25
26
|
- 存量单库应用需要 dev/online 多环境时,用 `+db-env-create --environment dev`。这是不可逆 high-risk 操作。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-miaoda-sandbox-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.45",
|
|
4
4
|
"description": "Miaoda 合并沙箱 skills 包(包含原 miaoda-skills 的 miaoda / miaoda-modern / miaoda-design / shared 四条业务线);发布公网 npm,随沙箱运行时经 update-skills 同步到 .agent/skills/",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|