adep — AgentDeploy CLI
面向终端开发者的 AgentDeploy 命令行工具:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 全栈工程(functions/ + web/ 前端) 一站式闭环。
adep 让你在一个项目目录里完成从脚手架 → 本地调试 → 部署上线 → 数据与资源运维的完整流程。它复用平台运行时内核(@adep/runtime),本地与线上的执行语义一致,离线、不装 Docker 也能开发。
目录
安装
# 通过 npm 全局安装
npm install -g @adep/cli
# 或使用 bun
bun add -g @adep/cli
# 发布前本地调试:从仓库根目录
bun run packages/cli/src/index.ts --help
要求:Node ≥ 22.19,或 Bun ≥ 1.3(Bun 可免编译直接运行 TS,推荐用于本地开发)。 安装后运行
adep --help查看全部可用命令。
快速开始
# 1. 登录平台(打开浏览器用网页授权;凭据按 server 分槽保存于 ~/.adep/credentials.d/,权限 0600)
adep login
# 无浏览器环境(SSH / 无桌面):只打印验证码与链接,手工在浏览器里完成授权
adep login --no-browser
# 或不走浏览器,直接用邮箱密码登录(脚本 / 老平台)
adep login -s https://platform.example.com -e [email protected] -p '***'
# 2. 初始化一个函数项目
adep init my-app --template function
# 3. 进入项目,本地调试(监听 functions/,支持热重载)
cd my-app
adep dev
# → http://127.0.0.1:8787/hello
# 4. 部署上线(全栈:函数 + 前端)
adep publish
三步即可把本地 functions/hello.ts 与 web/ 前端发到线上并得到公网访问地址。
只发布函数:adep publish --only functions;只发布前端:adep publish --only frontend。
子命令
所有子命令都支持全局 --json 标志,以机器可读的 JSON 信封输出(适合 Agent / 脚本调用):
adep --json whoami
# → {"ok":true,"command":"whoami","data":{"email":"...","server":"..."}}
失败时返回退出码 1 + {"ok":false,"command":"...","error":{"code":"...","message":"..."}}。
adep login / whoami / logout
adep login 缺省走浏览器网页授权(RFC 8628 设备授权流):CLI 向平台要一个设备码并把
<平台>/device?user_code=XXXX 打开,你在网页上确认「批准」后 CLI 自动换回会话并落盘凭据。
adep login # 打开浏览器;终端会同时打印验证码与链接
adep login --no-browser # 不开浏览器:手工访问打印出来的链接(SSH / 无桌面)
adep login -s <url> # 指定平台(缺省 ADEP_SERVER,再缺省 https://adep.top)
adep login -e <邮箱> -p <密码> # 邮箱密码直登,不发起浏览器流程(CI / 老平台)
adep whoami # 查看当前身份(读凭据并向平台确认会话仍有效)
adep logout # 通知平台注销并清除本地凭据
- 判定「打不开浏览器」的三种情况:
--no-browser、ADEP_NO_BROWSER=1、stdout 不是 TTY(CI)。 三种都不会尝试打开浏览器,但都会打印验证码与链接——命令仍可完成登录。 --json模式下交互提示(验证码 / 链接)写 stderr,stdout 恒为恰好一个 JSON 对象。- 失败语义:用户拒绝 →
LOGIN_DENIED;验证码过期 →DEVICE_CODE_EXPIRED; 平台没有设备授权端点(老版本)→LOGIN_UNSUPPORTED(提示改用-e/-p)。失败不写凭据。 - 凭据形态:浏览器登录落
authType: "bearer"(发Authorization: Bearer), 密码直登落authType: "cookie";旧凭据缺该字段按 cookie 兼容。 - 凭据按 server 分槽保存(
~/.adep/credentials.d/<host>.json+current指针, 权限 0600):SaaS 与本地自建位等多环境先后登录互不覆盖——登录哪个环境, 后续命令就连哪个环境的凭据,谁也不会被静默替换;旧版单文件~/.adep/credentials在首次读写时自动迁入对应槽并删除。同 server 重复登录只覆盖自己的槽(输出会注明 替换了哪个邮箱的凭据)。
adep init
初始化项目(脚手架)。默认 fullstack 模板:生成与 Web IDE 项目同一结构的全栈骨架
(functions/ 云函数 + web/ 标准 Vite 前端工程 + database/schema.sql):
| 模板 | 说明 |
|---|---|
fullstack | 全栈骨架:functions/ + web/(Vite + Vue 3)+ database/schema.sql(默认) |
function | 仅云函数:单个示例函数 functions/hello.ts |
empty | 仅项目骨架(adep.config.ts + 类型提示入口,不生成示例产物) |
adep init my-app # 默认 fullstack 模板(与 Web IDE 项目同构)
adep init my-app -t function # 仅云函数
adep init my-app -t empty # 空模板
adep init --list # 从平台 GET /api/v1/templates 列出可用模板
adep init --list -s https://platform.example.com
adep templates publish -t fullstack # 上传平台托管模板包(需登录;模板目录须含 template.json)
模板来源(CLI-023):adep init 优先下载平台托管模板包(GET /api/v1/templates/:code/package,
缓存到 ~/.adep/templates/<code>-<version>.json);平台无包 / 下载失败 / 离线时自动回退本地内置模板。
模板包为 JSON 文件映射,与本地内置模板共用同一套 {{var}} 渲染管线。
产物结构(fullstack 模板,CLI-014:vite 工程根在项目根目录):
my-app/
├── package.json # 应用定义:@adep/cli + 前端依赖同根声明 + scripts(dev/build/site:deploy/serve/deploy/doctor)+ engines
├── vite.config.ts # vite 配置:vue 插件 + adep() 云函数插件;build.outDir = site/
├── index.html # vite 入口(引用 /web/src/main.ts)
├── adep.config.ts # 项目配置:name / template / functionsDir / functions_prefix(默认 /api)
├── tsconfig.json # 云函数 TypeScript 编辑器配置(strict + noEmit)
├── adep.d.ts # 云函数上下文环境类型声明(ctx: AdepContext 补全)
├── functions/
│ ├── hello.ts # 示例函数(deploy 后经 /api/hello 触发)
│ └── README.md
├── web/src/ # 前端源码(Vue 3;与 Web IDE 的 web/ 工程共用同一份 src/)
│ ├── main.ts
│ └── App.vue
├── site/ # vite build 构建产物(站点托管目录,adep hosting deploy site 上传;已 gitignore)
├── database/
│ └── schema.sql # 数据库结构(占位;表由平台控制台 / adep db 管理)
├── README.md
└── .gitignore # 已忽略 node_modules/ data/ .adep/ site/
根 package.json:与导出的离线部署包(adep export / 平台自托管导出)的应用定义同构——
声明 @adep/cli 版本依赖(值来自 CLI 自身发布版本,不硬编码)与常用 scripts:
pnpm dev(= vite,全栈开发:前端 HMR + 云函数代理)/ pnpm build(= vite build → site/)/
pnpm site:deploy(= adep hosting deploy site --spa)/ pnpm serve / pnpm deploy / pnpm doctor。
全栈开发(vite + 云函数代理):pnpm dev 启动 vite 后,@adep/cli/vite 插件在 vite 进程内
启动 adep dev(模拟运行时,函数热重载 / .env.local / cloud.* 全可用),并把
/{functions_prefix}/* 请求代理到云函数——默认前缀 /api(与线上函数路由对齐),
前端 fetch('/api/hello') 即调用 functions/hello.ts。
functions_prefix(云函数路由前缀):adep.config.ts 的 functions_prefix(默认 /api):
adep dev访问路径/{prefix}/{fnName}(如/api/hello);vite 插件按同一前缀代理。adep serve为/api/{prefix}/{fnName}(前缀拼接在/api/之后)。- 留空(旧项目):
adep dev为/{fnName},adep serve为/api/{fnName}; vite 插件在配置为空时强制/api并同步覆盖 dev 路由,保证代理与路由一致。 - 边界:只影响本地 HTTP 入口路由;函数互调
ctx.cloud.invoke('name')与线上部署路径 按函数名,不随前缀变化(与线上同构)。
前端源码(web/src/):与 Web IDE 内 web/ 工程的 src/ 完全同构(同一份模板真相源);
vite 工程根在项目根目录(vite.config.ts / index.html / 根 package.json),构建产物输出到
site/(站点托管目录),pnpm site:deploy 上传部署。
类型提示:adep init 产出的项目自带 tsconfig.json 与 adep.d.ts,用 VSCode 打开即可在
functions/ 下获得 AdepContext / ctx.cloud.* 补全,无需本机安装 @adep/types。
函数签名写 export default async (ctx: AdepContext) => {...} 即可。
目录已存在时会拒绝覆盖(fail loud),不会静默改写你的代码。
adep dev
本地调试:监听 functions/(以及 .env.local / adep.config.ts)变化并热重载,默认监听 http://127.0.0.1:8787/<fnName>,冷启动 ≤ 1.5s,文件变更到可被调用 ≤ 300ms。
adep dev # 默认端口 8787
adep dev -p 3000 # 指定端口
adep dev --db sim # 回到纯模拟数据面(.adep/sim/db.json)
adep dev --db sqlite --db-file var/dev.sqlite # 自定义库文件
adep dev --schema database/schema.sql # 启动时灌 schema(与 serve 同一套语义)
-
模拟运行时:默认使用进程内模拟运行时(
@adep/runtime+ 本地模拟器),不连云端、不装 Docker、断网也能开发——只替换传输与持久化,不改写函数语义。 -
数据库面与
adep serve同源:缺省--db sqlite(真实 SQLite 库文件.adep/data/project.sqlite,schema.sql 全量生效、视图 / 唯一索引可用;运行时不支持或打开失败时自动降级sim并告警),--db sim回到内置模拟引擎。三个旗标(--db/--db-file/--schema)与adep serve共用同一份实现,两条命令在同一目录、同一环境下必然给出同一个数据面。 -
能力注入:自动装配
cloud.db/cloud.storage/cloud.realtime,语义与线上一致。 -
环境变量:读取
.env.local/.env的键值,注入执行沙箱(process.env[KEY])。 -
能力边界:启动时打印一行边界清单(如下),提示本地与线上的差异:
-
定时/事件触发器只登记,本地以手工 HTTP 调用替代;
-
realtime 为单进程内存广播,线上多实例语义不同;
-
本地不执行计量 / 配额门禁,不计费。
-
[adep] dev server listening on http://127.0.0.1:8787(Ctrl-C 退出)
[adep] curl 示例:curl http://127.0.0.1:8787/hello
...
adep serve
开发态服务器:把函数(/api/{fn})、/healthz、静态托管与组件预览合并到一个进程里常驻,默认只绑回环地址,是本地开发服务器而非生产服务器。自托管部署包内用它指向包内 web/ 站点。
adep serve # 默认 127.0.0.1:8787(PORT / HOST 环境变量可覆盖)
adep serve -p 9000 # 指定端口
adep serve --static web # 指定静态托管目录(缺省 public/)
adep serve --db sqlite # 真实 SQLite 库文件(缺省 .adep/data/project.sqlite)
adep serve --db sqlite --db-file var/local.sqlite # 指定库文件
-
缺省值优先级为「命令行旗标 > 环境变量(
PORT/HOST/ADEP_SERVE_STATIC_DIR/ADEP_SERVE_DB/ADEP_SERVE_DB_FILE)> 内置缺省」,容器编排注入的PORT/HOST不会被命令行缺省盖掉。 -
绑定非回环地址时给出对外暴露警告(它不带生产级加固)。
-
数据库两档模式(
--db):sqlite(缺省):真实 SQLite 库文件,驱动按运行时分派(Bun →bun:sqlite,Node ≥ 22.5 →node:sqlite)。--schema的每条语句都真实执行,失败即停(视图 / 唯一索引 / 触发器等 sim 做不到的能力全可用)。运行时不支持(Bun / Node < 22.5)或打开库文件失败时自动降级sim并告警,不拒绝启动;sim:内置模拟引擎,库是 JSON 文件.adep/sim/db.json。--schema只支持 DDL 子集,其余语句跳过并告警。- 模式名写错(旗标或环境变量)一律拒绝启动,不静默换库;启动横幅与根路径
/会回显db.mode/db.file/db.driver。
adep publish
全栈发布顶层命令(CLI-010):函数部署 + 前端构建托管一把梭。
adep publish # 缺省全栈:函数 deploy + 前端平台侧构建托管
adep publish --only functions # 只发布云函数
adep publish --only frontend # 只发布前端(vite 工程平台侧构建;纯静态站直通托管通道)
adep publish --only frontend --env dev # 前端发到**开发站点**(site-dev/ + dev- 子域 + 独立版本序列)
adep publish -p my-app # 显式指定目标 slug
-
函数部分:扫描本地
functions/计算内容哈希 → 调平台/api/v1/deploy/diff拿差异清单 → 仅上传变更函数 → 发布 → 输出访问域名(与adep deploy同一管线)。 -
前端部分(vite 工程):先整组同步本地
web/草稿,再调平台POST /api/v1/projects/:id/frontend/publish(服务端真实构建),读/frontend/versions取当前版本与siteUrl。未登录由createClient统一抛NOT_LOGGED_IN。 -
前端部分(纯静态站直通):本地
web/无scripts.build且无待编译源码(与平台isStaticSite同语义)时,不经草稿与平台构建,直接 multipart 上传site/打开托管—— 草稿通道的 ≤100 文件 / ≤512KB / 仅文本上限是「源码草稿」预算,vendored 依赖与 webp/woff2 二进制资产装不下也不该走它。缺web/index.html在上传前 fail loud (FRONTEND_NO_PAGE);SPA 回退保留平台当前值(adep hosting config --spa可改)。 -
成功输出每个函数的名字 / 版本 / 访问地址,末尾汇总函数部署数 + 前端构建状态 + 站点 URL:
hello v1 https://my-app.platform.example.com/api/hello
✓ 1 functions deployed in 3.2s
✓ frontend built v2 · https://my-app.platform.example.com
-
--json输出统一信封{ ok, command, data: { scope, functions?, frontend? } }。 -
--env dev只影响前端:产物写site-dev/、版本走独立序列(dev v1.. / prod v1..)、地址是dev-<子域>,与线上站点完全隔离。函数部署不受影响——网关恒读生产库,不存在「部署一个读开发库的 函数」,输出里会显式说明。纯静态站(无scripts.build、无待编译源码)传--env dev直接 fail loud (STATIC_DEV_UNSUPPORTED):该通道直传site/,会覆盖线上站点。
兼容别名:
adep deploy/adep functions deploy/adep hosting deploy保留原行为, 仅在 stderr 打印 deprecation 提示指向本命令(见下文)。
adep deploy(deprecated)
已废弃:推荐使用
adep publish(全栈)或adep publish --only functions(仅函数)。 本命令保留向后兼容,运行时在 stderr 打印迁移提示。
增量部署:扫描本地 functions/ 计算内容哈希 → 调平台 /api/v1/deploy/diff 拿差异清单 → 仅上传变更函数 → 发布 → 输出访问域名。
adep deploy # 项目 slug 取 adep.config.ts 的 name
adep deploy -p my-app # 显式指定目标 slug
adep deploy --worker -p <id|slug> # 按 **wrangler 项目**部署(CF-063,见下)
-
--worker是另一条链:当前目录当作 wrangler 项目根,读wrangler.jsonc直传构建产物——它不是 「部署 adep 函数」,输入 / 产物 / 成败判据都不同(因此-p必填,缺省即WORKER_PROJECT_REQUIRED)。 失败始终非零退出(WORKER_DEPLOY_FAILED):CI 里「部署失败但退出码 0」是最坏的一种静默。 -
本地
functions/<name>.ts对应平台函数<name>(入口文件固定为index.ts)。 目录形态functions/<name>/index.ts等价:目录内其余文件是该函数的私有文件,随草稿整组上传 (默认模板生成扁平形态;两种形态不可同名并存 —— 并存时命令非零退出并列出冲突路径)。 -
幂等:内容与已发布版本一致时输出
no changes,不产生新版本。 -
成功输出每个函数的名字 / 版本 / 访问地址,末尾给一行汇总:
hello v1 https://my-app.platform.example.com/hello
✓ 1 functions deployed in 3.2s
汇总只在真的产生新版本时写
deployed;全部复用时写✓ no changes · N functions up to date in Ts,不把幂等说成重新发布。
adep projects
项目子命令组:创建 / 列出 / 查看 / 删除。子域地址由平台回显(projectBaseUrl),CLI 不自行拼域名。
adep projects create notes-app # → ✓ Project created · https://notes-app.adep.top
adep projects create notes-app -n 笔记应用 # 展示名与 slug 分开
adep projects list # slug \t name \t url
adep projects info notes-app # id \t slug \t name \t url
adep projects delete notes-app --yes # → ✓ Project deleted · notes-app
-
--name缺省取 slug:一行命令即可建项目。 -
目标 slug 已占用 → 平台返回
PROJECT_SLUG_TAKEN,CLI 原样透传并退出码 1。 -
projects delete必须显式--yes:删除是级联且不可撤销的(云函数与草稿、前端代码、 环境变量、触发器、Git 绑定、自定义域名、MCP 工具一并清空,市场共享下架,子域名立即下线)。 缺--yes直接拒绝(CONFIRM_REQUIRED,退出码 1)且不发任何请求——不做 TTY 交互提问, 因为确定性输出(含--json)比一个可能被 CI 管道吞掉的提问更可靠。删除后 slug 不回收。 -
未登录 / 会话失效 → 打印错误后附带
提示:先执行 adep login。
adep functions(deprecated)
functions deploy已废弃:推荐使用adep publish --only functions(或adep publish --only functions -- <dir>等价语义)。functions list/functions logs保留不变。
云函数子命令组:部署(可指定目录)/ 列出 / 查日志。
adep functions deploy ./handlers # 覆盖 adep.config.ts 的 functionsDir(deprecated)
adep functions deploy # 等价于 adep deploy(deprecated)
adep functions list -p my-app # name \t 访问地址
adep functions logs hello -p my-app --tail 50
-
deploy与adep deploy共用同一执行体,输出逐字一致(两个入口各写一遍迟早只有一处有汇总行)。 -
日志缓冲在平台进程内(环形),未执行过的函数为空——命令会如实说明而非报错。
adep mcp
MCP Tool 子命令组:把云函数发布为项目 MCP Tool / 取消发布 / 列出。发布后外部 Agent 即可通过项目 /mcp 端点调用你的后端能力(只消费平台既有的 POST|DELETE /api/v1/functions/:id/mcp-publish 与 tools/list,CLI 不重实现发布逻辑)。
adep mcp publish --function stock # → ✓ MCP tool published · stock
adep mcp publish --function stock --name get-stock # 以别名注册(tools/list 里出现 get-stock)
adep mcp publish --function query-database --description "查询库存" # 覆盖面向 Agent 的描述
adep mcp unpublish --function stock # 取消发布(别名发布过则加 --name get-stock)
adep mcp list # 打印端点地址 + 已发布 tool(name \t description)
-
--function是函数名,--name是可选的注册 Tool 名(缺省取函数名);目标项目缺省取adep.config.ts的name,也可用-p <slug>指定。 -
服务端错误原样透出:函数入参无法映射为 JSON Schema 时返回
MCP_SCHEMA_UNSERIALIZABLE并以退出码 1 结束(CLI 不改写、不兜底,原因由服务端指出)。 -
mcp list读的是项目/mcp端点的tools/list(与外部 Agent 同一条路径):项目 MCP 鉴权模式为public时可直接读取;为api-key时未带 Key 会返回MCP_UNAUTHORIZED。 -
unpublish默认按函数名移除;若曾以别名发布,需加--name <tool>指向该别名。取消一个不存在的工具会如实说明、不报成功也不报错。
adep doctor
环境自检:凭据状态、平台可达性、离线可用范围与本地模拟运行时的能力边界。
adep doctor
adep doctor --json # { server, credentials, network, serverHealth, offlineCommands, boundaries }
-
已登录时体检的是凭据里记录的那个平台(
login --server可指到任意环境),未登录才回退ADEP_SERVER缺省。 -
平台可达性 = 对
GET /api/v1/health的一次真实探测;拿到任何 HTTP 应答都算可达。 -
凭据文件权限宽于
0600会给告警;命令本身不因体检结论失败(它是诊断,不是门)。
adep db
项目数据库维护子命令组:启动 / 状态 / 停止 / SQL 执行 / 快照 / 时间点回滚。项目参数缺省取 adep.config.ts 的 name(也可用 -p 显式指定)。
adep db start # 启动项目数据库(已存在则重新激活,不重建数据)
adep db status # 查询状态与用量(体积 / 表数 / 行数)
adep db stop # 停止数据库(连接关闭并标记;文件保留)
# 列当前环境的表与行数;--table 看单表结构(字段 / 索引 / 建表 DDL)
adep db tables
adep db tables --table users
# 执行单条 SQL;写 / 破坏性语句需 --confirm-table 二次确认目标表名
adep db exec --sql "SELECT * FROM users"
adep db exec --sql "DELETE FROM users WHERE id = 1" --confirm-table users
# 快照(付费档位):列出 / 创建 / 还原
adep db snapshot list
adep db snapshot create
adep db snapshot restore <snapshotId>
# 时间点回滚(团队版,基于变更流重放)
adep db rollback --to 2026-01-01T00:00:00.000Z
db exec 人读模式下返回对齐文本表(结果行截断时会提示),--json 输出结构化结果。
- 环境(
--env):exec/migrate/tables按全局--env dev|prod选库,优先级--env>ADEP_ENV>adep.config.ts的db_environment>prod;每条命令都打印生效环境及来源。 服务端一直支持?environment=,CLI 之前不传 ⇒exec恒打生产库,开发者在 dev 建的表用 CLI 一查 就「不存在」——同一类事故的 CLI 版。 start/status/stop与快照类不分环境:它们管理的是「项目数据库」这一资源本身。
adep storage
云存储 / 文件存储子命令组:上传 / 下载 / 列表 / 删除。
adep storage upload ./avatar.png --path avatars/a.png # 缺省 private(签名访问)
adep storage upload ./banner.png --path site/banner.png --visibility public # public 直连
adep storage download avatars/a.png [-o 本地输出路径] # 自动解析签名 / 直连 URL 落盘
adep storage ls [--prefix site/] # 按前缀列出(附访问 URL)
adep storage rm avatars/a.png
-
上传:multipart,单文件 ≤ 50MB;
path为桶内相对路径,visibility决定 public 直连或 private 签名。 -
下载:先列表解析出服务端生成的 URL(public 直连 / private 15 分钟签名),再取回内容——private 文件无需 CLI 自行签发签名。
-
所有命令都支持
-p <slug>指定项目。 -
四个子命令都跟随全局
--env:dev 走.env-dev/键空间(dev 传的文件在 prodls里看不到,反之亦然),输出会标明当前环境。上传走 multipart 的environment字段,其余走?environment=。
adep hosting(deprecated)
hosting deploy已废弃:推荐使用adep publish --only frontend——vite 工程平台侧构建web/草稿;纯静态站(无构建脚本、无待编译源码)同样直通本上传链路,文件数 / 总量 / 二进制不受草稿通道约束。仅当上传非web/目录(如本地vite build的dist/产物)时 仍需本命令,仅 stderr 提示迁移。hosting info/pull/config保留(运维类操作不并入 publish)。
静态托管子命令组:查看 / 部署 / 拉取 / 配置。站点文件即项目桶 site/ 前缀下的 public 文件。
adep hosting info # 查看托管配置、站点地址与文件树
adep hosting deploy ./dist # 增量上传目录到 site/ 并打开托管(deprecated)
adep hosting deploy ./dist --spa # 同时启用 SPA 回退(deprecated)
adep hosting deploy ./dist --build "npm run build" # 部署前先跑构建命令(非零退出即中止)
adep hosting pull [-o ./site] # 拉取站点公开文件到本地(跳过 private 托管配置)
adep hosting config --enabled true --spa true # 更新托管开关 / SPA 回退(幂等)
-
deploy:先与线上
site/文件表做增量比对(逐文件 sha256,CLI-038)——只上传变更文件, 输出「N 变更 / M 复用」与「部署内容构建于 <本地最新文件 mtime>」;与线上完全一致(空 diff)时 输出醒目警示——大概率是改了源码忘了先构建(旧实现全量盲传,曾把旧包原样重传上线且零提示)。--build <cmd>在项目根目录先执行构建命令,构建失败即中止部署。把本地目录上传为site/<rel>(public)→ 打开托管开关 → 输出站点地址。 -
pull:只拉公开站点文件,private 侧载的托管配置(
site/.hosting.json)不会进站点目录。
adep export
导出自托管部署包:触发平台长任务 → 轮询进度 → 下载 zip → 按 manifest 逐项自检,产出一个「解压即可运行」的可部署包(含应用定义、adep.config.ts、Docker 产物与静态站点)。
adep export # 目标项目取 adep.config.ts 的 name
adep export -p my-app --with-data # 显式指定项目并包含数据库行数据(缺省仅 schema)
adep export --out ./dist-export # 指定输出目录(缺省 ./exports)
-
--with-data附带行数据,--with-source附带函数源码(平台缺省已含,显式给出不改变默认)。 -
--json输出单个结果信封;导出失败(长任务未产出 / 自检不过)以退出码 1 +EXPORT_FAILED结束,不静默给半截产物。
adep app
应用壳工程(PRD §2.11 路径 A:在线运行时——页面随包本地加载、云函数服务端执行,改函数逻辑不需要重新发版)。
两种壳:--platform desktop(缺省,Electron)与 --platform mobile(Capacitor 移动壳,见下)。
adep app init -p my-app -n "我的应用" # 生成 Electron 壳工程(缺省 ./my-app-app)
cd my-app-app && npm install # 壳工程依赖(electron + electron-builder)
adep app open # 启动开发预览(等价壳工程内 npm start,构建前先跑一眼)
adep app build # 拉取已发布页面产物 → 注入 app/site/ → 出安装包
adep app build --platform win32 # 交叉指定目标平台(缺省当前平台)
adep app build --target dir # 只出未压缩目录(macOS 即 .app),本地快速验证
adep app build --prepare-only # 只拉产物 + 注入 + 出构建报告,不调 electron-builder
-
app build只把项目已发布的前端产物(adep publish --only frontend的产物)打进壳: 经平台导出链路拉包 → 校验 manifest 与逐项 sha256 → 只取site/**注入app/site/(注入前清空旧产物,上一次构建留下的陈旧文件不会躺在安装包里); -
缺省产物形态(APP-037 契约门锁住,漂移当场红):
平台 缺省格式 签名状态 说明 macOS( darwin).dmgunsigned 签名 / 公证由开发者本地完成 Windows( win32)nsis 安装器( *-Setup.exe)+ zip 便携包(*-win.zip)unsigned 安装器不是单文件绿色 exe;zip 便携包(APP-043)解压后直接运行里面的 <productName>.exe,两者同次产出Linux( linux).AppImage+.debunsigned deb 需要宿主机有 dpkg-deb——缺失时 electron-builder 会静默跳过,构建报告与终端会显式列出APP_BUILD_TARGET_SKIPPED及原因(rpm 不进缺省模板,见计划 R2 拍板 P3)Android( android)unsigned .apkunsigned gradlew assembleRelease,签名由开发者自担iOS( ios)unsigned .xcarchive/.ipaunsigned xcodebuild 或 xtool( --toolchain xtool产.ipa) -
函数源码不入包:在线运行时下函数在服务端执行,构建报告里的函数清单只标
runtime: "online"(安装包内不含任何functions/文件); -
产物版本回写
adep.app.json的build段(bundleVersion= manifest 项目版本 +site/内容指纹, 内容不变则版本不变),同一份版本也写进dist/build-report.json(appName/bundleVersion/functions/topAssets四字段 + 平台、产物路径等); -
平台不出具任何签名:签名与公证由开发者本地完成;
-
fail loud(都以退出码 1 结束且不产出安装包):项目从未发布前端(提示先跑
publish --only frontend)、壳工程依赖未安装(提示先npm install)、导出包损坏或内容物校验和不匹配。 其中「依赖未安装」的判定有意排在导出之前——导出是分钟级长任务,依赖没装不必白跑。 -
app open(APP-035)只服务桌面壳:直接调壳工程内安装的node_modules/.bin/electron .(与模板缺省 start scriptelectron .等价,不经 npm 以规避 Windows 下 npm.cmd 的 shell 差异; 若你改过 start script,open 不感知,直接跑npm start即可)。移动壳没有开发预览路径 (capacitor 壳下一步是npx cap add ios / android+ Xcode / Android Studio,xtool 壳是xtool dev), 对它们执行 open 当场报APP_OPEN_UNSUPPORTED并点名下一步。与app doctor同口径的开发期 便利命令:不需要登录、不需要网络、不设门(判定门在app build)。
移动壳(Capacitor,APP-009)
adep app init -p my-app -n "我的应用" --platform mobile # Capacitor 骨架 + plugins/adep-bridge 原生桥
cd my-app-app && npm install
npx cap add ios && npx cap add android # 原生工程由 Capacitor 生成
adep app build --platform android --prepare-only # 只注入 + 出报告,先验证注入面
adep app build --platform android # gradlew assembleRelease → unsigned APK
adep app build --platform ios # xcodebuild archive(CODE_SIGNING_ALLOWED=NO)
- 骨架含
plugins/adep-bridge原生桥(iOS Swift / Android Kotlin)与 host-side 门控镜像bridge/gate.mjs:权限门控做在原生侧,未在adep.permissions.json声明的权限一律PERMISSION_DENIED+ 设备侧审计日志;ownerKey只存 Keychain / Keystore。 - 构建往
app/site/落四件:adep.permissions.json(权限清单)、adep-bridge.js(页面桥)、adep.app.json(壳配置)、以及往index.html幂等注入的一行<script src="/adep-bridge.js">。 缺index.html即构建失败(APP_SITE_MISSING):它是 WebView 恒加载的入口页,缺了装机就是 白屏——所以不产出「报告成功、装机白屏」的包。页面加载后由桥装配window.__ADEP_APP__, 失败时按原因分三种字段(configError/offlineError/timeoutError),都带可读原因, 不会只剩一个MISSING。 - iOS 侧原生桥是双形态并存(APP-015):
Package.swift(SPM)与ios/AdepBridge.podspec(CocoaPods)各管一条消费路径。自 Capacitor 8 起cap add ios默认建 SPM 工程,缺 SPM 清单的 插件会被静默跳过(运行时才报 "plugin is not implemented"),所以两者都别删;包名 / product 名 (AdepBridge)、目录布局(清单在 npm 包根、源码平铺ios/Sources)与 iOS 下限(14.0)两形态必须一致。 - 移动平台必须显式指定
--platform ios|android;产物是 unsigned 的.xcarchive/ APK。 - ⚠️
.xcarchive≠ 安装包(APP-040):它是 Xcode 的归档格式,不能直接装真机;导出.ipa还需要在 Xcode 里完成 Distribute App(或xcodebuild -exportArchive),而导出要用户自备ExportOptions.plist与描述文件——平台不出具签名(PRD §2.11.7),这一步不做自动化 (见docs/mobile-shell-signing.md§3)。构建期会对.xcarchive做产物级结构核对: 缺Products/Applications/*.app报APP_ARCHIVE_INCOMPLETE;归档内出现_CodeSignature/embedded.mobileprovision报APP_ARCHIVE_SIGNED(与「平台不出具签名」 口径冲突,属实现回归)。 - 签名 / 公证 / 上架由开发者自担,逐步指引见仓库
docs/mobile-shell-signing.md。
离线运行时(--offline,APP-008 / APP-022)
adep app build --offline # 桌面壳:注入 app/offline/
adep app build --offline --toolchain xtool # iOS xtool 壳:注入 app/site/offline/
把设备端运行时(@adep/app-runtime)装入壳工程的离线根——桌面 app/offline/,xtool
app/site/offline/(页面经 capacitor:// 只能读 appLocation 之内的文件):白名单云函数与
数据库基线表全部随包本地执行,不需要平台在线。白名单函数含不可移植写法时构建失败、
退出码非 0,错误含违规类型与文件名行号。
--offline 在桌面壳下不接受 --platform(离线包与平台无关);iOS 走
--offline --toolchain xtool(恒 --platform ios)。
⚠️ xtool 离线构建要求先跑在线构建:离线路径只追加 site/offline/ 子树与壳注入面
(页面桥 / adep.app.json / 权限清单),不产生页面本体——页面只能来自平台导出。
缺 app/site/index.html 时当场报 APP_SITE_MISSING,不会产出「报告成功、装机白屏」的包。
adep app build --platform ios --toolchain xtool # 1) 拉页面产物 + 注入页面桥 / 壳配置
adep app build --offline --toolchain xtool # 2) 追加离线树
壳工程体检(adep app doctor,APP-031)
adep app doctor # 体检当前目录(壳工程根)
adep app doctor --dir ./my-app-app # 指定壳工程
adep app doctor --json # 机器可读(脚本 / CI 归档)
只读、不需要登录、不需要网络:体检的是「盘上已经有的东西」。它与 app build 的分工是场景不同——
build 要先登录、先发布、跑一整轮导出才知道注入面齐不齐;doctor 一条离线命令就回答同一个问题,
适合排查「上次构建留下的壳工程为什么装机白屏」。十项结论各带固定 id 与原因码(与构建侧同码):
| id | 查什么 | 不通过时 |
|---|---|---|
config | adep.app.json 在且过 schema | APP_CONFIG_NOT_FOUND / APP_CONFIG_INVALID(读不到就当场收口,不再往下猜) |
form | 由 template 推导构建形态(desktop→electron;mobile→capacitor + 盘上原生工程;xtool→xtool/ios) | 还没 npx cap add / 未知模板 ⇒ 提示 |
deps | 打包器前置(复用构建侧判定,不写盘) | APP_SHELL_DEPS_MISSING / APP_XTOOL_MISSING / APP_XCODE_SDK_MISSING |
siteEntry | app/site/index.html | APP_SITE_MISSING / APP_SITE_UNREADABLE |
bridgeTag | 入口页引用了 /adep-bridge.js(桌面壳改查 preload.cjs) | 桥压根不执行 ⇒ __ADEP_APP__ 永不出现 |
siteAssets | app/site/ 三件套齐(桌面壳不走注入,跳过) | 点名缺哪一个 |
configDrift | 站点内壳配置与壳根一致(比页面消费的 6 个字段;build 段不比) | 构建后改了配置没重建 ⇒ 页面还在打旧平台地址 |
buildBinding | 壳根有 build 段 | 未构建过 ⇒ 提示(新 init 的正常中间态) |
offline | 离线树必需文件 + 函数清单里每个函数的源码 | 点名缺件 / 缺哪个函数源码(设备上会 FN_NOT_FOUND) |
identity | 桌面壳首启是否签发了 ownerKey、落在哪种存储(读 data/identity/status.json,非机密) | 未自举 ⇒ 提示;落在 0600 文件 ⇒ 明说「非系统凭据」;签发失败 ⇒ 原因码原样透出(APP-033) |
它不是门:有 fail 项也以退出码 0 结束(与 adep doctor 同口径);判定门在 adep app build。
离线库同步(adep app sync,APP-032)
adep app sync --owner-key adep_ok_… # 一次往返:先上行 outbox,再拉下行增量
adep app sync --owner-key adep_ok_… --push # 只上行(排障时不必来回跑整轮)
adep app sync --owner-key adep_ok_… --pull # 只下行
ADEP_OWNER_KEY=adep_ok_… adep app sync # 也可以走环境变量
把桌面壳的设备本地库(data/offline.sqlite,由离线运行时首次启动时创建)与云端项目库按
单主语义对齐:上行读本地变更流 → outbox → POST /app-sync/upstream(整批原子,服务端确认后才推进
acked_seq);下行 GET /app-sync/changes?since=<游标> → 应用进本地库(直写驱动,不追加变更流,
否则刚拉下来的行会被下一次上行当成新写推回去)。
- 身份不是可选项:
--owner-key/ADEP_OWNER_KEY必填。ownerKey 由服务端签发 (POST /api/v1/projects/<slug>/app-installations,移动壳由原生侧首启自举写入 Keychain / Keystore, 桌面壳由主进程首启自举写入系统凭据库或 0600 文件,见 APP-033), CLI 不复用平台会话冒充设备身份;缺失即IDENTITY_MISSING且不发任何/app-sync请求。 桌面 app 自己启动后会自动同步;本命令用于开发期 / 排障(比如看某次上行到底卡在哪一批)。 - 同步目标取壳配置的
platformUrl/projectSlug(不是ADEP_SERVER):同步的一定是这个壳连的项目。 - 只支持桌面壳:移动壳的库在设备沙箱里,本机 CLI 摸不到(
APP_SYNC_UNSUPPORTED_SHELL)。 - 本地库不存在 ⇒
APP_SYNC_DB_MISSING(先跑一次离线运行时,库才会被建出来)。 - 服务端拒绝时命令退出码非 0,码与
failedSeq原样透出,本地位点不动(可原样重试)。
配置
环境变量
| 变量 | 说明 | 缺省 |
|---|---|---|
ADEP_SERVER | 平台地址(CLI 请求的默认 server) | https://adep.top |
ADEP_HOME | 凭据目录(测试隔离用) | ~/.adep |
ADEP_NO_BROWSER | 1 = adep login 不打开浏览器 | 未设置(TTY 下自动打开) |
ADEP_ENV | 数据面默认环境(dev / prod) | 未设置(再读 db_environment,最后 prod) |
ADEP_OWNER_KEY | adep app sync 的设备身份(服务端签发的 ownerKey) | 未设置(--owner-key 优先;两者都空即 IDENTITY_MISSING) |
export ADEP_SERVER=https://platform.example.com
export ADEP_HOME=~/.adep
export ADEP_ENV=dev # 等价于每条命令都带 --env dev(db exec / migrate / tables、storage、publish 前端)
也可通过
-s/-e/-p等子命令参数覆盖。全局旗标--env <dev|prod>优先级最高:--env>ADEP_ENV>adep.config.ts的db_environment>prod。
开发期环境变量(.env / .env.local)
项目根的 .env / .env.local 与进程环境变量是两条不同的通道,混着预期是本地开发
最常见的排查黑洞(esign 实测:ADEP_AUTH_ISSUER 写进 .env 后用 bun run 起 serve——
函数数据面一切正常、应用却显示未登录,白耗一轮排查):
| 通道 | 谁在读 | 变量 | 生效条件 |
|---|---|---|---|
| 函数 env(注入函数运行时) | adep dev / adep serve 自己读 .env + .env.local 文件 | 业务变量、ADEP_SECRET_*、FUNCTIONS_FETCH_* | 写进文件即可,不依赖 shell 进程 |
| 进程级(CLI 命令自身的行为开关) | 只认 process.env,不读 .env 文件 | PORT / HOST / ADEP_SERVE_STATIC_DIR / ADEP_SERVE_DB / ADEP_SERVE_DB_FILE / ADEP_SERVER 等 | export / env VAR=... adep serve / Docker env_file |
| 混合(登录即服务) | process.env 优先;缺失时回退 adep dev / adep serve 读到的 .env + .env.local | ADEP_AUTH_ISSUER | export(优先级最高)或直接写 .env |
函数 env 的合并优先级(adep dev / adep serve 同一份实现):adep.config.ts 的
environment 为基座 → .env 覆盖 → .env.local 最优先。
⚠️ bun run <script>(以及 npm run)直跑自定义脚本时,.env 不会自动生效——
bun run 的子进程不自动加载 .env(node 更不会)。但 adep dev / adep serve 自己会读文件,
所以「进程级变量全部失效」只对纯 shell 开关成立:
- 函数 env 不受影响(CLI 自己读文件,数据面已自愈);
ADEP_AUTH_ISSUER有文件回退(CLI-044):写进.env后bun run serve/adep serve也能 启用「登录即服务」,不再出现「平台授权成功、应用显示未登录」;process.env仍优先;- 其余进程级开关(
PORT/HOST/ADEP_SERVE_*)仍只认process.env,要显式export或env VAR=... adep serve。
拿不准一个变量属于哪条通道,看它在哪个环节被消费:函数代码里读的走文件通道,
adep serve / adep dev 命令行为开关走进程通道(ADEP_AUTH_ISSUER 是唯一的文件回退例外)。
adep.config.ts
adep init 生成的项目配置文件,CLI 的 dev / serve / deploy 按此识别项目(CLI 直接 import 后读 default 的字段,无需任何辅助包):
// adep 项目配置:CLI(dev / serve / deploy)与云函数 vite 插件按 default export 读取。
export default {
name: 'my-app', // 项目名(deploy 的默认 slug)
template: 'fullstack', // 模板:empty | function | fullstack
functionsDir: 'functions', // 云函数目录
functions_prefix: '/api', // 云函数路由前缀(默认 /api,与线上 /api/{fn} 对齐)
db_environment: 'dev', // 数据面默认环境(dev | prod,2026-10-18)
}
functionsDir:云函数目录(dev/serve/deploy读取,缺省functions/)。functions_prefix:影响本地路由与 vite 插件代理前缀(见上方adep init产物结构说明)—— 默认/api:adep dev访问/{prefix}/{fnName}、adep serve访问/api/{prefix}/{fnName}、 vite 插件按/{prefix}/*代理;函数互调与线上部署路径不随前缀变化。db_environment:不带--env也不设ADEP_ENV时数据面命令的默认环境。⚠️ 不能复用environment——那个键是「注入函数运行时的变量表」(Record<string, string>),同名会让解析拿到 对象、判成非法值。
常用示例
# 登录后查看当前身份
adep whoami
# 登出并清除本地凭据
adep logout
# 列出平台可用的脚手架模板
adep init --list
# 数据库运维:启动 → 看状态 → 跑 SQL → 打快照
adep db start
adep db status
adep db exec --sql "SELECT count(*) FROM users"
adep db snapshot create
# 云存储:上传 / 下载 / 删除
adep storage upload ./logo.png --path assets/logo.png --visibility public
adep storage download assets/logo.png -o ./logo.png
# 静态托管:本地构建产物一键上线,随时拉回
adep hosting deploy ./dist # deprecated:推荐 adep publish --only frontend
adep hosting pull -o ./site
# 以 JSON 输出(Agent / CI 可用)
adep --json publish -p my-app
FAQ
Q:adep login 失败提示 NOT_LOGGED_IN / SERVER_UNREACHABLE?
先确认 ADEP_SERVER(或 -s)指向可访问的平台地址,再重新 adep login。凭据保存在 ~/.adep/credentials(权限 0600)。
Q:adep dev 需要 Docker / 云连接吗?
不需要。默认使用进程内模拟运行时(@adep/runtime),断网也能开发——只替换传输与持久化,不改变函数语义。
Q:本地开发与线上行为会不一致吗?
模拟器刻意保持执行语义一致:cloud.db / cloud.storage / cloud.realtime 能力装配复用线上逻辑。差异点仅限传输层与持久化层,且 adep dev 启动时会打印边界清单(触发器不自动执行、无跨实例广播、不在本地计量计费)。
Q:adep deploy 显示 no changes?
这是幂等设计:本地函数内容与已发布版本一致时复用当前版本,不产生新版本。想强制更新需修改函数内容。
Q:项目名的命名规则?
init 项目名:小写字母开头,仅含小写字母 / 数字 / 连字符(≤ 63 字符)。
Q:adep db exec 的 --confirm-table 是做什么的?
写 / 破坏性语句(DELETE / DROP / ALTER…)服务端要求二次确认目标表名,--confirm-table users 即输入目标表名以放行;只读查询无需该参数。
Q:变量写进 .env 了,adep serve 却没生效?
先分通道:函数 env(业务变量 / ADEP_SECRET_*)走 .env / .env.local 文件,adep dev / adep serve 自己读、必生效;进程级开关(PORT / HOST / ADEP_SERVE_* 等)只认 process.env,要显式 export 或 env VAR=... adep serve。ADEP_AUTH_ISSUER 是混合通道(CLI-044):process.env 优先,缺失时回退 .env / .env.local,所以写进 .env 也能启用登录即服务。bun run <script> / npm run 直跑时 .env 不会自动加载(详见「配置 → 开发期环境变量」)。
Q:adep storage / adep hosting 需要在平台项目目录里跑吗?
不需要。它们通过 -p <slug> 指定目标项目,缺省才读取 adep.config.ts 的 name——在任何目录下都能对指定项目做资源运维。
Q:private 文件如何下载?
adep storage download 会自动调用平台列表接口拿到服务端生成的 15 分钟签名 URL 再取回内容,无需 CLI 自行签发签名;公网访问同样走该 URL。
Q:--json 输出长什么样?
每个命令输出一个 JSON 对象:成功 {"ok":true,"command":"...","data":{...}},预期内失败 {"ok":false,"command":"...","error":{"code":"...","message":"..."}} 且退出码为 1。
相关
@adep/runtime—— 平台运行时内核(执行器 + 本地模拟运行时 + 能力装配),adep dev复用其保证本地/线上语义一致。