@lingxia/skill 0.8.0

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.
Files changed (37) hide show
  1. package/README.md +95 -0
  2. package/bin/install.mjs +247 -0
  3. package/package.json +49 -0
  4. package/scripts/sync.mjs +69 -0
  5. package/skill/SKILL.md +334 -0
  6. package/skill/app/apple-sdk.md +312 -0
  7. package/skill/app/applinks.md +289 -0
  8. package/skill/app/project.md +760 -0
  9. package/skill/cli/lxdev.md +195 -0
  10. package/skill/cli/reference.md +481 -0
  11. package/skill/examples/hello-host-js/README.md +25 -0
  12. package/skill/examples/hello-host-js/home/lxapp.json +12 -0
  13. package/skill/examples/hello-host-js/home/pages/home/index.json +4 -0
  14. package/skill/examples/hello-host-js/home/pages/home/index.ts +14 -0
  15. package/skill/examples/hello-host-js/home/pages/home/index.tsx +15 -0
  16. package/skill/examples/hello-host-js/lingxia.yaml +39 -0
  17. package/skill/examples/hello-host-rust/Cargo.toml +15 -0
  18. package/skill/examples/hello-host-rust/README.md +44 -0
  19. package/skill/examples/hello-host-rust/home/lxapp.json +13 -0
  20. package/skill/examples/hello-host-rust/home/pages/home/index.html +46 -0
  21. package/skill/examples/hello-host-rust/home/pages/home/index.json +4 -0
  22. package/skill/examples/hello-host-rust/lingxia.yaml +32 -0
  23. package/skill/examples/hello-host-rust/src/lib.rs +58 -0
  24. package/skill/examples/hello-lxapp/README.md +29 -0
  25. package/skill/examples/hello-lxapp/lxapp.config.ts +8 -0
  26. package/skill/examples/hello-lxapp/lxapp.json +14 -0
  27. package/skill/examples/hello-lxapp/package.json +14 -0
  28. package/skill/examples/hello-lxapp/pages/home/index.json +4 -0
  29. package/skill/examples/hello-lxapp/pages/home/index.ts +35 -0
  30. package/skill/examples/hello-lxapp/pages/home/index.tsx +34 -0
  31. package/skill/lxapp/bridge.md +654 -0
  32. package/skill/lxapp/components.md +375 -0
  33. package/skill/lxapp/guide.md +675 -0
  34. package/skill/lxapp/lx-api.md +481 -0
  35. package/skill/native/development.md +414 -0
  36. package/skill/reference/file-lifecycle.md +325 -0
  37. package/skill/skill-manifest.json +6 -0
@@ -0,0 +1,325 @@
1
+ # LingXia File Lifecycle
2
+
3
+ This document defines LingXia-managed file lifetimes, storage locations, cleanup triggers, quota behavior, and the relationship between APIs such as `downloadFile`, `getFileManager()`, `chooseMedia`, `compressImage`, and `compressVideo`.
4
+
5
+ The design goal is simple: a returned path should tell developers whether the file is temporary, cache-managed, or durable.
6
+
7
+ ## Storage Classes
8
+
9
+ LingXia exposes three LxApp-owned storage classes:
10
+
11
+ | Class | URI | Physical Owner | Lifetime |
12
+ | --- | --- | --- | --- |
13
+ | Temp | `lx://temp/<opaque_id>` | current LxApp runtime session | short-lived, auto-cleaned |
14
+ | User Data | `lx://userdata/<path>` | one LxApp | durable, never auto-cleaned |
15
+ | User Cache | `lx://usercache/<path>` | one LxApp | regenerable, auto-cleaned |
16
+
17
+ Shell or desktop-visible downloads are host product behavior and are not exposed through `downloadFile.filePath`. A future Shell-managed download API should own progress records, permissions, and user-visible cleanup separately.
18
+
19
+ ## Physical Layout
20
+
21
+ LingXia identifies each LxApp storage owner by its fingermark.
22
+
23
+ ```text
24
+ <app_data>/lingxia/lxapps/<lxapp_fingermark>/
25
+ installed LxApp bundle
26
+
27
+ <app_data>/lingxia/userdata/<lxapp_fingermark>/
28
+ durable LxApp files
29
+
30
+ <app_data>/lingxia/usercache/<lxapp_fingermark>/
31
+ LingXia-managed regenerable cache
32
+
33
+ <app_data>/lingxia/storage/<lxapp_fingermark>.redb
34
+ LxApp key-value storage
35
+
36
+ <app_cache>/lingxia/lxapps/temp/<lxapp_fingermark>/<session_id>/
37
+ current runtime temp files
38
+ ```
39
+
40
+ `usercache` intentionally lives under app data, not OS cache, because LingXia owns its cleanup policy. Temp lives under app cache because it is session-scoped and disposable.
41
+
42
+ ## API Semantics
43
+
44
+ ### `downloadFile`
45
+
46
+ `downloadFile` always stages internally first. Final output depends on `filePath`.
47
+
48
+ Without `filePath`, the result is temp:
49
+
50
+ ```ts
51
+ const result = await lx.downloadFile({ url, headers, timeout, signal });
52
+ result.tempFilePath; // lx://temp/<opaque_id>
53
+ ```
54
+
55
+ With `filePath`, the destination must be relative or `lx://userdata/...`:
56
+
57
+ ```ts
58
+ const result = await lx.downloadFile({
59
+ url,
60
+ filePath: "downloads/video.mp4",
61
+ });
62
+ result.filePath; // lx://userdata/downloads/video.mp4
63
+ ```
64
+
65
+ Rejected destinations:
66
+
67
+ - `lx://usercache/...`
68
+ - native absolute paths
69
+ - host download directories
70
+ - drive-style paths containing `:`
71
+ - backslash paths
72
+ - empty path segments
73
+ - `.` or `..` segments
74
+ - the `lx://userdata` root itself
75
+
76
+ ### `getFileManager`
77
+
78
+ `getFileManager` returns the LingXia-managed file manager.
79
+
80
+ ```ts
81
+ const fs = lx.getFileManager();
82
+ ```
83
+
84
+ Relative paths resolve under userdata. `lx.env.USER_DATA_PATH` and `lx.env.USER_CACHE_PATH` provide the explicit `lx://userdata` and `lx://usercache` roots. Read methods also accept `lx://temp/...`.
85
+
86
+ ### File Copy And Move
87
+
88
+ ```ts
89
+ const fs = lx.getFileManager();
90
+ await fs.copyFile({
91
+ srcPath: result.tempFilePath,
92
+ destPath: "downloads/video.mp4",
93
+ });
94
+
95
+ await fs.rename({
96
+ oldPath: result.tempFilePath,
97
+ newPath: `${lx.env.USER_CACHE_PATH}/previews/video.mp4`,
98
+ });
99
+ ```
100
+
101
+ Rules:
102
+
103
+ - `copyFile` copies from temp, userdata, or usercache into userdata or usercache
104
+ - `rename` moves from temp, userdata, or usercache into userdata or usercache
105
+ - relative destinations resolve under userdata
106
+ - explicit `lx://` destinations may target `lx://userdata` or `lx://usercache`
107
+ - parent directories are created automatically
108
+ - existing destination files are not overwritten
109
+ - final writes use a sibling temp file and rename/replace, so failed writes do not leave final partial files
110
+
111
+ ### FileManager writes
112
+
113
+ `writeFile`, `copyFile`, and `rename` are explicit file management APIs. They default to no overwrite and support `overwrite: true` only when requested. Overwrite applies to files only; directories are never replaced by file writes.
114
+
115
+ `rename` is move semantics. It may move from temp, userdata, or usercache into userdata or usercache. Moving a temp download into usercache avoids a second durable copy and hands the file to cache cleanup.
116
+
117
+ `readDir` resolves to an async iterator of directory entries with `name`, `isFile`, `isDirectory`, and `isSymlink`, matching the Rong fs shape while keeping LingXia path lifecycle rules.
118
+
119
+ Userdata writes run userdata and appStorage quota checks. Usercache writes run usercache cleanup/quota checks and then appStorage checks.
120
+
121
+ ### Media APIs
122
+
123
+ `chooseMedia`, `compressImage`, `compressVideo`, and video thumbnail APIs return temp outputs by default. Use `copyFile` to keep a copy, or `rename` to move it into userdata or usercache.
124
+
125
+ ## `lingxia.yaml` Storage Configuration
126
+
127
+ `lingxia.yaml` configures storage limits. This section is only configuration; cleanup behavior is described in the next section.
128
+
129
+ ```yaml
130
+ storage:
131
+ tempMaxSizeMB: 1024
132
+ cacheMaxSizeMB: 2048
133
+ dataMaxSizeMB: 4096
134
+ appStorageMaxSizeMB: 16384
135
+ ```
136
+
137
+ | Setting | Default | Scope | Meaning | `0` Means |
138
+ | --- | ---: | --- | --- | --- |
139
+ | `tempMaxSizeMB` | 1024 | per LxApp runtime session | max size for returned temp files | disable temp size limit |
140
+ | `cacheMaxSizeMB` | 2048 | per LxApp usercache | size cap for one LxApp cache directory; cleanup triggers at 80% high water and LRU-evicts down to 50% low water | disable usercache size enforcement |
141
+ | `dataMaxSizeMB` | 4096 | per LxApp userdata | max durable files for one LxApp | disable userdata size limit |
142
+ | `appStorageMaxSizeMB` | 16384 | whole LingXia-managed app storage | total userdata + usercache budget | disable app-wide storage limit |
143
+
144
+ `cacheMaxSizeMB` is per LxApp. Normal per-LxApp cleanup should not evict another LxApp's cache. Cross-LxApp cleanup happens only for app-wide storage pressure.
145
+
146
+ There is no fixed age cutoff for usercache. In particular, LingXia does **not** delete files merely because they have not been accessed for 7 days. Eviction is capacity-driven and uses least-recently-used ordering only after a cleanup trigger fires.
147
+
148
+ ## Cleanup Policy
149
+
150
+ Cleanup is triggered by runtime events, not by developers calling arbitrary cleanup code.
151
+
152
+ | Trigger | Scope | May Delete | Never Deletes |
153
+ | --- | --- | --- | --- |
154
+ | LxApp startup/open | stale temp sessions for that LxApp | old temp session dirs | current active temp session |
155
+ | Temp output registration/finalization | current runtime temp session | old unpinned temp files | active `.download-staging`, current keep file |
156
+ | LxApp destroy | current runtime temp session | current temp session dir | userdata |
157
+ | App startup maintenance | all LxApp usercache dirs | LRU usercache once over high water | userdata, temp staging |
158
+ | Usercache write | current LxApp usercache | LRU files in that LxApp cache once over high water | other LxApp caches in normal per-LxApp cleanup |
159
+ | `downloadFile({ filePath })` / FileManager managed writes under appStorage pressure | all LxApp usercache dirs | usercache across LxApps | userdata |
160
+ | LxApp uninstall | that LxApp storage | its userdata, usercache, KV storage, bundle | other LxApps |
161
+
162
+ Global invariants:
163
+
164
+ - LingXia may delete temp and usercache automatically.
165
+ - LingXia must not delete userdata automatically to satisfy quota.
166
+ - Quota failures are business errors, not internal runtime errors.
167
+ - Failed writes must not leave final partial files.
168
+
169
+ ## Temp Policy
170
+
171
+ Temp is session-scoped. Returned temp URIs are opaque and should not reveal filesystem layout.
172
+
173
+ Cleanup:
174
+
175
+ - stale sessions are removed when the LxApp opens
176
+ - current session temp is removed on runtime destroy best-effort
177
+ - size cleanup runs when temp files are returned or finalized
178
+ - OS may also clear app cache
179
+
180
+ Quota:
181
+
182
+ - each active runtime session uses `tempMaxSizeMB`
183
+ - size cleanup deletes oldest unpinned files first
184
+ - if cleanup cannot free enough space, LingXia deletes the current output and returns `TEMP_QUOTA_EXCEEDED`
185
+
186
+ ## User Cache Policy
187
+
188
+ Usercache is for regenerable data only. LxApps may explicitly place files there through FileManager when the file can be downloaded or generated again.
189
+
190
+ Cleanup modes:
191
+
192
+ - app-wide maintenance cleanup runs once on host process startup and scans `<app_data>/lingxia/usercache/*`
193
+ - per-LxApp opportunistic cleanup runs when that LxApp writes to usercache (`writeFile`, `copyFile`, `rename` into usercache); reads only refresh access metadata, they do not trigger cleanup
194
+ - appStorage pressure cleanup may delete usercache across LxApps when any FileManager write (userdata or usercache) would otherwise exceed `appStorageMaxSizeMB`
195
+
196
+ LRU high water / low water:
197
+
198
+ Per-LxApp cleanup is gated on the cache reaching the **high water mark** at 80% of `cacheMaxSizeMB`. For write-time cleanup, LingXia evaluates the projected size after the operation: `current usercache bytes + incoming write bytes - replaced destination bytes - removed source bytes`. A write that would push the cache to or above high water can therefore trigger cleanup even if the cache is currently below high water. Overwrites and moves are judged by their net growth, so replacing an existing cache file does not double-count the old file.
199
+
200
+ Once triggered, LingXia sorts eligible files by access time and deletes the oldest files first. The target is the **low water mark** at 50% of `cacheMaxSizeMB`; for write-time cleanup, LingXia evicts until `current bytes <= 50% - net incoming bytes`, so the cache lands near 50% after the pending write completes. Going deeper than just-under-cap prevents thrash: subsequent writes can fill back to 80% before another cleanup fires.
201
+
202
+ Setting `cacheMaxSizeMB: 0` disables size enforcement entirely; the cache is then only bounded by `appStorageMaxSizeMB`, the OS, or LxApp uninstall.
203
+
204
+ Deletion order and protections (once cleanup is triggered):
205
+
206
+ 1. skip protected files (`.lock`, `.part`, `.ok`) and data files with an active sibling `.lock`
207
+ 2. preserve active operation paths: the destination being written and any usercache source being copied or moved
208
+ 3. LRU-evict by access metadata, oldest atime first, until the low-water target is reached
209
+ 4. under appStorage or physical disk pressure, continue deleting eligible LRU usercache across LxApps until app storage fits or the requested physical bytes have been freed
210
+
211
+ Access-time semantics:
212
+
213
+ - access metadata is the file's atime; LingXia writes atime explicitly via `utimensat` rather than relying on the kernel
214
+ - atime is updated on FileManager reads (`readFile`, `readDir`, `stat`, `exists`, and `copyFile`/`rename` from a usercache source) and on WebView `lx://usercache` resource loads
215
+ - direct native reads that bypass these paths do not update atime
216
+ - on Android and other mounts that use `relatime`/`noatime`, automatic kernel atime updates are unreliable; the explicit touch path above is the source of truth
217
+ - a WebView page that keeps an asset in its internal resource cache will not re-hit the scheme handler, so the file's atime can go stale. The high water gate above prevents deletion as long as the cache is well under cap; once usage approaches `cacheMaxSizeMB`, stale-atime assets are the first LRU candidates. If a long-lived asset must survive cap pressure, write it to userdata or refresh atime explicitly with `fs.stat(path)` / `fs.exists(path)` at session start.
218
+ - newly written usercache files are touched after the write succeeds and are preserved during the immediate post-write cleanup pass, so normal quota cleanup should not delete the file that was just written. Usercache is still regenerable storage: later cleanup passes may evict it if it becomes the least-recently-used eligible file under capacity pressure.
219
+
220
+ Protection rules:
221
+
222
+ - do not recurse into symlink directories
223
+ - skip `.lock`, `.part`, `.ok`
224
+ - skip data files with an active sibling `.lock`
225
+ - when deleting a data file, delete its `.ok` marker and remove empty parent directories
226
+
227
+ If cleanup cannot make room for a cache write, return `USERCACHE_QUOTA_EXCEEDED`.
228
+
229
+ ## User Data Policy
230
+
231
+ Userdata is durable owner-private data.
232
+
233
+ Cleanup:
234
+
235
+ - explicit delete APIs
236
+ - LxApp uninstall
237
+ - app data clearing
238
+ - host-admin reset tools
239
+
240
+ LingXia must not apply age-based or LRU cleanup to userdata.
241
+
242
+ Write checks:
243
+
244
+ - `dataMaxSizeMB` applies to one LxApp userdata directory
245
+ - `appStorageMaxSizeMB` applies to all LingXia-managed userdata + usercache
246
+ - appStorage pressure may clean usercache before rejecting the write
247
+
248
+ Failure behavior:
249
+
250
+ - exceeding `dataMaxSizeMB` returns `USERDATA_QUOTA_EXCEEDED`
251
+ - exceeding `appStorageMaxSizeMB` after usercache cleanup returns `APP_STORAGE_QUOTA_EXCEEDED`
252
+ - existing userdata is not silently deleted
253
+
254
+ ## Physical Disk Pressure
255
+
256
+ Configured quotas are logical caps measured against `dataMaxSizeMB`,
257
+ `cacheMaxSizeMB`, and `appStorageMaxSizeMB`. The host device's physical
258
+ filesystem can run out of space well before any of these caps are reached
259
+ (low-end Android, near-full disks, shared volumes).
260
+
261
+ LingXia FileManager writes (`writeFile`, `copyFile`, `rename`) and
262
+ `downloadFile` finalization detect filesystem `ENOSPC` (and the
263
+ platform-equivalent `StorageFull` error) and run a recovery pass:
264
+
265
+ 1. delete LRU usercache files across all LxApps until the freed bytes cover
266
+ the incoming write (with 25% headroom, minimum 1 MiB)
267
+ 2. retry the write once
268
+
269
+ If the retry still fails with `ENOSPC`, the IO error is surfaced to the caller.
270
+ Recovery only deletes usercache; userdata is never touched. FileManager
271
+ recovery preserves active operation paths: the destination being written and
272
+ any source file being copied or moved.
273
+
274
+ This recovery is best-effort: if the device is full and there is no
275
+ regenerable cache to evict, the LxApp must surface the failure to the user.
276
+
277
+ ## Download Staging
278
+
279
+ `downloadFile` writes to staging before finalization.
280
+
281
+ Current physical staging location:
282
+
283
+ ```text
284
+ <app_cache>/lingxia/lxapps/temp/<lxapp_fingermark>/<session_id>/.download-staging/<task_id>
285
+ ```
286
+
287
+ Behavior:
288
+
289
+ - default temp downloads use a unique staging id per call, so identical URLs can download concurrently
290
+ - `filePath` downloads reserve the userdata destination while running or paused
291
+ - pause keeps staging so resume can continue
292
+ - cancel deletes staging
293
+ - failure deletes staging when possible
294
+ - success moves staging to temp or userdata final location
295
+
296
+ ## Storage Summary
297
+
298
+ ```text
299
+ tempFilePath -> lx://temp/<opaque_id>
300
+ short-lived, session/size scoped, physically under app cache
301
+
302
+ filePath -> lx://userdata/<path>
303
+ durable owner-private data, physically under app data
304
+
305
+ usercache -> lx://usercache/<path>
306
+ regenerable cache, physically under app data and owned by LingXia cleanup
307
+ ```
308
+
309
+ ## Rules for Developers
310
+
311
+ - Use temp files for immediate preview, upload, transform, or save flows.
312
+ - Use `fs.writeFile({ filePath: lx.env.USER_CACHE_PATH + "/..." })` for developer-generated regenerable files.
313
+ - Use `fs.copyFile` when a temp file must be copied into userdata or usercache.
314
+ - Use `fs.rename({ oldPath: tempFilePath, newPath: "lx://usercache/..." })` when a temp file should become auto-cleaned cache without a second copy.
315
+ - Use `downloadFile({ filePath })` only for durable userdata destinations.
316
+ - Do not pass `lx://usercache`, host download directories, or native paths to `downloadFile.filePath`.
317
+ - Do not store business-critical references to `tempFilePath`.
318
+
319
+ ## Rules for LingXia Internals
320
+
321
+ - Do not return `lx://usercache` as `tempFilePath`.
322
+ - Do not store default downloads in usercache.
323
+ - Keep temp URI values opaque.
324
+ - Keep usercache cleanup inside `lingxia-lxapp` cache management.
325
+ - Keep userdata outside automatic cleanup.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@lingxia/skill",
3
+ "version": "0.8.0",
4
+ "entry": "SKILL.md",
5
+ "syncedAt": "2026-05-25T06:51:06.410Z"
6
+ }