# Remote Project Bundle Spec

Status: active

本文定义通过 `/openclaw-employee-config` 下发 zip 项目包，并在客户端专家页直接拉起执行的最小规范。

目标是让后续项目例如抖音、小红书都按同一约定交付：

1. 云端配置返回下载地址。
2. 客户端下载 zip 并解压到本地 workspace。
3. 专家页显示该项目。
4. 用户在专家页发送一句自然语言。
5. 客户端自动把请求路由到该项目的 `workspace-entry`。
6. 项目在本地完整执行自己的自动化流程。

## 1. 适用范围

本规范适用于当前桌面客户端的 bundle 路径：

- 启动时或配置同步时，客户端根据员工配置中的远程 bundle 下载地址拉取 zip。
- 客户端将 zip 安装到本地 `user-data/projects/<projectId>`。
- 专家页根据 `project.json` 中的声明决定该项目如何被展示和执行。

当前实现里，bundle 来源仍然是员工配置中的 skill 下载地址，不是单独的 `project_bundle_url`。

## 2. 服务端需要提供什么

服务端不需要生成本地运行态文件。

服务端只需要保证：

- 员工配置中包含可下载的 zip 地址。
- zip 内容满足本文规定的目录结构和文件契约。
- 同一个项目更新时，继续使用同一个 `projectId`。
- 如果 zip 内容变了，下载地址、配置版本或远端 freshness 元数据能够触发客户端重新拉取。

服务端不需要在 zip 内预生成这些内容：

- `updatedAt`
- `ready`
- `boundSkillIds`
- `memory/`
- `sessions.json`
- `session-messages/`
- `manifests/active-project.json`
- `manifests/project-bundles.json`

这些都由客户端本地生成或维护。

## 3. 一个 zip 里只能有一个项目

当前客户端按“一个 zip = 一个项目”处理。

支持两种 zip 根结构：

### 结构 A：zip 根目录直接就是项目目录

```text
douyin/
  project.json
  workspace_entry.py
  README.md
  tools/
  ...
```

### 结构 B：zip 根目录包含 `projects/`

```text
projects/
  douyin/
    project.json
    workspace_entry.py
    README.md
    tools/
    ...
```

如果采用 `projects/` 结构，则 `projects/` 下必须且只能有一个项目目录。

## 4. 必须文件

### 4.1 `project.json`

每个项目包必须包含 `project.json`。

最小建议字段如下：

```json
{
  "id": "douyin",
  "name": "Douyin Automation",
  "description": "Project-isolated Douyin workflow.",
  "version": "0.1.0",
  "projectType": "workspace-package",
  "platform": "douyin",
  "capabilities": ["publish", "workflow"],
  "requires": ["playwright"],
  "workspaceAutomation": {
    "runtime": "python",
    "script": "workspace_entry.py",
    "args": [
      "--prompt",
      "{prompt}",
      "--prepared-prompt",
      "{preparedPrompt}",
      "--project-root",
      "{projectRoot}"
    ]
  },
  "bundlePackaging": {
    "excludePaths": [
      "__pycache__"
    ]
  },
  "defaultEntry": {
    "id": "workspace-entry",
    "type": "workspace-entry",
    "intentAliases": ["douyin", "抖音", "发帖", "发布"],
    "capabilities": ["publish", "workflow"]
  },
  "entries": [
    {
      "id": "workspace-entry",
      "type": "workspace-entry",
      "intentAliases": ["douyin", "抖音", "发帖", "发布"],
      "capabilities": ["publish", "workflow"]
    }
  ],
  "workspaceEntryEnabled": true
}
```

字段说明：

- `id`: 必须稳定。后续同一项目升级时不要改。
- `name`: 专家页展示名的基础来源。
- `platform`: 建议填写，便于展示名和路由归类。
- `workspaceAutomation`: 如果希望专家页一句话直接拉起项目，这是必须的。
- `defaultEntry.type = "workspace-entry"`: 推荐配置，保证默认走项目入口而不是普通聊天。
- `entries`: 推荐显式声明，便于后续扩展 skill entry 或更多 intent alias。
- `bundlePackaging.excludePaths`: 只用于你在本地打 zip 时过滤不该进包的目录。

### 4.2 `workspace_entry.py`

如果项目需要通过专家页直接执行自动化流程，必须提供入口脚本。

当前客户端会把这些参数传给入口脚本：

- `--prompt`
- `--prepared-prompt`
- `--project-root`

入口脚本必须：

1. 在项目根目录内执行。
2. 能从自然语言 prompt 里解析出项目需要的执行参数。
3. 调用项目自身的主流程。
4. 通过 `QJC_WORKSPACE_EVENT\t<json>` 输出 started/status/delta/completed/error 事件。

推荐最小事件协议：

```text
QJC_WORKSPACE_EVENT\t{"type":"started","runId":"..."}
QJC_WORKSPACE_EVENT\t{"type":"status","stage":"...","label":"...","detail":"..."}
QJC_WORKSPACE_EVENT\t{"type":"delta","runId":"...","textDelta":"...","fullText":"..."}
QJC_WORKSPACE_EVENT\t{"type":"completed","runId":"...","content":"..."}
QJC_WORKSPACE_EVENT\t{"type":"error","runId":"...","message":"..."}
```

### 4.3 `README.md`

不是强制必需，但强烈建议有。

原因：

- 客户端会把项目 README 注入到项目上下文。
- 专家页模型在做项目路由和上下文理解时会受益。

## 5. 推荐目录结构

建议以后所有项目都收敛到下面这个结构：

```text
<project-id>/
  project.json
  workspace_entry.py
  README.md
  AGENTS.md
  tools/
  skills/
  cron/
  memory/
```

说明：

- `tools/`: 项目自己的脚本和模块。
- `skills/`: 可选。需要共享 skill 时放这里。
- `cron/`: 可选。需要共享 cron 时放这里。
- `memory/`: 所有可变运行态都应放这里。

## 6. 运行态文件必须放到 `memory/`

这是当前规范里最重要的一条。

客户端在 bundle 更新时，会整体替换项目目录，只保留以下本地状态：

- `sessions.json`
- `session-messages/`
- `memory/`

这意味着任何不在 `memory/` 下的可变状态，在下一次 bundle 替换时都可能丢失。

因此所有项目都必须遵守：

- 浏览器登录态放在 `memory/`
- Cookie / token / 本地缓存放在 `memory/`
- 调试截图和 HTML dump 放在 `memory/`
- 运行结果文件放在 `memory/`
- 生成图片、临时文件、下载文件放在 `memory/`

不建议把这些运行态写在项目根目录：

- `.env`
- `generated_images/`
- `openclaw_runs/`
- `openclaw_debug/`
- `*_profile/`

如果项目必须写环境文件，建议写成：

```text
memory/project.env
```

并由代码显式从这里读取。

## 7. 当前客户端会自动补哪些内容

bundle 安装完成后，客户端会自动：

- 重写 `project.json` 中的 `id`
- 重写 `project.json` 中的 `name`
- 回写 `updatedAt`
- 回写 `ready = true`
- 回写 `boundSkillIds`
- 创建 `memory/`
- 创建默认 session
- 创建 `sessions.json`
- 创建 `session-messages/`

因此 zip 内不需要预先包含这些运行态内容。

## 8. 专家页能跑起来的前提

如果以后抖音项目也按这个规范做，那么在当前客户端里，拉取后可以在专家页跑起来，但有两个前提。

### 前提 A：项目走 `workspace-entry`

也就是：

- `project.json` 里声明了 `workspaceAutomation`
- `defaultEntry` 或 `entries` 明确指向 `workspace-entry`
- `workspace_entry.py` 能处理自然语言 prompt

### 前提 B：项目依赖已经包含在当前 bundled runtime 里

当前 bundled runtime 不会根据 zip 自动安装额外 Python 依赖。

也就是说：

- `project.json.requires` 目前只是元数据，不会自动触发安装。
- 如果项目依赖一个 runtime 里没有的 Python 包，项目仍然会启动失败。

当前已确认的 bundled runtime Python 依赖包括：

- `openpyxl`
- `pandas`
- `requests`
- `beautifulsoup4`
- `lxml`
- `pypdf`
- `python-docx`
- `charset-normalizer`
- `pyyaml`
- `pillow`
- `python-dotenv`
- `playwright`

如果新项目例如抖音项目只依赖这些现有能力，那么它可以直接按本规范打包并在专家页执行。

如果它需要新的 Python 依赖，则必须先扩 runtime，而不是只改项目 zip。

## 9. 对项目作者的约束

最终用户不会修改项目代码。

所以项目包必须满足：

- zip 下载后可直接运行
- 首次初始化逻辑由项目自己完成
- 不依赖用户手工创建目录
- 不依赖用户手工改 `project.json`
- 不依赖用户手工改 Python 代码

可以接受的首次使用动作只有这类：

- 专家页发一句话
- 首次登录某个站点
- 首次授权浏览器

不可以接受的动作：

- 让用户进项目目录改路径
- 让用户自己补脚本
- 让用户手工建状态目录
- 让用户自己 pip install

## 10. 新项目交付检查清单

在交付任何新项目 zip 前，至少检查以下内容：

1. zip 中只有一个项目。
2. `project.json` 存在且 `id` 稳定。
3. `workspaceAutomation` 指向的脚本真实存在。
4. 专家页意图词已经写进 `defaultEntry.intentAliases` 或 `entries.intentAliases`。
5. 所有可变状态都写入 `memory/`。
6. 项目不依赖当前 runtime 中缺失的 Python 包。
7. 项目首次运行所需的浏览器/登录/配置流程已经内置在代码里。
8. 用真实 bundle 路径做过一次 `cloud zip -> 专家页 -> 自动化执行` 验证。

## 11. 推荐的最小模板

```text
douyin/
  project.json
  workspace_entry.py
  README.md
  AGENTS.md
  tools/
    run.py
    publish.py
  memory/
```

其中：

- `run.py` 是项目自己的完整自动化主流程。
- `workspace_entry.py` 只负责把专家页自然语言转换成 `run.py` 的结构化调用。
- 所有本地状态都进入 `memory/`。

## 12. `xhs` 当前状态

`workspace/xhs` 已经完成了“专家页可直接拉起”的主体改造：

- 有 `project.json`
- 有 `workspaceAutomation`
- 有 `workspace_entry.py`
- 真实专家页链路已经验证通过

但它仍然有一部分运行态还写在项目根目录，不完全符合本规范的最终状态。

后续如果要把 `xhs` 作为标准模板，建议再补一轮整理，把这些目录迁移到 `memory/`：

- `xhs_profile`
- `generated_images`
- `openclaw_runs`
- `openclaw_debug`
- 项目级环境文件

## 13. 最终结论

以后抖音项目可以按这份规范来做。

只要同时满足下面两点：

- zip 结构和 `workspace-entry` 契约满足本文要求
- 项目依赖不超出当前 bundled runtime 能力

那么客户端在拉取配置并同步 zip 后，就可以在专家页通过对话直接把项目跑起来。
