@h-ai/cli 0.1.0-alpha.53 → 0.1.0-alpha.55
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/README.md +18 -0
- package/dist/{chunk-U4PA632G.js → chunk-FVDXJYMO.js} +34 -67
- package/dist/chunk-FVDXJYMO.js.map +1 -0
- package/dist/cli-config-templates-WGN2I23J.js +5 -0
- package/dist/{cli-config-templates-GSXKLGU5.js.map → cli-config-templates-WGN2I23J.js.map} +1 -1
- package/dist/index.js +174 -105
- package/dist/index.js.map +1 -1
- package/dist/{src-QNEJJ3BV.js → src-6ZBWDBUB.js} +127 -87
- package/dist/{src-QNEJJ3BV.js.map → src-6ZBWDBUB.js.map} +1 -1
- package/package.json +13 -4
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -0
- package/templates/apps/api/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +1 -0
- package/templates/apps/fullstack/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
- package/templates/base/eslint.config.js.hbs +1 -0
- package/templates/base/package.json.hbs +3 -1
- package/templates/skills/AGENTS.md +8 -22
- package/templates/skills/CLAUDE.md +3 -16
- package/templates/skills/bridges/admin/AGENTS.md +3 -16
- package/templates/skills/bridges/admin/CLAUDE.md +3 -11
- package/templates/skills/bridges/admin/copilot-instructions.md +2 -39
- package/templates/skills/bridges/api/AGENTS.md +5 -18
- package/templates/skills/bridges/api/CLAUDE.md +3 -11
- package/templates/skills/bridges/api/copilot-instructions.md +3 -38
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +24 -59
- package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +1 -15
- package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +5 -45
- package/templates/skills/bridges/h5/AGENTS.md +4 -17
- package/templates/skills/bridges/h5/CLAUDE.md +3 -11
- package/templates/skills/bridges/h5/copilot-instructions.md +2 -37
- package/templates/skills/bridges/mobile-app/AGENTS.md +5 -18
- package/templates/skills/bridges/mobile-app/CLAUDE.md +3 -11
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -38
- package/templates/skills/bridges/website/AGENTS.md +4 -17
- package/templates/skills/bridges/website/CLAUDE.md +3 -11
- package/templates/skills/bridges/website/copilot-instructions.md +2 -37
- package/templates/skills/copilot-instructions.md +2 -36
- package/templates/skills/hai-ai/SKILL.md +35 -389
- package/templates/skills/hai-ai/guide.md +383 -0
- package/templates/skills/hai-ai/reference.md +6 -2
- package/templates/skills/hai-api-client/SKILL.md +6 -6
- package/templates/skills/hai-api-contract/SKILL.md +10 -25
- package/templates/skills/hai-app-create/SKILL.md +29 -388
- package/templates/skills/hai-app-create/reference.md +350 -0
- package/templates/skills/hai-app-review/SKILL.md +15 -23
- package/templates/skills/hai-app-tests/SKILL.md +29 -494
- package/templates/skills/hai-app-tests/reference.md +442 -0
- package/templates/skills/hai-audit/SKILL.md +6 -6
- package/templates/skills/hai-build/SKILL.md +19 -330
- package/templates/skills/hai-build/reference.md +326 -0
- package/templates/skills/hai-cache/SKILL.md +28 -31
- package/templates/skills/hai-capacitor/SKILL.md +19 -364
- package/templates/skills/hai-capacitor/reference.md +352 -0
- package/templates/skills/hai-ci/SKILL.md +17 -20
- package/templates/skills/hai-core/SKILL.md +25 -564
- package/templates/skills/hai-core/reference.md +533 -0
- package/templates/skills/hai-crypto/SKILL.md +8 -20
- package/templates/skills/hai-datapipe/SKILL.md +9 -23
- package/templates/skills/hai-deploy/SKILL.md +18 -24
- package/templates/skills/hai-framework-sync/SKILL.md +8 -20
- package/templates/skills/hai-iam/SKILL.md +27 -365
- package/templates/skills/hai-iam/reference.md +343 -0
- package/templates/skills/hai-kit/SKILL.md +6 -6
- package/templates/skills/hai-payment/SKILL.md +6 -26
- package/templates/skills/hai-pr-review/SKILL.md +6 -22
- package/templates/skills/hai-reach/SKILL.md +19 -317
- package/templates/skills/hai-reach/reference.md +303 -0
- package/templates/skills/hai-reldb/SKILL.md +19 -472
- package/templates/skills/hai-reldb/reference.md +452 -0
- package/templates/skills/hai-scheduler/SKILL.md +18 -6
- package/templates/skills/hai-serv/SKILL.md +27 -759
- package/templates/skills/hai-serv/reference.md +736 -0
- package/templates/skills/hai-storage/SKILL.md +7 -25
- package/templates/skills/hai-ui/SKILL.md +29 -621
- package/templates/skills/hai-ui/reference.md +598 -0
- package/templates/skills/hai-vecdb/SKILL.md +18 -303
- package/templates/skills/hai-vecdb/reference.md +291 -0
- package/dist/chunk-U4PA632G.js.map +0 -1
- package/dist/cli-config-templates-GSXKLGU5.js +0 -5
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# hai-capacitor — 详细用法
|
|
2
|
+
|
|
3
|
+
从 [SKILL.md](SKILL.md) 选择主题后读取对应小节。代码片段展示 API 用法;省略的业务变量、schema、依赖与 return 上下文需由应用补齐。
|
|
4
|
+
|
|
5
|
+
> `@h-ai/capacitor` 是 hai-framework 的 Capacitor 原生桥接模块,封装常用原生能力为统一 API,返回 `HaiResult<T>`。与 `@h-ai/api-client` 配合时,Token 仅存于原生安全存储,不回退到 Web `localStorage`。
|
|
6
|
+
|
|
7
|
+
## 运行环境
|
|
8
|
+
|
|
9
|
+
> **浏览器端 / 原生 App 专用。** 在 Capacitor 原生环境(Android/iOS)中提供完整能力;`createCapacitorTokenStorage()` 仅在原生环境持久化 token,纯 Web 不做不安全回退。`capacitor.preferences` 仍可用于普通偏好数据。
|
|
10
|
+
|
|
11
|
+
## 适用场景
|
|
12
|
+
|
|
13
|
+
- Android/iOS 原生应用开发(Svelte 5 + Vite + Capacitor)
|
|
14
|
+
- Token 安全存储(`@aparajita/capacitor-secure-storage`)
|
|
15
|
+
- 设备信息获取(平台、型号、版本)
|
|
16
|
+
- 推送通知注册与监听(FCM / APNs)
|
|
17
|
+
- 原生相机拍照 / 相册选取
|
|
18
|
+
- 状态栏配置(沉浸式、颜色、样式)
|
|
19
|
+
|
|
20
|
+
## 使用步骤
|
|
21
|
+
|
|
22
|
+
### 1. 初始化与关闭
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
26
|
+
|
|
27
|
+
// 应用启动时初始化(检测 Capacitor 环境可用性)
|
|
28
|
+
const result = await capacitor.init()
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
// result.error.code === HaiCapacitorError.NOT_AVAILABLE.code
|
|
31
|
+
// 非 Capacitor 环境(纯 Web)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 检查状态
|
|
35
|
+
capacitor.isInitialized // boolean
|
|
36
|
+
capacitor.getPlatform() // 'android' | 'ios' | 'web'
|
|
37
|
+
capacitor.isNative() // true = 原生 App
|
|
38
|
+
|
|
39
|
+
// 关闭模块(重置状态)
|
|
40
|
+
await capacitor.close()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Token 安全存储(与 api-client 配合)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { apiClient } from '@h-ai/api-client'
|
|
47
|
+
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
48
|
+
|
|
49
|
+
await apiClient.init({
|
|
50
|
+
baseUrl: import.meta.env.PUBLIC_API_BASE,
|
|
51
|
+
auth: {
|
|
52
|
+
storage: createCapacitorTokenStorage(),
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`createCapacitorTokenStorage()` 返回 `TokenStorage` 实例(兼容 `@h-ai/api-client`),底层使用 `@aparajita/capacitor-secure-storage`:
|
|
58
|
+
|
|
59
|
+
- Android → Android KeyStore + 加密 SharedPreferences
|
|
60
|
+
- iOS → Keychain
|
|
61
|
+
- Web → **不回退到 localStorage**;`get*()` 返回 `null`,`set*()` / `clear()` 为 no-op
|
|
62
|
+
|
|
63
|
+
所有方法内置 try-catch;原生安全存储异常时 get 返回 `null`、set/clear 静默失败并记录日志。
|
|
64
|
+
|
|
65
|
+
#### Preferences 子操作
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
69
|
+
|
|
70
|
+
// 返回 HaiResult<string | null>
|
|
71
|
+
const result = await capacitor.preferences.get('my_key')
|
|
72
|
+
if (result.success) {
|
|
73
|
+
// result.data — 值或 null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await capacitor.preferences.set('my_key', 'value')
|
|
77
|
+
await capacitor.preferences.remove('my_key')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. 设备信息
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
84
|
+
|
|
85
|
+
const info = await capacitor.device.getInfo()
|
|
86
|
+
if (info.success) {
|
|
87
|
+
info.data.platform // 'android' | 'ios' | 'web'
|
|
88
|
+
info.data.model // 'Pixel 7'
|
|
89
|
+
info.data.osVersion // '14'
|
|
90
|
+
info.data.manufacturer // 'Google'
|
|
91
|
+
info.data.isVirtual // false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const version = await capacitor.device.getAppVersion()
|
|
95
|
+
if (version.success) {
|
|
96
|
+
version.data.version // '1.0.0'
|
|
97
|
+
version.data.build // '42'
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. 推送通知
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
105
|
+
|
|
106
|
+
// 注册推送(请求权限 + 获取设备 Token)
|
|
107
|
+
const reg = await capacitor.push.register()
|
|
108
|
+
if (reg.success) {
|
|
109
|
+
// 将 reg.data.token 上报给后端
|
|
110
|
+
await api.post('/push/register', { token: reg.data.token })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 监听推送事件(返回 HaiResult 包裹的 async 清理函数)
|
|
114
|
+
const listenResult = await capacitor.push.listen({
|
|
115
|
+
onReceived: (notification) => {
|
|
116
|
+
// 前台收到推送
|
|
117
|
+
// notification: { id, title?, body?, data? }
|
|
118
|
+
},
|
|
119
|
+
onActionPerformed: (notification) => {
|
|
120
|
+
// 用户点击推送
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// 停止监听
|
|
125
|
+
if (listenResult.success) {
|
|
126
|
+
await listenResult.data()
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 5. 相机
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
134
|
+
|
|
135
|
+
const photo = await capacitor.camera.takePhoto({
|
|
136
|
+
quality: 80, // 0-100
|
|
137
|
+
source: 'camera', // 'camera' | 'photos' | 'prompt'
|
|
138
|
+
resultType: 'base64', // 'uri' | 'base64' | 'dataUrl'
|
|
139
|
+
width: 800, // 最大宽度(可选)
|
|
140
|
+
height: 600, // 最大高度(可选)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
if (photo.success) {
|
|
144
|
+
const imgSrc = `data:image/${photo.data.format};base64,${photo.data.data}`
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 6. 状态栏
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
152
|
+
|
|
153
|
+
await capacitor.statusBar.configure({
|
|
154
|
+
backgroundColor: '#ffffff',
|
|
155
|
+
style: 'dark', // 'dark' | 'light' | 'default'(文字颜色)
|
|
156
|
+
overlay: true, // 沉浸式
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await capacitor.statusBar.hide()
|
|
160
|
+
await capacitor.statusBar.show()
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 核心 API
|
|
164
|
+
|
|
165
|
+
| API | 用途 | 返回值 |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `capacitor.init()` | 初始化模块(检测环境) | `HaiResult<void>` |
|
|
168
|
+
| `capacitor.close()` | 关闭模块,重置状态 | `Promise<void>` |
|
|
169
|
+
| `capacitor.getPlatform()` | 获取当前平台 | `'android' \| 'ios' \| 'web'` |
|
|
170
|
+
| `capacitor.isNative()` | 是否为原生环境 | `boolean` |
|
|
171
|
+
| `capacitor.isInitialized` | 是否已初始化 | `boolean` |
|
|
172
|
+
| `createCapacitorTokenStorage()` | 创建 Token 存储 | `TokenStorage`(兼容 api-client) |
|
|
173
|
+
| `capacitor.preferences.get(key)` | 安全读取 Preference | `HaiResult<string \| null>` |
|
|
174
|
+
| `capacitor.preferences.set(key, value)` | 安全写入 Preference | `HaiResult<void>` |
|
|
175
|
+
| `capacitor.preferences.remove(key)` | 安全删除 Preference | `HaiResult<void>` |
|
|
176
|
+
| `capacitor.device.getInfo()` | 设备信息 | `HaiResult<DeviceInfo>` |
|
|
177
|
+
| `capacitor.device.getAppVersion()` | 应用版本 | `HaiResult<{ version, build }>` |
|
|
178
|
+
| `capacitor.push.register()` | 注册推送 | `HaiResult<PushRegistration>` |
|
|
179
|
+
| `capacitor.push.listen(callbacks)` | 监听推送事件 | `HaiResult<() => Promise<void>>`(async 清理函数) |
|
|
180
|
+
| `capacitor.camera.takePhoto(options?)` | 拍照 / 选取图片 | `HaiResult<PhotoResult>` |
|
|
181
|
+
| `capacitor.statusBar.configure(config)` | 配置状态栏 | `HaiResult<void>` |
|
|
182
|
+
| `capacitor.statusBar.show()` | 显示状态栏 | `HaiResult<void>` |
|
|
183
|
+
| `capacitor.statusBar.hide()` | 隐藏状态栏 | `HaiResult<void>` |
|
|
184
|
+
|
|
185
|
+
## 错误码 — `HaiCapacitorError`
|
|
186
|
+
|
|
187
|
+
| 错误码 | code | 说明 |
|
|
188
|
+
| -------------------------------------------- | ---------------------- | -------------------- |
|
|
189
|
+
| `HaiCapacitorError.INIT_FAILED` | `hai:capacitor:001` | 初始化失败 |
|
|
190
|
+
| `HaiCapacitorError.NOT_AVAILABLE` | `hai:capacitor:002` | Capacitor 不可用 |
|
|
191
|
+
| `HaiCapacitorError.INIT_IN_PROGRESS` | `hai:capacitor:003` | 正在初始化中 |
|
|
192
|
+
| `HaiCapacitorError.NOT_INITIALIZED` | `hai:capacitor:010` | 模块未初始化 |
|
|
193
|
+
| `HaiCapacitorError.PREFERENCES_GET_FAILED` | `hai:capacitor:011` | Preferences 读取失败 |
|
|
194
|
+
| `HaiCapacitorError.PREFERENCES_SET_FAILED` | `hai:capacitor:012` | Preferences 写入失败 |
|
|
195
|
+
| `HaiCapacitorError.PREFERENCES_REMOVE_FAILED`| `hai:capacitor:013` | Preferences 删除失败 |
|
|
196
|
+
| `HaiCapacitorError.DEVICE_INFO_FAILED` | `hai:capacitor:020` | 获取设备信息失败 |
|
|
197
|
+
| `HaiCapacitorError.APP_VERSION_FAILED` | `hai:capacitor:021` | 获取应用版本失败 |
|
|
198
|
+
| `HaiCapacitorError.PUSH_REGISTER_FAILED` | `hai:capacitor:030` | 推送注册失败 |
|
|
199
|
+
| `HaiCapacitorError.PUSH_LISTEN_FAILED` | `hai:capacitor:031` | 推送监听失败 |
|
|
200
|
+
| `HaiCapacitorError.CAMERA_FAILED` | `hai:capacitor:040` | 拍照/相册失败 |
|
|
201
|
+
| `HaiCapacitorError.STATUS_BAR_FAILED` | `hai:capacitor:050` | 状态栏配置失败 |
|
|
202
|
+
|
|
203
|
+
## 常见模式
|
|
204
|
+
|
|
205
|
+
### Mobile App 标准初始化
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// src/lib/capacitor.ts
|
|
209
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
210
|
+
|
|
211
|
+
export async function initCapacitor() {
|
|
212
|
+
const result = await capacitor.init()
|
|
213
|
+
if (!result.success) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (capacitor.isNative()) {
|
|
218
|
+
await capacitor.statusBar.configure({
|
|
219
|
+
backgroundColor: '#ffffff',
|
|
220
|
+
style: 'light',
|
|
221
|
+
overlay: false,
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```svelte
|
|
228
|
+
<!-- src/App.svelte -->
|
|
229
|
+
<script lang='ts'>
|
|
230
|
+
import { onMount } from 'svelte'
|
|
231
|
+
import { initCapacitor } from './lib/capacitor'
|
|
232
|
+
|
|
233
|
+
onMount(() => { initCapacitor() })
|
|
234
|
+
</script>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### SPA 模式配置(必需)
|
|
238
|
+
|
|
239
|
+
Capacitor 应用使用 Vite 构建 SPA,并让原生壳读取 `dist`:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// capacitor.config.ts
|
|
243
|
+
import type { CapacitorConfig } from '@capacitor/cli'
|
|
244
|
+
|
|
245
|
+
const config: CapacitorConfig = {
|
|
246
|
+
appId: 'com.example.app',
|
|
247
|
+
appName: 'Example App',
|
|
248
|
+
webDir: 'dist',
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export default config
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Token 存储 + 认证流程
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// src/lib/api.ts
|
|
258
|
+
import { apiClient } from '@h-ai/api-client'
|
|
259
|
+
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
260
|
+
|
|
261
|
+
export async function initApi() {
|
|
262
|
+
return apiClient.init({
|
|
263
|
+
baseUrl: `${import.meta.env.PUBLIC_API_BASE}/api/v1`,
|
|
264
|
+
auth: {
|
|
265
|
+
storage: createCapacitorTokenStorage(),
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export { apiClient }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 推送通知完整流程
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
277
|
+
|
|
278
|
+
export async function setupPush() {
|
|
279
|
+
if (!capacitor.isNative()) {
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const reg = await capacitor.push.register()
|
|
284
|
+
if (!reg.success) {
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 上报 Token 给后端
|
|
289
|
+
await api.post('/push/register', {
|
|
290
|
+
token: reg.data.token,
|
|
291
|
+
platform: capacitor.getPlatform(),
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// 监听推送
|
|
295
|
+
const listenResult = await capacitor.push.listen({
|
|
296
|
+
onReceived: (n) => {
|
|
297
|
+
// 前台通知处理
|
|
298
|
+
},
|
|
299
|
+
onActionPerformed: (n) => {
|
|
300
|
+
// 用户点击跳转
|
|
301
|
+
},
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 拍照上传
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
310
|
+
|
|
311
|
+
async function captureAndUpload() {
|
|
312
|
+
const photo = await capacitor.camera.takePhoto({
|
|
313
|
+
source: 'camera',
|
|
314
|
+
resultType: 'base64',
|
|
315
|
+
quality: 80,
|
|
316
|
+
width: 1024,
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
if (!photo.success) {
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 上传 base64 给后端
|
|
324
|
+
await api.post('/files/upload', {
|
|
325
|
+
data: photo.data.data,
|
|
326
|
+
format: photo.data.format,
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## 插件依赖
|
|
332
|
+
|
|
333
|
+
| 插件 | 类型 | 用于 |
|
|
334
|
+
| --- | --- | --- |
|
|
335
|
+
| `@capacitor/core` | peerDependency(必需) | 核心运行时 |
|
|
336
|
+
| `@aparajita/capacitor-secure-storage` | 可选 peerDependency(原生 token 存储必需) | `createCapacitorTokenStorage()` |
|
|
337
|
+
| `@capacitor/preferences` | peerDependency(必需) | `capacitor.preferences.*` 普通偏好数据 |
|
|
338
|
+
| `@capacitor/device` | 可选 | `getDeviceInfo()` |
|
|
339
|
+
| `@capacitor/app` | 可选 | `getAppVersion()` |
|
|
340
|
+
| `@capacitor/push-notifications` | 可选 | `registerPush()` / `listenPush()` |
|
|
341
|
+
| `@capacitor/camera` | 可选 | `takePhoto()` |
|
|
342
|
+
| `@capacitor/status-bar` | 可选 | `configureStatusBar()` / `show/hide` |
|
|
343
|
+
|
|
344
|
+
可选插件未安装时,对应 API 调用会返回 `err`(动态 import 失败被 catch)。
|
|
345
|
+
|
|
346
|
+
## 相关 Skills
|
|
347
|
+
|
|
348
|
+
- `hai-api-client`:HTTP 客户端(Token 管理依赖 capacitor 存储)
|
|
349
|
+
- `hai-iam`:认证流程(登录获取 Token → 存储到 Capacitor)
|
|
350
|
+
- `hai-ui`:移动端 UI 组件(SafeArea、BottomNav 等)
|
|
351
|
+
|
|
352
|
+
原生交付必须单独执行 Tauri/Gradle/Xcode 打包,并在目标 OS/设备验证安装启动、登录/退出、重启会话、安全 TokenStore、真实 API 域名与覆盖升级。Web 构建/E2E 不代替原生验收。保存提交、包 hash、平台/工具链及逐项结果;缺平台写未验证,禁止静默 skip。hai-framework 仓库参考 `.github/workflows/native.yml` 与 desktop-app / mobile-app README 的「原生交付与验收」章节。
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-ci
|
|
3
|
-
description:
|
|
3
|
+
description: "维护 hai 应用的 CI/CD、GitHub Actions、质量门禁和凭据泄漏扫描。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-ci — CI/CD 与质量门禁规范
|
|
7
7
|
|
|
8
8
|
## 能力契约
|
|
9
9
|
|
|
10
|
+
框架完整质量门禁在 Node.js 22.12.0(声明的最低版本)执行;升级 engines 时同步 CI、README 和脚手架,不能只在更高版本通过后宣称最低版本通过。
|
|
11
|
+
|
|
12
|
+
框架发布通过实际 npm 精确版本和 GitHub Release 判断完成,不能只看 tag。部分失败重跑原提交,只补发缺失包;npm 全部成功后再建 tag/Release。查询异常与 404 必须区分,tag 指向其他提交时停止补发,禁止将新源码混入旧版本。
|
|
13
|
+
|
|
14
|
+
Turbo 的 `build.env` 必须列出影响产物的变量;框架包含容器构建开关、E2E 构建模式及 PUBLIC/VITE/TAURI 前缀,共享 tsup 配置也纳入依赖。Docker/Testcontainers 连接变量通过测试任务透传。验证缓存须切换变量检查哈希和真实产物,不能只检查配置文本。
|
|
15
|
+
|
|
16
|
+
框架仓库发布验收使用 `pnpm test:release-local`;其 E2E 与 CI 共用根 `pnpm e2e`,覆盖 CLI 和全部 Web 应用,不能缩减为仅 Admin Console。应用仓库应在自己的根 E2E 命令集中维护所有端到端入口。容器和原生验收须独立记录实际结果。
|
|
17
|
+
|
|
10
18
|
| 项目 | 契约 |
|
|
11
19
|
| --- | --- |
|
|
12
|
-
| 能力 |
|
|
13
|
-
| 适用场景 |
|
|
14
|
-
| 输入 |
|
|
15
|
-
| 输出 |
|
|
16
|
-
| 限制 |
|
|
20
|
+
| 能力 | 维护 hai 应用的 CI/CD、GitHub Actions、质量门禁和凭据泄漏扫描 |
|
|
21
|
+
| 适用场景 | 应用 CI/CD 和质量门禁维护 |
|
|
22
|
+
| 输入 | 现有 workflow、package scripts、权限和发布策略 |
|
|
23
|
+
| 输出 | 可执行工作流与检查结果 |
|
|
24
|
+
| 限制 | 默认验证真实 npm 依赖;最小 workflow 权限,不向不可信 PR 暴露 secret,未经授权不发布。 |
|
|
17
25
|
|
|
18
26
|
> 面向 AI 助手和团队成员的仓库自动化指南。目标是让每个 PR 都能被机器稳定验证,同时避免把 secrets 暴露给不可信 PR 或第三方 Action。
|
|
19
27
|
|
|
20
|
-
---
|
|
21
|
-
|
|
22
28
|
## 适用场景
|
|
23
29
|
|
|
24
30
|
- 新增或修改 `.github/workflows/**`
|
|
@@ -29,8 +35,6 @@ description: 设计和维护 hai-framework 应用仓库的 CI/CD、GitHub Action
|
|
|
29
35
|
|
|
30
36
|
不负责:PR 人工审查流程(用 `hai-pr-review`)、测试用例设计(用 `hai-app-tests`)、代码质量审查(用 `hai-app-review`)、云资源开通与应用部署 API(用 `hai-deploy`)。
|
|
31
37
|
|
|
32
|
-
---
|
|
33
|
-
|
|
34
38
|
## 核心原则
|
|
35
39
|
|
|
36
40
|
1. **确定性检查优先**:CI 只负责可重复的机器检查;测试内容设计交给 `hai-app-tests`,代码规范判断交给 `hai-app-review`。
|
|
@@ -40,8 +44,6 @@ description: 设计和维护 hai-framework 应用仓库的 CI/CD、GitHub Action
|
|
|
40
44
|
5. **先轻后重**:核心 CI 必须稳定;E2E、发布、安全深扫可以拆成 manual/nightly 或独立 workflow。
|
|
41
45
|
6. **可追溯**:workflow 改动必须说明触发条件、权限、缓存、失败路径和验证命令。
|
|
42
46
|
|
|
43
|
-
---
|
|
44
|
-
|
|
45
47
|
## 标准 CI 工作流
|
|
46
48
|
|
|
47
49
|
### PR / main 基础质量门禁
|
|
@@ -70,8 +72,6 @@ pnpm build
|
|
|
70
72
|
- 本地联调使用 `framework:use:local` / `framework:watch`,但不要让默认 PR CI 隐式依赖本机路径。
|
|
71
73
|
- 需要验证本地 framework 改动时,创建显式的 sync/drift check 或双仓联动 PR。
|
|
72
74
|
|
|
73
|
-
---
|
|
74
|
-
|
|
75
75
|
## GitHub Actions 安全清单
|
|
76
76
|
|
|
77
77
|
- [ ] workflow 顶层声明 `permissions`
|
|
@@ -83,8 +83,6 @@ pnpm build
|
|
|
83
83
|
- [ ] release workflow 独立提权,只在 main/tag/manual 触发
|
|
84
84
|
- [ ] 失败日志可定位,不吞错、不 `|| true` 掩盖核心质量门禁
|
|
85
85
|
|
|
86
|
-
---
|
|
87
|
-
|
|
88
86
|
## Secret scan 策略
|
|
89
87
|
|
|
90
88
|
### 推荐
|
|
@@ -99,8 +97,6 @@ pnpm build
|
|
|
99
97
|
- ❌ 把完整密钥关键词列表传给未知第三方 action
|
|
100
98
|
- ❌ 为了通过扫描而删除失败证据或扩大忽略范围
|
|
101
99
|
|
|
102
|
-
---
|
|
103
|
-
|
|
104
100
|
## Workflow 变更交付清单
|
|
105
101
|
|
|
106
102
|
修改 CI/CD 时,PR 描述必须包含:
|
|
@@ -111,8 +107,6 @@ pnpm build
|
|
|
111
107
|
- 安全影响:是否读取 secrets、是否写仓库、是否评论 PR
|
|
112
108
|
- 验证方式:本地命令或 GitHub Actions dry run 结果
|
|
113
109
|
|
|
114
|
-
---
|
|
115
|
-
|
|
116
110
|
## 相关 Skills
|
|
117
111
|
|
|
118
112
|
- `hai-build` — 项目结构与质量门禁总览
|
|
@@ -120,3 +114,6 @@ pnpm build
|
|
|
120
114
|
- `hai-pr-review` — PR 审查与 AI Review 策略
|
|
121
115
|
- `hai-deploy` — 应用部署与云服务开通
|
|
122
116
|
- `hai-framework-sync` — hai-framework 与应用仓库同步策略
|
|
117
|
+
|
|
118
|
+
发布完成判定始终查询根版本的 npm 包;发布 job 同步包版本但不提交,标签固定指向触发质量验收的源码提交。发布成功后由独立 job 提交版本同步;完整重跑即使无需补发也会执行同步,远端已存在相同内容时成功退出,分支有新源码时拒绝覆盖并要求从最新 main 重跑。
|
|
119
|
+
原生交付必须单独执行 Tauri/Gradle/Xcode 打包,并在目标 OS/设备验证安装启动、登录/退出、重启会话、安全 TokenStore、真实 API 域名与覆盖升级。Web 构建/E2E 不代替原生验收。保存提交、包 hash、平台/工具链及逐项结果;缺平台写未验证,禁止静默 skip。hai-framework 仓库参考 `.github/workflows/native.yml` 与 desktop-app / mobile-app README 的「原生交付与验收」章节。
|