| name | lazycat-app-publisher |
| description | Use when converting Docker Compose or Docker Run apps to LazyCat LPK v2, publishing or updating LazyCat apps, copying Docker images to the LazyCat registry, selecting images in multi-service manifests, using lzc-publish, or configuring permissions, passwordless login, app interconnect, resources, run_as, and file picker integration. |
| preferences | [{"id":"add_root_to_public_path","name":"添加 / 到 public_path","description":"自动在生成的 manifest 中添加 \"/\" 到 application.public_path","type":"boolean","default":true},{"id":"manifest_multilingual","name":"Manifest 多语言支持","description":"在 manifest 文件中生成中英文双语 locales","type":"boolean","default":true},{"id":"simple_app_optimization","name":"简单应用优化","description":"简单应用跳过 lzc-deploy-params.yml 生成","type":"boolean","default":true},{"id":"auto_healthcheck","name":"自动健康检查","description":"自动为服务添加健康检查配置","type":"boolean","default":true},{"id":"auto_resource_limits","name":"自动资源限制","description":"自动为服务添加合理的资源限制","type":"boolean","default":true},{"id":"minimal_docs","name":"最简文档","description":"只生成 README.md,跳过其他 markdown 文件","type":"boolean","default":true},{"id":"background_task","name":"后台任务模式","description":"默认将 application.background_task 设为 true,适用于长时间运行的后台服务","type":"boolean","default":false},{"id":"package_prefix","name":"包名前缀","description":"应用包名默认前缀(community.lazycat.app、cloud.lazycat.app 或自定义)","type":"string","default":"cloud.lazycat.app"},{"id":"generate_compose_override","name":"生成 compose_override","description":"在 lzc-build.yml 中为不支持的字段生成 compose_override 部分","type":"boolean","default":true},{"id":"passwordless_login","name":"免密登录配置","description":"为有密码体系的应用自动配置免密登录(使用 injects)","type":"boolean","default":true}] |
LazyCat 应用发布助手
协助将 Docker Compose 文件和 Docker 命令转换为 LazyCat Cloud 应用配置,提供智能依赖分析和自动配置优化。
Reference Documents
OS Version Requirements
⚠️ LPK v2 格式强制要求:min_os_version: 1.5.0
LPK v2(tar 格式,包含 package.yml)是 lzcos v1.5.0+ 才支持的特性。如果使用 LPK v2 格式,必须设置 min_os_version: 1.5.0 或更高版本。
| Feature | Minimum Version | Notes |
|---|
LPK v2 / package.yml | v1.5.0 | 强制要求!新项目默认使用 |
多入口 application.entries | v1.4.3 | 多入口支持 - 主界面+管理后台等 ⭐ |
/lzcapp/documents (新路径) | v1.5.0 | 替代旧路径 /lzcapp/document |
permissions 声明 | v1.5.0 | 权限系统 |
services.[].healthcheck | v1.4.1 | 100% Docker 兼容,详见下方映射表 |
compose_override | v1.3.0 | For unsupported docker-compose params |
application.upstreams | v1.3.8 | Recommended over routes |
services.[].mem_limit, shm_size | v1.3.8 | Memory limits |
lzc-cli project workflow | lzc-cli v2.0.0+ | Required for LPK v2 |
应用间访问 .lzcx | v1.5.2 | app.<pkg>.lzcx 地址 ⭐ |
import_resources / resource_exports | v1.5.2 | Skill/MCP 导入导出 |
lzcapp.self_delegate / lzcapp.user_delegate | v1.5.2 | 应用间访问权限 |
run_as (UID/GID + owner 映射) | v1.6.0+ | 容器数字 UID/GID + /lzcapp 持久目录 owner 映射 ⭐ |
hidden_from_launcher | v1.5.3 | 从启动器隐藏应用入口 |
user.notify 权限 | v1.6.0 | 向用户发送通知 |
| 文件选择器拦截 | v1.5.0+ | 应用商店强制:有上传/下载必须接入 ⭐ |
版本决策表:
| 格式/特性 | 设置 min_os_version |
|---|
| LPK v2 (tar, package.yml) | 1.5.0 (强制) |
| LPK v1 (zip) | 无限制 |
使用 /lzcapp/documents | 1.5.0 |
使用 permissions | 1.5.0 |
使用 多入口 entries | 1.4.3 ⭐ |
使用 upstreams, mem_limit | 1.3.8 |
使用 应用间访问 .lzcx | 1.5.2 ⭐ |
| 使用 Skill/MCP 导入导出 | 1.5.2 ⭐ |
使用 run_as (UID/GID owner 映射) | 1.6.0+ ⭐ |
使用 hidden_from_launcher | 1.5.3 |
使用 user.notify 权限 | 1.6.0 |
| 文件选择器拦截(应用商店强制) | 1.5.0 ⭐ |
详见 references/version-features.md 和 references/strict-constraints.md
Conversion Workflow
Phase 1: 输入分析
输入: Docker Compose 文件 或 Docker Run 命令
输出: 服务分类结果(Internal/External),参数需求清单
Step 1.1: 解析源文件
用户提供 docker-compose.yml 或 docker run 命令
→ 解析 services、ports、volumes、environment、depends_on 等
⚠️ 检查点 - 源文件有效性验证:
| 验证项 | 验证方法 | 失败处理 |
|---|
| YAML 语法 | 尝试 YAML 解析 | 提示 YAML 格式错误及具体行号 |
| Dockerfile/image 有效性 | 检查 image 字段是否为空 | 要求用户提供有效镜像名称 |
| 端口格式 | ports 是否为数字端口 | 提示修正格式(非数字/范围超出) |
| volumes 路径 | 检查 volumes 格式 | 提示正确格式 /host:/container |
| depends_on 循环依赖 | 依赖图检测 | 提示循环依赖的服务列表 |
Fallback:如果输入无法解析,引导用户使用交互式 CLI 模式逐步配置。
Step 1.2: 服务分类
使用智能分析逻辑判断服务类型:
| 判断条件 | 类型 | 处理方式 |
|---|
| 有 healthcheck + 无外部端口 | Internal | 自动配置密码 |
| 有外部端口 | External | 用户配置 upstreams |
| 有密码体系 | 密码应用 | ⚠️ 检查点:确认是否配置免密登录 |
Step 1.3: 检查应用商店
⚠️ 检查点: 自动搜索应用商店,确认同名应用是否已存在
GET https://search.lazycat.cloud/api/v1/app?keyword={app_name}&size=48
如果找到匹配:
- 提示用户:「应用商店已存在同名应用 XXX,是否继续创建?」
- 用户确认后继续,否则终止
详见 references/intelligent-analysis.md
Phase 2: 配置生成
输入: 服务分类结果
输出: package.yml + lzc-manifest.yml + lzc-build.yml(LPK v2 格式)
前置检查:
lzc-cli --version || echo "lzc-cli 未安装,请参考 https://docs.lazycat.cloud/cli/install"
docker --version || echo "Docker CLI 未安装,手动提供镜像名称即可"
Step 2.1: 生成 package.yml(静态元数据)
⚠️ 检查点: 确认包名前缀(默认 cloud.lazycat.app)
⚠️ 检查点: 自动分析权限需求(根据 binds 路径自动声明)
权限自动分析逻辑:当 lzc-manifest.yml 中 services.*.binds 包含特定路径时,必须在 package.yml.permissions 中声明对应权限:
| binds 路径 | 必需权限 id | 说明 |
|---|
/lzcapp/documents | document.private | 应用文稿目录,必须声明 |
/lzcapp/documents/${uid} | document.private | 用户隔离文稿目录 |
/lzcapp/run/mnt/home | document.read + document.write | 兼容旧路径(v1.7.0+ 需授权) |
/lzcapp/media | media.read 或 media.write | 媒体目录访问 |
检测流程:
解析 services.*.binds 列表
→ 匹配路径前缀
→ 自动生成 permissions.required 或 permissions.optional
→ 提示用户确认权限声明
⚠️ 检查点: 如果检测到 /lzcapp/documents 挂载,必须暂停并告知用户:
- 「检测到应用文稿目录
/lzcapp/documents 挂载,需要声明 document.private 权限」
- 「该权限允许应用使用私有文稿目录,实际数据按用户隔离存放」
⚠️ 检查点: 确认 author 来源(按以下优先级):
| 优先级 | 来源 | 规则 |
|---|
| 1 | 用户明确指定 | 使用用户输入的作者名 |
| 2 | GitHub URL | 从 homepage 提取 GitHub 用户名/组织名 |
| 3 | 默认生成 | 使用 应用名 + Team |
Author 自动填入示例:
| homepage | 提取结果 |
|---|
https://github.com/dani-garcia/vaultwarden | dani-garcia |
https://github.com/blinkospace/blinko | blinkospace |
https://nextcloud.com (非 GitHub) | Nextcloud Team |
详见 references/intelligent-analysis.md 的 Author Auto-Fill Logic 章节
package: cloud.lazycat.app.myapp
version: 1.0.0
name: MyApp
description: "应用描述"
min_os_version: 1.5.0
homepage: https://github.com/user/repo
author: user
Step 2.2: 生成 lzc-manifest.yml(运行结构)
⚠️ 检查点: 确认路由方式(upstreams 或 ingress)
application:
subdomain: myapp
upstreams:
- location: /
backend: http://web:8080/
services:
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD={{.INTERNAL.db_password}}
Step 2.3: 生成 lzc-build.yml(构建配置)
manifest: ./lzc-manifest.yml
pkgout: ./
icon: ./icon.png
Step 2.4: 配置免密登录(密码应用必须)
⚠️ 检查点: 如果应用有密码体系,必须询问用户免密登录方案
| 方案 | 适用场景 | 配置复杂度 |
|---|
| simple-inject-password | 固定账号/部署参数提供 | 简单 |
| 三阶段联动 | 用户首次创建账号 | 中等 |
| Basic Auth Header | 上游服务验证 | 简单 |
详见 references/passwordless-login.md
Step 2.5: 配置资源导出(可选,v1.5.2+)
如果应用需要向微服提供 Skill 或 MCP 资源,在 lzc-build.yml 中配置 resource_exports:
resource_exports:
- kind: skills
source: ./resources/skills
- kind: mcp-providers
source: ./resources/mcp-providers
检查点: 如果应用提供 Skill/MCP,需确认 permissions 声明:
- 应用需访问其他应用 →
lzcapp.user_delegate
- 应用只访问自己 →
lzcapp.self_delegate
使用 .lzcx 地址访问:
http://app.<目标包名>.lzcx<endpoint>
详见 references/resource-export.md 和 references/app-interconnect.md
Phase 3: 打包发布
输入: 配置文件已生成
输出: LPK 包 + 发布到应用商店
Step 3.0: 既有项目版本更新(推荐脚本)
已有 LPK 项目升级版本时,优先使用通用脚本。先按 manifest 镜像数量选模式:
| 场景 | 命令 | 结果 |
|---|
| 单镜像 | scripts/lzc-release-update.sh 1.2.3 --source-template 'ghcr.io/acme/app:{version}' | 自动更新唯一 service |
| 多镜像 | scripts/lzc-release-update.sh 1.2.3 --service web --source-template 'ghcr.io/acme/web:{version}' | 只更新 web |
| 发布审核 | scripts/lzc-release-update.sh 1.2.3 --service web --publish --changelog '更新到 1.2.3' | 构建后调用 lzc-publish |
scripts/lzc-release-update.sh 1.2.3 --service web --source-image ghcr.io/acme/web:1.2.3
多镜像规则:
- 如果
lzc-manifest.yml 只有一个 services.*.image,脚本可以自动更新。
- 如果有多个镜像,必须通过
--service <name> 指明要更新哪个服务。
- 脚本会把选择记到
.lazycat-release.env;下次使用记忆值时仍会显式打印正在更新的 service。
- 镜像复制优先调用 fish 中的
lzc-copy-image,不存在时回退到 lzc-cli appstore copy-image。
- 发布是显式动作:只有
--publish 或配置 publish=1 时才调用 lzc-publish <lpk> <changelog> [lang]。
- 如果
copy-image 没有返回 registry.lazycat.cloud/...,停止并保留原 manifest,不要猜测镜像地址。
Step 3.1: 本地验证
lzc-cli project release -o app.lpk
lzc-cli lpk info app.lpk
⚠️ 检查点: 确认包内容正确后再发布
Step 3.2: 发布到应用商店
lzc-cli appstore publish app.lpk
Quick Start
Convert Docker Compose
输入格式: Docker Compose YAML 文件路径或内容
输出格式: LPK v2 包目录(package.yml + lzc-manifest.yml + lzc-build.yml)
Convert this docker-compose.yml to LazyCat app format:
[provide docker-compose.yml content or file path]
示例输入:
services:
web:
image: nginx:latest
ports:
- "80:80"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: mypassword
预期输出:
├── package.yml # 包元数据
├── lzc-manifest.yml # 运行配置
├── lzc-build.yml # 构建配置
└── icon.png # 应用图标(需用户提供)
Convert Docker Run
输入格式: Docker Run 命令字符串
输出格式: 同上(LPK v2)
Convert this docker run command to LazyCat app:
docker run -d -p 8080:80 -e APP_ENV=production --name myapp nginx:latest
解析规则:
- -p → application.upstreams 或 ingress
- -e → services.*.environment
- -v → services.*.binds
- --name → application.subdomain
Publish to Store
输入格式: LPK 包文件路径
输出: 发布到应用商店,返回应用 ID
Help me publish this application to LazyCat Cloud:
[provide LPK package path or directory]
步骤:
1. 验证 LPK 包结构
2. 确认应用商店无同名应用
3. 执行 lzc-cli appstore publish
4. 返回发布结果
Update Existing App Version
输入格式: 版本号 + 上游镜像(或镜像模板)
输出: 更新后的 package.yml、lzc-manifest.yml、LPK 包,可选发布
从注释自动推导(推荐)
在 lzc-manifest.yml 的 image: 行上方添加注释,记录上游镜像:
services:
web:
image: registry.lazycat.cloud/xxx/yyy:hash123
然后只需指定版本号:
scripts/lzc-release-update.sh 1.2.3
scripts/lzc-release-update.sh 1.2.3 --service web
推导逻辑:
- 优先使用
--source-image 或 --source-template
- 如果都没有,尝试从
image: 行上方的注释推导
- 注释格式必须是
# org/repo:tag 或 # registry/org/repo:tag
- 版本号会被替换为指定的新版本
其他方式
scripts/lzc-release-update.sh 1.2.3 --service web --source-template 'ghcr.io/acme/web:{version}'
scripts/lzc-release-update.sh 1.2.3 --source-image ghcr.io/acme/web:1.2.3
scripts/lzc-release-update.sh 1.2.3 --service web --publish --changelog '更新到 1.2.3'
当 manifest 中有多个镜像时,先列出所有候选镜像并要求明确选择 service;不要静默批量替换,也不要默认替换第一个镜像。
Configure Multi-Entry Points
使用场景: 应用需要多个入口(如主界面 + 管理后台)
⚠️ 版本要求: lzcos v1.4.3+,需设置 min_os_version: 1.4.3
application:
subdomain: myblog
entries:
- id: main
title: "Blog"
path: /
- id: admin
title: "Admin"
path: /admin
locales:
en:
entries.main.title: "Blog"
entries.admin.title: "Admin Console"
zh:
entries.main.title: "博客"
entries.admin.title: "管理后台"
字段说明:
id: 入口唯一标识
title: 显示名称(支持 locales)
path: 入口路径(如 /、/admin)
prefix_domain: 可选,域名前缀(如 admin → admin-myblog.xxx.lzcapp)
详细说明见 references/advanced-features.md
智能分析逻辑
服务分类
def classify_service(service_config):
has_healthcheck = 'healthcheck' in service_config
has_external_ports = 'ports' in service_config
if has_healthcheck and not has_external_ports:
return 'INTERNAL'
else:
return 'EXTERNAL'
| 服务 | 健康检查 | 外部端口 | 类型 | 配置方式 |
|---|
| PostgreSQL | ✅ | ❌ | Internal | 自动生成密码 |
| Redis | ✅ | ❌ | Internal | 自动生成密码 |
| Web App | ❌ | ✅ | External | 用户配置 |
参数模板
| 场景 | 推荐函数 | 示例 |
|---|
| 内部服务密码 | {{.INTERNAL.xxx}} | {{.INTERNAL.db_password}} |
| 用户必须配置 | {{.U.xxx}} | {{.U.jwt_secret_key}} |
| 系统域名 | {{.S.AppDomain}} | {{.S.AppDomain}} (不含协议) |
| 稳定密钥 | {{ stable_secret "seed"}} | {{ stable_secret "api_key"}} |
| 运行时变量 | ${LAZYCAT_*} | ${LAZYCAT_APP_ID} |
| 条件渲染 | {{if}}/{{else}}/{{end}} | 根据参数动态配置 |
⚠️ 模板语法规范(重要)
字段名包含特殊字符时必须使用 index 语法,否则使用点语法:
{{ .U.login_user }}
{{ .U.target }}
{{ .INTERNAL.db_password }}
{{ index .U "listen.port" }}
{{ index .U "api.endpoint" }}
{{ index .U "login_user" }}
{{ index .U "target" }}
规则总结:
- 字段名不含
. → 使用 {{ .U.xxx }} 点语法
- 字段名含
. → 使用 {{ index .U "xxx" }} index 语法
详见 references/intelligent-analysis.md 和 references/go-template-conditional.md ⭐
Setup Wizard 约束
生成 lzc-deploy-params.yml 时,严格按照官方规范:
允许的字段(完整列表)
- ✅
id - 参数 ID(推荐小写英文+下划线)
- ✅
type - 仅支持 bool、lzc_uid、string、secret
- ✅
name - 参数名称(英文)
- ✅
description - 参数描述(英文)
- ✅
optional - 是否可选
- ✅
default_value - 默认值,支持 $random(len=5)
- ✅
hidden - 字段生效但不在界面中渲染
❌ 禁止使用的字段
以下字段不存在,禁止生成:
- ❌
placeholder - 不存在
- ❌
regex - 不存在
- ❌
regex_message - 不存在
- ❌
min - 不存在
- ❌
max - 不存在
- ❌
type: number - 不支持
- ❌
type: integer - 不支持
- ❌
type: email - 不支持
- ❌
type: url - 不支持
- ❌
required - 应使用 optional: false
- ❌
value - 应使用 default_value
约束输入的正确做法
需要约束输入格式时,在 description 中说明:
params:
- id: port_number
type: string
name: "Port Number"
description: "Service port number (1-65535, default 8080)"
default_value: "8080"
详见 references/strict-constraints.md
Package Layout Constraints
生成应用文件时,默认使用 LPK v2 格式(需要 lzcos v1.5.0+ 和 lzc-cli v2.0.0+):
lzc-build.yml 允许的字段
- ✅
manifest - manifest.yml 文件路径(必需)
- ✅
pkgout - LPK 输出路径(必需)
- ✅
icon - 应用图标路径(必需,512x512 PNG)
- ✅
contentdir - 内容目录(可选)
- ✅
pkg_id - 覆盖 package.yml.package(可选)
- ✅
pkg_name - 覆盖 package.yml.name(可选)
- ✅
envs - 构建期变量(可选,KEY=VALUE 数组)
- ✅
buildscript - 构建脚本(可选)
- ✅
images - 内嵌镜像构建(可选)
- ✅
compose_override - compose 覆盖(可选)
❌ lzc-build.yml 禁止的字段
以下字段应放在其他文件:
- ❌
package, version, name, description, min_os_version, locales, author, license, homepage → 应在 package.yml
- ❌
application, services, subdomain → 应在 lzc-manifest.yml
- ❌
dockerfile, context → 应在 images 配置内部
LPK v2 默认布局(推荐)
.
├── lzc-build.yml # 构建配置
├── lzc-build.dev.yml # 开发态覆盖(可选)
├── package.yml # 静态包元数据(LPK v2 必需)
├── lzc-manifest.yml # 运行结构定义
└── icon.png # 应用图标
package.yml 必须包含:
package - 包 ID
version - 版本
name, description - 名称和描述
min_os_version: 1.5.0 - LPK v2 格式强制要求
lzc-manifest.yml 只保留运行结构:
application - 应用配置
services - 服务配置
ext_config - 扩展配置
usage - 使用说明
详见 references/strict-constraints.md
Docker → LazyCat Mapping
| Docker Feature | LazyCat Equivalent |
|---|
ports (HTTP) | application.upstreams ⭐ |
ports (TCP/UDP) | application.ingress |
volumes | services.*.binds |
environment | services.*.environment |
depends_on | services.*.depends_on |
command | services.*.command (必须是字符串!) |
shm_size | services.*.shm_size |
healthcheck | services.*.healthcheck (v1.4.1+, 100%兼容) ⭐ |
Healthcheck 快速参考
v1.4.1+ 关键变更:
- ✅
healthcheck(无下划线)- 100% Docker Compose 兼容
- ❌
health_check(带下划线)- 已废弃,迁移时需改字段名并添加时间单位
services:
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
⚠️ Rule 1: 不要自动添加 healthcheck(容器可能缺少 curl/wget)
详见 references/healthcheck.md
Volume Path Mapping
- Docker:
/some/path:/container/path
- LazyCat:
/lzcapp/var/...:/container/path 或 /lzcapp/cache/...:/container/path
文档路径变更(v1.5.0+):
| 版本 | 路径 | 说明 |
|---|
| v1.5.0+ | /lzcapp/documents | 新路径,推荐 |
| < v1.5.0 | /lzcapp/document | 废弃,仅兼容 |
需要文档访问权限时,需在 manifest 中声明:
ext_config:
enable_document_access: true
详见 references/quick-reference.md
Passwordless Login Configuration
⚠️ 重要:所有自带密码体系的应用都必须配置免密登录!
LazyCat 微服要求应用提供良好的用户体验,用户不应该每次都手动输入密码。
三种常用方案
方案一:部署参数 + simple-inject-password(简单场景)
适用于登录账号固定或由部署参数提供的场景:
params:
- id: login_user
type: string
name: "Login User"
description: "Default login username"
default_value: "admin"
- id: login_password
type: secret
name: "Login Password"
description: "Default login password"
default_value: "$random(len=20)"
application:
injects:
- id: login-autofill
when:
- /login
- /signin
do:
- src: builtin://simple-inject-password
params:
user: "{{ .U.login_user }}"
password: "{{ .U.login_password }}"
方案二:三阶段联动(高级场景)
适用于用户首次创建账号、后续可能修改密码的场景:
injects:
- id: capture-password
on: request
when:
- /api/login
- /api/setup
do: |
const payload = ctx.body.getJSON();
ctx.flow.set("pending_user", payload.username);
ctx.flow.set("pending_pass", payload.password);
- id: commit-password
on: response
when:
- /api/login
- /api/setup
do: |
if (ctx.status >= 200 && ctx.status < 300) {
ctx.persist.set("saved_user", ctx.flow.get("pending_user"));
ctx.persist.set("saved_pass", ctx.flow.get("pending_pass"));
}
- id: autofill-login
when:
- /login
do:
- src: builtin://simple-inject-password
params:
user:
$persist: saved_user
password:
$persist: saved_pass
方案三:Basic Auth Header 注入
适用于上游服务使用 Basic Auth 的场景:
application:
injects:
- id: inject-basic-auth
on: request
auth_required: false
when:
- /api/*
do: |
ctx.headers.set("Authorization", "Basic " + ctx.base64.encode("admin:password"));
关键注意事项
on: request/response 不能使用 hash 规则(如 /#login)
- request 阶段不要直接写入
persist,先存入 flow,response 成功后再持久化
- 显式指定选择器,确保特殊命名页面也能正确填充
详见 references/passwordless-login.md
最佳实践
✅ Do - LPK v2 完整示例(推荐,lzcos v1.5.0+)
⚠️ 重要:LPK v2 格式必须设置 min_os_version: 1.5.0
package: cloud.lazycat.app.myapp
version: 1.0.0
name: MyApp
description: "My application"
min_os_version: 1.5.0
author: "Developer"
license: MIT
homepage: https://example.com
locales:
zh:
name: "我的应用"
description: "我的应用描述"
en:
name: "My App"
description: "My application"
permissions:
required:
- net.internet
- document.private
optional:
- document.read
- document.write
application:
subdomain: myapp
upstreams:
- location: /
backend: http://myapp:8080/
injects:
- id: login-autofill
when:
- /login
do:
- src: builtin://simple-inject-password
params:
user: "{{ .U.login_user }}"
password: "{{ .U.login_password }}"
services:
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD={{.INTERNAL.db_password}}
healthcheck:
test:
- CMD-SHELL
- pg_isready -U postgres
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
binds:
- /lzcapp/var/db:/var/lib/postgresql/data
manifest: ./lzc-manifest.yml
pkgout: ./
icon: ./icon.png
params:
- id: login_user
type: string
name: "Login User"
description: "Default login username"
default_value: "admin"
optional: true
- id: login_password
type: secret
name: "Login Password"
description: "Default login password"
default_value: "$random(len=20)"
optional: true
locales:
zh:
login_user:
name: "登录用户"
description: "默认登录用户名"
login_password:
name: "登录密码"
description: "默认登录密码"
❌ Avoid - 常见错误
发布更新反例黑名单:
| ❌ 不要做 | 后果 | ✅ 正确做法 |
|---|
对 image: .* 做全局 sed 替换 | 多镜像项目会更新错服务 | 用 scripts/lzc-release-update.sh --service <name> |
| 多镜像 manifest 未确认 service 就复制/发布 | 用户无法判断到底更新了哪个容器 | 先列出 services.*.image,要求显式选择或读取 .lazycat-release.env 并打印 |
| 构建后默认发布 | 未审核的 LPK 进入应用商店流程 | 只有用户要求或传 --publish 时调用 lzc-publish |
copy-image 解析不到 registry 地址仍继续 | manifest 指向不存在或旧镜像 | 立即停止,保留文件,提示用户贴出完整输出 |
只改 manifest 不改 package.yml 版本 | 构建产物版本混乱,更新审核失败 | 同步更新 package.yml 顶层 version |
package: cloud.lazycat.app.myapp
version: 1.0.0
name: MyApp
min_os_version: 1.3.8
package: myapp
version: 1.0.0
subdomain: myapp
params:
- id: port
type: number
min: 1
max: 65535
placeholder: "8080"
regex: "^[0-9]+$"
lzc-sdk-version: "0.1"
services:
postgres:
health_check:
application:
routes:
- /=http://myapp:8080/
admin_only: true
application:
public_path:
- /
environment:
- PASSWORD=secret123
binds:
- /lzcapp/document:/data
services:
app:
binds:
- /lzcapp/documents:/data
services:
redis:
command: ["redis-server", "--requirepass", "mypass"]
application:
subdomain: myapp
开发工作流
环境要求
LPK v2 格式(默认,推荐):
- lzcos v1.5.0+
- lzc-cli v2.0.0+ (
npm install -g @lazycatcloud/lzc-cli@2.0.0)
安装 CLI:
npm install -g @lazycatcloud/lzc-cli
npm install -g @lazycatcloud/lzc-cli@2.0.0
快速决策表
| 目标 | 使用命令 |
|---|
| UI 热更新 | project deploy + npm run dev |
| 后端代码修改 | project deploy + project sync --watch + project exec |
| 构建发布包 | project release |
主要命令
lzc-cli project create myapp -t hello-vue
lzc-cli project deploy
lzc-cli project deploy --release
lzc-cli project sync --watch
lzc-cli project exec /bin/sh
lzc-cli project log -f
lzc-cli project release -o app.lpk
lzc-cli lpk info app.lpk
lzc-cli appstore publish app.lpk
详见 references/dev-workflow.md 和 references/cli-reference.md
LPK v1 到 v2 迁移
使用转换脚本
提供 Python 脚本将现有 LPK v1 包转换为 v2 格式:
python scripts/lpk_v1_to_v2.py app.lpk
python scripts/lpk_v1_to_v2.py app.lpk new-app.lpk
python scripts/lpk_v1_to_v2.py app.lpk ./output/
转换器做什么:
- 解压 LPK v1(zip 格式)
- 将
manifest.yml 拆分为:
package.yml - 静态元数据(package, version, name, description 等)
manifest.yml - 运行结构(application, services, ext_config)
- 重新打包为 LPK v2(tar 格式)
依赖要求:
pip install pyyaml
手动迁移
简单场景可以手动迁移:
unzip app.lpk -d temp/
cat > package.yml << 'EOF'
package: your.app.id
version: 1.0.0
name: Your App
description: App description
min_os_version: 1.3.8
EOF
tar -cf app-v2.lpk package.yml manifest.yml content.tar.gz META/
injects allows you to inject scripts into browser, request, or response phases.
| Phase | Runtime | Use Case |
|---|
on=browser | Browser | DOM manipulation, autofill |
on=request | lzcinit | Header injection, routing |
on=response | lzcinit | CORS/CSP modification |
application:
injects:
- id: dev-proxy
on: request
auth_required: false
when:
- "/*"
do:
- src: |
if (ctx.dev.id && ctx.net.reachable("tcp", "127.0.0.1", 3000, ctx.net.via.client(ctx.dev.id))) {
ctx.proxy.to("http://127.0.0.1:3000", {
via: ctx.net.via.client(ctx.dev.id),
use_target_host: true,
});
}
详见 references/injects.md
Critical Rules
🚨 TOP 9 CRITICAL RULES
执行转换时,遇到以下情况必须暂停并询问用户。每条 Rule 附带「失败后果」和「恢复路径」,便于快速定位问题。
❌ Rule 1: DO NOT Auto-Add Healthcheck
如果原始 docker-compose.yml 没有 healthcheck,不要自动添加。容器可能缺少 curl/wget 工具。
🔴 CHECKPOINT: 发现服务无 healthcheck 时,询问用户:
| 失败后果 | 恢复路径 |
|---|
| 容器内无 curl/wget → healthcheck 永远失败 → 服务反复重启 | 删除 healthcheck 字段,或改用容器原生命令(如 pg_isready、redis-cli ping) |
❌ Rule 2: binds Only Supports Directories, Not Files
使用 contentdir + setup_script 替代文件挂载:
contentdir: ./content
services:
web:
setup_script: |
cp /lzcapp/pkg/content/nginx.conf /etc/nginx/nginx.conf
🔴 CHECKPOINT: 发现文件挂载(如 /path/file.conf:/etc/file.conf)时,提示用户转换为 contentdir 方案。
| 失败后果 | 恢复路径 |
|---|
binds 挂载文件 → lzcinit 拒绝启动 → 应用无法部署 | 改用 contentdir + setup_script 在容器启动时复制文件 |
❌ Rule 3: LPK v2 MUST Set min_os_version: 1.5.0
LPK v2 格式(tar + package.yml)是 v1.5.0+ 才支持的特性:
package: cloud.lazycat.app.myapp
version: 1.0.0
min_os_version: 1.5.0
🔴 CHECKPOINT: 若用户需要兼容旧版本(< v1.5.0),提示切换到 LPK v1 格式(zip)。
| 失败后果 | 恢复路径 |
|---|
| 低版本 lzcos 无法解析 tar 格式 → 安装失败 | 降低 min_os_version 并切换到 LPK v1(zip 格式),或要求用户升级 lzcos |
❌ Rule 4: DO NOT Generate Invalid Fields
严格按官方规范生成配置文件,禁止生成不存在的字段:
- lzc-build.yml: 禁止
package, version, name, subdomain 等
- lzc-deploy-params.yml: 禁止
placeholder, regex, min, max, type: number
🔴 CHECKPOINT: 每次生成配置后,对照 strict-constraints.md 验证字段有效性。
| 失败后果 | 恢复路径 |
|---|
| lzcinit 解析 manifest 报错 → 应用无法安装 | 删除无效字段,严格按 strict-constraints.md 的字段白名单生成 |
❌ Rule 5: All Password Apps MUST Have Passwordless Config
如果应用自带密码体系,必须配置免密登录:
- 使用
simple-inject-password 自动填充
- 或使用三阶段联动记录用户修改的密码
🔴 CHECKPOINT: 检测到密码字段(如 PASSWORD, SECRET_KEY, ADMIN_PASSWORD)时,强制询问:
- 「检测到密码体系,请选择免密登录方案:[简单自动填充 / 三阶段联动 / Basic Auth]」
- 用户确认后才能继续生成配置
⚠️ Rule 6: usage 字段只支持字符串形式(防止幻觉)
usage 是简单字符串字段,不支持嵌套结构:
usage: "简单使用说明"
usage: |
多行使用说明
第二行内容
usage:
intro: "应用介绍"
steps:
- "第一步"
tips: "提示"
🔴 CHECKPOINT: 生成 usage 时,只生成字符串内容,不要生成任何嵌套字段结构。详见 strict-constraints.md 的 usage 字段章节。
| 失败后果 | 恢复路径 |
|---|
| lzcinit 解析 manifest 报错或 usage 不显示 | 将嵌套结构展开为纯字符串,用换行符分隔段落 |
⚠️ Rule 7: 免密登录 when 路径必须与实际登录页匹配
when 条件的路径判定不正确会导致免密登录完全失效。
🔴 CHECKPOINT: 配置免密登录时,必须先确认实际登录页路径:
| 步骤 | 操作 |
|---|
| 1 | 在浏览器中打开应用,进入登录页 |
| 2 | 复制浏览器地址栏的完整路径 |
| 3 | 根据实际路径配置 when 条件 |
| 4 | 验证自动填充是否生效 |
常见应用登录页路径对照表(见 passwordless-login.md):
| 应用 | 实际登录页路径 |
|---|
| Jellyfin | /web/#/login.html |
| WordPress | /wp-login.php |
| Portainer | /#/login |
| 失败后果 | 恢复路径 |
|---|
| inject 不命中登录页 → 密码框不自动填充 → 用户每次手动输入 | 根据实际 URL 修正 when 路径,hash 路径(如 /#login)仅 browser 阶段支持 |
错误示例:
injects:
- id: login
when:
- /login
⚠️ Rule 8: binds 路径必须匹配权限声明(自动分析)
binds 中使用特定路径时,必须在 package.yml.permissions 中声明对应权限:
| binds 路径前缀 | 必需权限 id | 声明位置 |
|---|
/lzcapp/documents | document.private | permissions.required |
/lzcapp/documents/${uid} | document.private | permissions.required |
/lzcapp/run/mnt/home | document.read + document.write | permissions.required |
/lzcapp/media | media.read 或 media.write | 按读写需求 |
🔴 CHECKPOINT: 检测到以下路径时,自动添加权限声明并提示用户确认:
# 检测流程
1. 解析 services.*.binds
2. 匹配路径前缀
3. 自动生成 permissions
4. 提示用户:「检测到应用文稿挂载,已自动添加 document.private 权限」
| 失败后果 | 恢复路径 |
|---|
| 缺少权限声明 → lzcinit 拒绝挂载该路径 → 应用启动后无法读写文件 | 在 package.yml 的 permissions.required 中补充对应权限 id |
正确示例:
services:
app:
binds:
- /lzcapp/documents:/data
permissions:
required:
- net.internet
- document.private
错误示例:
services:
app:
binds:
- /lzcapp/documents:/data
🚨 Rule 9: 有上传/下载功能必须接入文件选择器拦截
应用商店强制要求:有上传/下载功能的应用必须接入懒猫网盘自动拦截文件选择器,未接入无法上架。
检测条件:应用包含以下任一特征时触发此规则:
<input type="file"> 文件上传
showOpenFilePicker() / showSaveFilePicker() API
<a download> 下载链接
- 文件导入/导出功能
🔴 CHECKPOINT: 检测到文件操作时,必须配置文件选择器拦截:
application:
injects:
- id: open-save-chooser
on: browser
when:
- /*
do:
- src: file:///lzcapp/pkg/content/lazycat-injects/lzc-file-chooser-inject.js
同时需要将 lzc-file-chooser-inject.js 放入包的 content 目录。
| 失败后果 | 恢复路径 |
|---|
| 应用商店审核拒绝 → 无法上架 | 下载 lzc-file-chooser-inject.js 放入 content/lazycat-injects/,在 manifest 添加 inject 声明 |
详见 references/file-picker-intercept.md
⚠️ 边界条件与异常处理
每条异常采用「触发条件 → 一线修复 → 仍失败兜底」三段式,避免只写"处理方案"导致执行时无 fallback。
构建阶段异常
| 异常类型 | 触发条件 | 一线修复 | 仍失败兜底 |
|---|
| 镜像拉取失败 | lzc-cli project release 报错 timeout/404 | 检查镜像源是否可访问,提示用户配置代理 | 使用 registry.lazycat.cloud 镜像源,或 embed:<alias> 内嵌镜像构建 |
| manifest 验证失败 | 字段格式错误,lzcinit 拒绝解析 | 对照 strict-constraints.md 逐字段检查,删除无效字段 | 生成最小可用 manifest(仅保留 subdomain + 一个 upstream),让用户手动补充 |
| 图标文件缺失 | icon.png 不存在 | 提示用户准备 512x512 PNG 图标 | 使用占位图标继续构建(在 README 中标注需替换) |
| 权限不足 | 文件写入失败 | 检查目标目录权限,提示 chmod | 改为输出到当前用户有权限的目录(如 ~/output/) |
网络阶段异常
| 异常类型 | 触发条件 | 一线修复 | 仍失败兜底 |
|---|
| 应用商店 API 不可达 | GET search.lazycat.cloud 超时 | 提示用户检查网络,可跳过检查继续创建 | 进入离线模式,用户需手动确认无重名后继续 |
| 镜像源不可达 | docker pull 超时或 502 | 推荐 registry.lazycat.cloud 镜像源 | 配置 compose_override 使用备用源(dockerproxy.net 等),或内嵌镜像 |
发布更新阶段异常
| 异常类型 | 触发条件 | 一线修复 | 仍失败兜底 |
|---|
| 多镜像选择缺失 | lzc-manifest.yml 有多个 services.*.image 且未提供 --service | 列出 service/image 清单,要求用户指定 | 若 .lazycat-release.env 有 service,使用该值并显式打印 |
| 镜像地址解析失败 | copy-image 输出不含 registry.lazycat.cloud/... | 停止更新,要求用户贴完整输出 | 用 COPY_IMAGE_OUTPUT 复现解析,或手动传 --skip-copy --source-image <registry-image> |
| 发布参数缺失 | 用户要求发布但没有 changelog | 默认使用 更新到 <version> 并打印 | 用户要求自定义时重跑 --changelog '<message>' |
| LPK 文件名不匹配 | 构建输出不是 <package>-v<version>.lpk | 用 --lpk <file> 指定实际产物 | 检查 lzc-build.yml 的 pkgout/pkg_id 配置 |
运行时异常
| 异常类型 | 触发条件 | 一线修复 | 仍失败兜底 |
|---|
| 应用启动失败 | 健康检查超时(start_period 内未就绪) | 检查 start_period 是否过短,增大后重试 | 进入容器手动排查:lzc-cli project exec /bin/sh,查看进程状态和日志 |
| 502/404 错误 | HTTP 响应 502 或 404 | 通过 injects response 阶段拦截返回自定义页面 | 检查 upstream backend 地址是否正确,确认服务容器是否在运行 |
| 服务依赖超时 | depends_on 的服务未在超时内就绪 | 检查依赖服务的 healthcheck 和 start_period | 移除 depends_on 改用应用层重试逻辑(如 wait-for-it 脚本) |
兼容性警告
| 场景 | 问题描述 | 处理方案 |
|---|
| 系统降级 (v1.3.8+) | v1.3.8+ 降级后应用数据不可用 | ⚠️ 强制警告:告知用户升级后不可降级 |
| user 字段格式 (v1.4.2) | services.xx.user: 1000 会报错 | 必须使用引号:user: "1000" |
| environment 空值 (v1.4.2) | environment: 空数组会报错 | 不要留空,删除字段或填写值 |
| compose_override 使用 | 需联系官方备案 | ⚠️ 检查点:使用前提示用户联系开发者群或客服 |
| 文档路径 (v1.5.0) | /lzcapp/document 废弃 | 使用新路径 /lzcapp/documents |
| run_as 冲突 (v1.6.0) | application.run_as 与 user 同时使用 | ⚠️ 强制警告:run_as 不得与 user 在同一级别同时使用 |
| run_as + setup_script 冲突 (v1.6.0) | services.<name>.run_as 与 setup_script 同时使用 | ⚠️ 强制警告:service 级别 run_as 不得与 setup_script 同时使用 |
恢复流程
当构建或运行失败时,按以下步骤恢复:
lzc-cli project log -f
lzc-cli project exec /bin/sh
cat /lzcapp/run/manifest.yml
自定义错误页面与请求拦截(injects 方式)
⚠️ handlers.error_page_templates 已废弃,请使用 injects 的 response 阶段实现自定义错误页面。
使用场景
injects 的 on: response 阶段适用于以下场景:
场景一:帮助文件跳转 — 应用包含使用说明/帮助文档,用户访问不存在的页面时自动跳转到帮助页(代替旧版 handler+JS 重定向方案)。
application:
injects:
- id: redirect-404-to-help
on: response
auth_required: false
when:
- "/*"
do: |
if (ctx.status === 404) {
ctx.response.send(302, "", { location: "/help" });
return;
}
upstreams:
- location: /help
backend: file:///lzcapp/pkg/content/web/index.html
- location: /
backend: http://yourapp:8080/
public_path:
- /
- /help
配置要点:帮助页路径(/help)须同时出现在 upstreams 和 public_path 中,否则无法访问。
真实项目参考:Ask4Me(easychen/ask4me)使用此模式实现 404 → 帮助页自动跳转,详见 references/real-world-examples.md 的示例 7。
场景二:自定义错误页面 — 应用后端返回 502/503 时,显示友好的错误提示而非浏览器默认白页:
application:
injects:
- id: custom-error-pages
on: response
auth_required: false
when:
- "/*"
do: |
if (ctx.status === 502) {
ctx.response.send(200, { "content-type": "text/html; charset=utf-8" }, `
<html>
<body>
<h1>应用暂时不可用</h1>
<p>请稍后再试</p>
</body>
</html>
`);
return;
}
场景三:维护模式 — 在特定路径下拦截所有请求,返回维护公告页面。
更多 injects 用法(CORS 修正、Basic Auth 注入、开发代理等)见 references/injects.md。完整的帮助文档跳转示例见 references/developer-guide.md。
应用商店检查(自动)
每当用户请求转换或创建应用时,技能会自动检查懒猫应用商店中是否已存在同名应用:
GET https://search.lazycat.cloud/api/v1/app?keyword={app_name}&size=48
如果找到匹配应用,会提示用户是否继续创建。
偏好设置
| 偏好 | 默认值 | 说明 |
|---|
| 添加 / 到 public_path | true | 自动添加 "/" 到 public_path |
| Manifest 多语言 | true | 生成中英文 locales |
| 简单应用优化 | true | 简单应用跳过 params 生成 |
| 自动健康检查 | true | 自动添加 healthcheck |
| 自动资源限制 | true | 自动添加资源限制 |
| 最简文档 | true | 只生成 README.md |
| 后台任务模式 | false | 设置 background_task: true |
| 包名前缀 | cloud.lazycat.app | 包名前缀 |
| 生成 compose_override | true | 生成 compose_override |
| 免密登录 | true | 为密码体系应用自动配置免密登录 |
示例
示例 1:简单 Web 应用
输入(docker-compose.yml):
services:
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
app:
image: myapp:latest
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@postgres:5432/app
- SECRET_KEY=${SECRET_KEY}
depends_on:
- postgres
ports:
- "3000:3000"
输出(智能转换):
services:
postgres:
environment:
- POSTGRES_PASSWORD={{.INTERNAL.db_password}}
healthcheck: {...}
app:
environment:
- DATABASE_URL=postgresql://postgres:{{.INTERNAL.db_password}}@postgres:5432/app
- SECRET_KEY={{.U.secret_key}}
更多示例见 references/docker-compose-examples.md 和 references/real-world-examples.md
参考资料