All checks were successful
技能自动化发布 / release (push) Successful in 9s
新增 DATA_SOURCES.md、data-sources 示例与双后端 connection 骨架;CLI 与宿主 UI 可读写同一库。要求匠厂 Host ≥ 6.9.28。 Co-authored-by: Cursor <cursoragent@cursor.com>
195 lines
8.2 KiB
Markdown
195 lines
8.2 KiB
Markdown
# 用户数据目录与文件路径黄金标准
|
||
|
||
> 本文是 skill **读写本地文件** 的权威约定。凡涉及输入、输出、缓存、RPA 存证等路径,复制模板后须遵守本文;Agent 实现业务 skill 时也应先读本文。
|
||
|
||
---
|
||
|
||
## 1. 根目录
|
||
|
||
所有技能持久化数据(配置、数据库、业务文件)默认落在:
|
||
|
||
```text
|
||
{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill_slug}/
|
||
```
|
||
|
||
示例:
|
||
|
||
```text
|
||
D:\jiangchang-data\12500\download-video-baidu-haokan\
|
||
```
|
||
|
||
实现:`util.runtime_paths.get_skill_data_dir()`。
|
||
|
||
**禁止**把技能自己写入的文件落到:
|
||
|
||
- Agent / 宿主 **workspace**(进程 CWD)
|
||
- 技能安装目录(含 `.openclaw/skills/...`)
|
||
- 未在本文声明的任意相对 CWD 路径
|
||
|
||
---
|
||
|
||
## 2. 标准子目录树
|
||
|
||
用户可见 · **文件目录**(数据管理「输入 / 输出」文件浏览器只挂这两个):
|
||
|
||
| 子目录 | 方向 | 用途 | 默认 helper |
|
||
|--------|------|------|-------------|
|
||
| `input/` | 上行 | 用户/Agent 放入的待处理材料(urls.txt、batch.json、CSV、产品表…) | `get_input_dir()` |
|
||
| `output/` | 下行 | 技能/宿主交给用户拿走的结果(视频、回单、xlsx/csv/json…) | `get_output_dir()` |
|
||
|
||
用户可见 · **HTML 报告页**(宿主扫描挂到技能下,**不是** input/output 文件浏览器):
|
||
|
||
| 子目录 | 用途 | 默认 helper |
|
||
|--------|------|-------------|
|
||
| `html/` | 技能写出的静态 `.html` 报告;宿主一层扫描 `*.html`,侧栏预览 / 右键重新生成 | `get_html_dir()` |
|
||
|
||
内部 / 可选目录(**不上侧栏**):
|
||
|
||
| 子目录 | 用途 | 默认 helper |
|
||
|--------|------|-------------|
|
||
| `uploads/` | 待发布到外部平台的 staging(可选) | `get_uploads_dir()` |
|
||
| `cache/` | 可再生缓存(页面快照、解析中间结果) | `get_cache_dir()` |
|
||
| `temp/` | 单次任务 scratch,任务结束可删 | `get_temp_dir()` |
|
||
| `rpa-artifacts/` | RPA 失败截图、录屏中间产物 | `get_rpa_artifacts_dir(batch_id)` |
|
||
| `videos/` | RPA 录屏成片 MP4 | `get_videos_dir()` |
|
||
| `{skill_slug}.db` | SQLite(默认本地库) | `get_db_path()` |
|
||
| `data-sources.json` | 声明式数据源(可选;不配则宿主自动扫描 `.db`) | 用户目录根;格式见 [`../references/DATA_SOURCES.md`](../references/DATA_SOURCES.md) |
|
||
| `.env` | 用户配置(含可选 `MYSQL_DSN`) | `config.get_env_file_path()` |
|
||
|
||
复制后若业务不需要某目录,可不创建;**但一旦写入该类文件,必须走对应 helper**。
|
||
|
||
**开发者硬规则:**
|
||
|
||
- 用户/Agent **放进来**的文件 → `input/`(`get_input_dir()`)
|
||
- 技能/宿主 **交给用户拿走**的文件 → `output/`(`get_output_dir()`)
|
||
- 可浏览的 **HTML 报告页** → `html/`(`get_html_dir()` / `service.html_page_service`);**不要**把整页报告塞进 `output/` 指望侧栏预览
|
||
- 禁止再引入业务「文件浏览器」第三目录(如旧的 `downloads` / `imports` / `exports` 三分法);`html/` 是报告页扫描面,不是第三种文件浏览器
|
||
- 缓存、临时、RPA 录屏用 `cache` / `temp` / `videos`,不要当成用户可见的「输出目录」
|
||
|
||
宿主约定(与本模板对齐):
|
||
|
||
- 工具栏「导入」**UI 一律**打开宿主「导入数据」面板(默认从本技能 `input/` 选文件);「导出」默认写到 `output/`
|
||
- **Action 表单**里 `display_type`/`format` 为 `file` 或 `directory` 时:宿主原生对话框默认打开**当前技能** `{skill_data}/input`;若字段声明可选 **`pathRoot`**(相对技能数据根,如 `input/库存数据`),则打开该子目录。禁止在技能里写用户 ID 或绝对盘符。契约见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「本地路径选择与 pathRoot」
|
||
- 若表声明了 `replaces: import`,确认导入后由技能 CLI 写库(见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「`replaces`」);仅有 `replaces: create` 时宿主可按行调 create(兼容)
|
||
- 数据管理扫描两处 HTML(都只扫一层):安装包 `assets/html/pages/*.html` 是装完就有的壳子页;`{skill_data}/html/*.html` 是快照,「重新生成」调用 Action id **`render-html`**。页面里用 `jiangchang.call` 调本技能已声明的命令。详见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「HTML 页面报告」
|
||
|
||
---
|
||
|
||
## 3. 两类路径
|
||
|
||
### A. 技能拥有(skill-owned)
|
||
|
||
技能**创建或更新**的文件:下载结果、导出报表、失败截图、队列 sidecar 等。
|
||
|
||
- **默认**必须在 `{skill_data_dir}/<子目录>/` 下。
|
||
- 用户可拿走的业务产出 → **`output/`**。
|
||
- **必须**通过 `resolve_data_path()` 或 `get_*_dir()` 解析,**禁止** `os.path.abspath(config.get(...))` 相对 CWD。
|
||
|
||
### B. 用户显式输入(user-owned)
|
||
|
||
用户在 CLI / Agent 对话中给出的**已有文件**绝对路径,例如「发布这个视频 `D:\素材\x.mp4`」。
|
||
|
||
- 技能**只读**,路径可以是用户磁盘任意位置。
|
||
- 使用 `resolve_input_path()`:绝对路径原样;**相对路径**解析为 `{skill_data_dir}/input/<相对路径>`,**不**相对 workspace/CWD。
|
||
|
||
---
|
||
|
||
## 4. 配置项(`.env`)
|
||
|
||
可选覆盖项(**留空则用默认子目录**):
|
||
|
||
```ini
|
||
# ── 数据目录子路径(见 development/DATA_PATHS.md)──
|
||
# 相对路径相对于 {JIANGCHANG_DATA_ROOT}/{USER_ID}/{slug}/,不是 workspace
|
||
# SKILL_INPUT_DIR=input
|
||
# SKILL_OUTPUT_DIR=output/videos
|
||
# SKILL_UPLOAD_DIR=uploads
|
||
# SKILL_CACHE_DIR=cache
|
||
# SKILL_TEMP_DIR=temp
|
||
```
|
||
|
||
| 规则 | 说明 |
|
||
|------|------|
|
||
| 未配置 | `{skill_data_dir}/{default_subdir}` |
|
||
| 相对值 `output/videos` | `{skill_data_dir}/output/videos` |
|
||
| 绝对值 `D:\nas\output` | 高级用户/NAS 挂载,原样使用 |
|
||
| **禁止** `./outputs/...` | 会随 CWD 漂移,不得作为 `.env.example` 活跃配置行 |
|
||
|
||
业务专属路径 env key 须带 **`SKILL_` 前缀**(或技能 slug 前缀),避免污染全局命名空间。
|
||
|
||
---
|
||
|
||
## 5. 代码用法
|
||
|
||
```python
|
||
from util.runtime_paths import (
|
||
get_input_dir,
|
||
get_output_dir,
|
||
resolve_data_path,
|
||
resolve_input_path,
|
||
list_resolved_data_paths,
|
||
)
|
||
|
||
# 下行产出(A 类)——视频、报表等
|
||
dest = os.path.join(get_output_dir(), f"{vid}.mp4")
|
||
|
||
# 或带 env 覆盖
|
||
out_dir = resolve_data_path("SKILL_OUTPUT_DIR", "output")
|
||
|
||
# CLI -i urls.txt(B 类,相对路径 → input/)
|
||
manifest = resolve_input_path(args.input_path)
|
||
|
||
# health 诊断
|
||
paths = list_resolved_data_paths()
|
||
```
|
||
|
||
**反模式(禁止):**
|
||
|
||
```python
|
||
# ❌ 相对 CWD,Agent 从 workspace 执行时会写错盘
|
||
os.path.abspath(config.get("DOWNLOAD_OUTPUT_DIR"))
|
||
|
||
# ❌ .env.example 里写 ./outputs/videos
|
||
DOWNLOAD_OUTPUT_DIR=./outputs/videos
|
||
|
||
# ❌ 再拆 downloads / imports / exports 三个用户目录
|
||
```
|
||
|
||
---
|
||
|
||
## 6. health / config-path
|
||
|
||
`health` 或 `config-path` 建议输出 `list_resolved_data_paths()` 中的路径,便于排查「文件写到哪里去了」。展示路径须与 `run` 实际使用的一致。
|
||
|
||
---
|
||
|
||
## 7. 与 RPA 存证的关系
|
||
|
||
- 失败截图、录屏中间文件:`rpa-artifacts/{batch_id}/`(见 [`RPA.md`](RPA.md) §5.1)
|
||
- 录屏成片:`videos/`(见 [`RPA.md`](RPA.md) §5.3)
|
||
|
||
业务 `output/` 与 RPA `videos/` **分工不同**:前者是业务产出文件,后者是操作过程录屏。
|
||
|
||
---
|
||
|
||
## 8. 测试
|
||
|
||
- 单元测试须用 `IsolatedDataRoot`,断言路径落在临时数据根下。
|
||
- `tests/test_runtime_paths.py` 覆盖 `resolve_data_path` / `resolve_input_path`。
|
||
- `POLICY-DATA-PATH-001`:禁止 `.env.example` 中 `./` 路径;禁止业务代码 `abspath(config.get(...))`。
|
||
|
||
### 卸载技能与数据保留
|
||
|
||
宿主**卸载技能只删除安装目录**(如 `.openclaw/skills/{slug}`),**不会**删除 `{JIANGCHANG_DATA_ROOT}/{user_id}/{skill_slug}/` 下的 SQLite、输入输出与日志。重装会复用同一用户库;schema 迁移须在 `init_db()` 中幂等完成(见 [`../references/SCHEMA.md`](../references/SCHEMA.md)「表增删改与退役」)。
|
||
|
||
---
|
||
|
||
## 9. 相关文档
|
||
|
||
| 文档 | 内容 |
|
||
|------|------|
|
||
| [`RUNTIME.md`](RUNTIME.md) | 数据根、共享 runtime、编码 |
|
||
| [`CONFIG.md`](CONFIG.md) | `.env` bootstrap 与三层优先级 |
|
||
| [`RPA.md`](RPA.md) | rpa-artifacts / videos 存证 |
|
||
| [`POLICY_MATRIX.md`](POLICY_MATRIX.md) | POLICY-DATA-PATH-001 |
|