关于 cargo-hosting
cargo-hosting 是 Cargo CLI 的托管管理技能,面向部署静态应用和边缘 Worker 的工作区用户。它将脚手架、创建资源、构建部署与上线提升分为明确步骤,并介绍应用和 Worker 的配置差异。
主要功能
- 01初始化应用与 Worker 项目
- 02创建工作区托管资源
- 03构建并上传部署
- 04将部署提升为线上版本
- 05管理应用及 Worker 环境变量
适用场景
- 01从模板创建静态前端应用
- 02部署处理 HTTP 请求的边缘 Worker
- 03发布构建完成的应用或 Worker
- 04为 Worker 配置环境变量和密钥
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ getcargohq/ cargo-skills --skill cargo-hosting安装后在 Claude Code 中输入/cargo-hosting确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
skill-metadata.jsonreferences/response-shapes.mdtroubleshooting.mdreferences/examples/apps.mddeployments.mdworkers.md详细介绍
Cargo CLI — 托管
Cargo Hosting 运行两种工作区级资源,以及负责发布它们的部署:
- App — 静态前端(默认使用 Vite 单页应用;也会检测 Astro、Gatsby、Create React App、Parcel 和 Webpack;Next.js、SvelteKit 或 Nuxt 则可通过各自的
build脚本运行,参见 App 构建),通过专属子域名提供服务(参见 URL)。这些模板基于@cargo-ai/app-sdk构建(Vite + refine + shadcn 基础组件,并将getCargoEnv()/useCargoApi()配置为连接到工作区)。 - Worker — 在边缘运行的无服务器 HTTP 处理程序(
fetch(request, env)),基于@cargo-ai/worker-sdk构建(自动生成 OpenAPI 3.1 规范,位于/openapi.json;Swagger UI 位于/docs)。 - Deployment — 将本地源目录构建并上传到 App 或 Worker。部署只有经过提升后才会生效。
如需将 App/Worker 整理到文件夹中,请使用
cargo-workspace-management(folder …)。此处的--folder-uuid标志使用这些文件夹 UUID。
端到端操作指南请参见
references/examples/apps.md、references/examples/workers.md和references/examples/deployments.md。 JSON 响应结构请参见references/response-shapes.md。 常见错误及其解决方法请参见references/troubleshooting.md。
初始化
已登录(cargo-ai whoami 返回工作区)?请跳至下一节。
npm install -g @cargo-ai/cli # no global install? prefix every command with `npx @cargo-ai/cli`
cargo-ai login --email [email protected] # emailed code, no browser; creates the account on first use
# alternatives: --oauth (browser) · --token <api-token> (CI)
cargo-ai whoami # confirm the active workspace before any write
每条命令都会将 JSON 输出到 stdout;失败时以非零状态码退出,并返回 {"errorMessage": "..."}。任何会创建运行或批次的操作都是异步的——请传入 --wait-until-finished,或轮询对应的 get 命令。安装完整技能包后,../cargo/references/prerequisites.md 会补充 CLI 版本固定、令牌作用域以及仅限管理员使用的功能。
生命周期
App 和 Worker 遵循相同的流程——搭建脚手架 → 创建槽位 → 部署 → 提升:
init (local scaffold) → create (slot + slug) → deployment create (build+upload) → deployment promote (go live)
- 从模板搭建脚手架,创建本地项目——
hosting app init <dir>/hosting worker init <dir>。 - 在工作区中创建槽位——
hosting app create --name --slug→appUuid(或workerUuid)。--slug会成为子域名的一部分,并且在工作区内必须唯一。 - (可选)配置本地开发环境——对于 App,运行
hosting app env <appUuid>,输出本地副本所需的.env.local配置行(Cargo OAuth + 工作区 + App UUID + API URL)。对于 Worker,在搭建脚手架时生成的项目中运行npm run dev(参见在本地运行 Worker)。如果 Worker 会调用 Cargo API,则首次部署前还需要设置CARGO_API_TOKEN密钥(参见 Worker 环境变量和密钥)。 - 部署——
hosting deployment create --app-uuid <uuid> --source <dir>会上传源代码。随后,后端会在沙箱中构建项目:对于 App,先运行npm ci --ignore-scripts,再运行 App 自带的build脚本;如果没有该脚本,则使用框架的默认构建流程(参见 App 构建);对于 Worker,则会打包入口点。命令返回deploymentUuid。 - 提升——
hosting deployment promote --uuid <deploymentUuid>会将线上 URL 指向该构建版本。
部署会异步构建——请轮询 hosting deployment get <uuid>,直到状态变为终态后再进行提升(参见异步轮询)。
URL
线上主机名为托管根域名下的 <slug>-<first 8 chars of the workspace UUID>,应用和工作进程使用不同的根域名——在生产环境中,应用使用 https://<slug>-<ws>.app.getcargo.run,工作进程使用 https://<slug>-<ws>.worker.getcargo.run。工作区后缀使主机名在全局范围内唯一,因此 slug 只需在你的工作区内唯一。
不要手动拼接 URL。从 create 或 get 中读取 url——不同环境的根域名有所不同。每次部署还会获得一个预览主机名 https://deployment-<deploymentUuid>.<root>,在部署被提升之前即可使用。
由于根域名不同,应用调用工作进程始终是跨源请求。没有任何选项能让它们使用相同的源,因此工作进程必须处理 CORS。请参阅 references/examples/workers.md 中的应用 + 工作进程模式。
应用
# Discover
cargo-ai hosting app list # all apps (filter with --folder-uuid <uuid>)
cargo-ai hosting app get <uuid> # one app's details + URL
# Scaffold locally (Vite + @cargo-ai/app-sdk)
cargo-ai hosting app init ./my-app --list-templates # see available templates, then:
cargo-ai hosting app init ./my-app --template blank --name "My App"
# Create the slot (slug unique per workspace; `url` in the response is the live host)
cargo-ai hosting app create --name "My App" --slug my-app --folder-uuid <folder-uuid>
# Print .env.local for local development
cargo-ai hosting app env <app-uuid>
cargo-ai hosting app env <app-uuid> --api-url https://api.getcargo.io
# App env vars (public build-time values; take effect on the next deploy + promote)
cargo-ai hosting app env-var list --app-uuid <app-uuid>
cargo-ai hosting app env-var set --app-uuid <app-uuid> --key VITE_LOG_LEVEL --value debug
cargo-ai hosting app env-var remove <env-var-uuid> # uuid from env-var list
# Update / remove
cargo-ai hosting app update --uuid <app-uuid> --name "Renamed"
cargo-ai hosting app update --uuid <app-uuid> --folder-uuid null # move to workspace root
cargo-ai hosting app remove <app-uuid> # also removes its deployments
模板:blank(最简起始模板)、territories-overview(只读地区网格示例,演示 useCargoApi() + react-query),以及 public-site(公开、可编入索引的网站,会通过自身的构建脚本预渲染每个路由,并提供 robots.txt + sitemap.xml)。运行 app init <dir> --list-templates 查看当前模板列表。
应用构建
如果 package.json 声明了 build 脚本,Cargo 就会运行它,整个构建过程都由该脚本负责。 Cargo 不会在它之前或之后运行任何命令,因此该脚本必须生成客户端包以及其他内容,例如预渲染的 HTML。如果没有 build 脚本,则运行检测到的框架的默认命令:
| 框架(根据依赖项检测) | 默认构建命令 | 输出目录 |
|---|---|---|
| Vite,以及任何未检测到的框架 | vite build | dist |
| Astro | astro build | dist |
| Gatsby | gatsby build | public |
| Create React App | react-scripts build | build |
| Parcel | parcel build | dist |
| Webpack | webpack build | dist |
Next.js、SvelteKit 和 Nuxt 不会被检测到:它们会回退到默认值(dist),因此请声明一个 build 脚本,将静态文件导出到 dist/。无论使用哪种框架,每个应用都会接收所有公开环境变量前缀(参见应用环境变量是公开的)。
- 输出必须位于该框架的输出目录中,并包含
index.html,否则构建会失败。未知路径会回退到该外壳页面。 - 当前构建脚本执行的所有操作都会在每次部署时运行。 这包括
tsc检查、不同的--mode,以及prebuild/postbuild钩子。如果脚本在此处失败,部署也会失败。线上部署会继续提供服务,因为只有构建成功后才会进行提升。 - Cargo 无法使用的
build脚本会静默回退。 这包括无法解析的package.json、空脚本或非字符串条目。部署仍会成功,但原因只会记录在构建日志中,因此如果预渲染步骤似乎被跳过,请检查日志。 - 平台值会注入到每个公开前缀下。 Vite 应用读取
VITE_CARGO_API_URL,Next.js 应用读取NEXT_PUBLIC_CARGO_API_URL。使用build脚本时,这些值也会作为实际进程环境变量传入,因此纯 Node.js 预渲染步骤可以读取它们。用户环境变量在构建日志中会被隐藏。
应用环境变量是公开的
应用只会读取键名以公开前缀开头的环境变量:VITE_、NEXT_PUBLIC_、PUBLIC_、NUXT_PUBLIC_、GATSBY_ 或 REACT_APP_。无论使用哪种框架,这些前缀都有效。它们来自工作区环境变量和应用级条目(hosting app env-var set、使用 "kind":"app" 的 POST /v1/hosting/env-vars,或项目中的 defineApp({ env }))。应用条目会覆盖同名的工作区条目。所有这些变量都会被编译到任何人都能下载的构建包中。因此:
- 应用环境变量不能是机密变量。 API 会因
secretNotSupportedForApp而拒绝isSecret: true,项目的defineApp遇到secret()值时会抛出错误,而机密工作区条目永远不会进入应用构建。凭据应放在应用调用的 Worker 中。hosting app env-var set --help仍然会显示--secret示例(应用上的CARGO_API_TOKEN)——但这会失败。 该标志与 Worker 命令共用;用于应用时,后端会拒绝它,无论是创建还是更新都一样。 - 与任何前缀下的平台键匹配的键(
*_CARGO_API_URL、*_CARGO_WORKSPACE_UUID、*_APP_BASE_PATH、……)均为保留键。 - 值会在构建时写入,因此更改后需要重新部署并提升版本。
自定义域名和搜索索引
Cargo 所有的主机名均设为 noindex。 默认的 *.app.getcargo.run 主机名和每个 deployment-<uuid> 预览环境都会返回 X-Robots-Tag: noindex,因此只有使用自定义域名的应用才能被编入索引。在 CLI 1.0.96 中,没有 CLI 命令可以绑定自定义域名,因此请使用 API:
CARGO_API_BASE=$(cargo-ai whoami | jq -r '.baseUrl') # https://api.getcargo.io in production
curl -X POST "$CARGO_API_BASE/v1/hosting/custom-domains" \
-H "authorization: Bearer $CARGO_API_TOKEN" -H "content-type: application/json" \
-d '{"kind":"app","appUuid":"<uuid>","hostname":"www.example.com"}'
# → DNS records to add: certificate validation records + a cnameTarget for the hostname
curl -X POST "$CARGO_API_BASE/v1/hosting/custom-domains/<domain-uuid>/refresh-status" \
-H "authorization: Bearer $CARGO_API_TOKEN" # repeat until status is "active"
- 主机名至少需要三个标签,因此请绑定
www.example.com,而不是example.com。 - Worker 也可以使用自定义域名(
"kind":"worker","workerUuid":…)。不同主机名仍属于不同源,因此应用 → Worker 的 CORS 规则仍然适用。 - 被编入索引的页面还需要为每个 URL 提供真实 HTML。 在
build脚本中进行预渲染(public-site模板展示了具体做法)。以.html路径链接预渲染路由:/about.html会提供预渲染文件,而/about会回退到 SPA 外壳。发布public/robots.txt和public/sitemap.xml,并在每个预渲染页面的 head 中加入标题、描述、canonical 和 Open Graph 标签。 - 托管应用永远不会返回 404。 未知路径会以
200状态码返回 SPA 外壳,因此客户端的未找到视图应自行设置<meta name="robots" content="noindex">。 - 基于
CargoRefineApp构建的应用需要登录 Cargo,因此无法被编入索引。公共网站会自行渲染其组件树。 - 不会有人代你向搜索引擎提交内容。请在 Search Console 中提交站点地图。
Worker
命令格式与应用相同——将 app 替换为 worker:
cargo-ai hosting worker list # filter with --folder-uuid <uuid>
cargo-ai hosting worker get <uuid>
# Scaffold (edge fetch(request, env) handler on @cargo-ai/worker-sdk)
cargo-ai hosting worker init ./my-worker --list-templates
cargo-ai hosting worker init ./my-worker --template blank --name "My Worker"
cargo-ai hosting worker create --name "My Worker" --slug my-worker --folder-uuid <folder-uuid>
cargo-ai hosting worker update --uuid <worker-uuid> --name "Renamed"
cargo-ai hosting worker remove <worker-uuid> # also removes its deployments
模板:blank(自动生成 OpenAPI 规范和 Swagger UI)和 custom-integration(Cargo 自定义集成——清单 / 操作 / 提取器 / 自动补全 / 动态架构)。
入口点。 构建会打包以下文件中第一个存在的文件:src/index.ts、src/index.js、index.ts、index.js;如果一个都没有,则构建失败。不会识别 .mjs、.mts 和 .cjs 入口点,因此请将它们重命名为 .js/.ts;由于脚手架的 package.json 设置了 "type": "module",.js 文件中可以使用 ES 模块语法。
Worker 环境变量和密钥
Worker 从 c.env.KEY(Hono 上下文)或 fetch(request, env) 的 env 参数读取配置。有三个来源:
| 来源 | 设置方式 | 生效范围 |
|---|---|---|
| 平台绑定 | 自动 | CARGO_API_URL、CARGO_WORKSPACE_UUID、CARGO_WORKER_UUID |
| 工作区环境变量 | cargo-ai workspaceManagement envVar create --key K [--secret] | 工作区中的每个 Worker(应用只能获取非密钥、带公开前缀的变量) |
| Worker 环境变量 | cargo-ai hosting worker env set --worker-uuid <uuid> --key K [--secret]、项目中的 defineWorker({ env }),或 POST /v1/hosting/env-vars | 仅该 Worker;同名时,Worker 条目会覆盖工作区条目 |
默认情况下不会注入 CARGO_API_TOKEN。 在提供该变量之前,createCargoApi(c.env) 会抛出 Missing CARGO_API_TOKEN…。唯一例外是:设置了服务用户的 Worker(通过 hosting worker update --uuid <uuid> --run-as-user-uuid <service-user-uuid> 或 defineWorker({ runAs }))会获得该用户的临时 CARGO_API_TOKEN,每次发布时都会重新生成(包括预览部署;旧令牌会被撤销),除非其环境变量中已设置此键。worker create 没有 --run-as-user-uuid;请使用 update 设置。该用户必须是权限范围在你权限之内的 服务 用户(否则会出现 runAsUserNotFound / runAsUserNotPermitted)——创建方法见 ../cargo-workspace-management/SKILL.md →“服务用户”。否则,请生成一个工作区 API 令牌,并在首次部署前将其作为密钥保存:
cargo-ai workspaceManagement token create --name "worker: my-worker" # value shown ONCE
export CARGO_API_TOKEN=<token value>
cargo-ai workspaceManagement envVar create --key CARGO_API_TOKEN --secret \
--description "Cargo API token for hosted workers" # --value omitted → read from $CARGO_API_TOKEN
工作区条目会将该令牌提供给每个 Worker。如果只有一个 Worker 应持有该令牌,请改为在该 Worker 上设置:
- 项目:
defineWorker("my-worker", { path, env: { CARGO_API_TOKEN: secret("CARGO_API_TOKEN") } })。 - CLI:
printf %s "$CARGO_API_TOKEN" | cargo-ai hosting worker env set --worker-uuid <uuid> --key CARGO_API_TOKEN --value - --secret(省略--value可从 Shell 读取$CARGO_API_TOKEN)。hosting worker env list --worker-uuid <uuid>会显示键,密钥值显示为null;hosting worker env remove <env-var-uuid>会删除一个变量(UUID 来自env list),在下次部署并提升后生效。 - API: 使用
{"kind":"worker","workerUuid":"<uuid>","key":"CARGO_API_TOKEN","value":"…","isSecret":true}调用POST /v1/hosting/env-vars。在 TypeScript 中,对应@cargo-ai/api中的api.hosting.envVars.create(…);list接受{ workerUuid }。
变量值会在部署时捕获,而不是实时读取。 部署提升时会附加绑定,非密钥值也会编译进 Bundle,成为 process.env.KEY。添加或更改变量后,请再次运行 deployment create 和 promote。在此之前,正在运行的 Worker 会继续使用旧值。
密钥绝不会进入 Bundle。 密钥会作为加密的运行时值绑定。只有非密钥值会被编译进去,而且可以从每个部署的预览主机下载 Bundle,因此任何敏感值都必须使用 --secret / isSecret: true。
在本地运行 Worker
两个模板都附带 dev.ts 测试脚本:npm run dev 会在 Node 下运行 src/index.ts,并在 http://localhost:8787 上提供服务,支持热重载(可通过 PORT 覆盖)。dev.ts 不会被部署。
- 本地不存在平台绑定,因此
c.env为空。createCargoApi会回退到process.env,所以请在 Shell 中先导出CARGO_API_TOKEN(若使用非生产 API,还需导出CARGO_API_URL),再运行npm run dev。你自己的变量也需要在代码中使用相同的回退方式。 - 本地不会强制执行
manifest.json中的outboundAllowlist和 Cron 触发器。请在部署上测试。
如果项目是在 dev.ts 尚不存在时生成的,可以从全新的 hosting worker init 中复制该文件,以及 dev 脚本和 @hono/node-server、tsx 开发依赖。
存储工作器中的文件
@cargo-ai/worker-sdk 中的 createCargoObjects(env) 使用与 createCargoApi 相同的 CARGO_API_TOKEN,按路径读取和写入工作区内容文件:
import { createCargoObjects } from "@cargo-ai/worker-sdk";
const objects = createCargoObjects(c.env);
await objects.put("decks/acme.pdf", pdfBytes, { contentType: "application/pdf" }); // ≤ 100 MB; overwrites in place
const { url } = await objects.url("decks/acme.pdf", { expiresInSeconds: 3600 }); // signed, no auth needed to open
此外还有 get(path) → Blob、stat(path)、list({ prefix, cursor, limit })、remove(path)、setVisibility(path, "public" | "private")。新文件默认为私有,不会编入 Agent 搜索索引,也不会镜像到上下文仓库,除非 put 传入 indexMode / visibility / mirrorToContext。签名 url(最长 3 h)或公开文件的固定链接无需身份验证即可打开——只有在调用方应当获得该文件时,才将其返回给调用方。路径模型和默认值:../cargo-content/references/objects.md。
工作器日志和错误
createWorker() 会捕获 Cargo 在每次请求期间分发的 console.log/info/warn/error/debug,并将其发送到工作器日志(每次请求最多 50 行)。未捕获的错误会连同堆栈一起记录,并返回一个不带内容的 500。
捕获的错误不会留下任何痕迹。 如果路由捕获异常并返回经过清理的响应,例如 502 "The data provider is unavailable",日志中只会有 HTTP 行。清理错误信息前,请先记录错误:
try {
return c.json(await loadData(createCargoApi(c.env)));
} catch (err) {
console.error(err); // stack goes to the logs; the response stays clean
return c.json({ error: "The data provider is unavailable." }, 502);
}
每次工作器调用(定时任务或入站 HTTP)也会作为 nodeKind: "worker" 的 span 记录;如果上游没有开启 trace,则会自动开启一个。使用 cargo-ai orchestration span list --node-kind worker --execution-statuses error --execution-started-after <iso> 查找所有工作器中的失败调用(每条记录都带有 logs list --run-uuid 所需的 runUuid),使用 orchestration trace get <traceUuid> 查看某次调用触发了什么。CLI 的 trace list 不支持筛选工作器;API 的 POST /v1/orchestration/traces/list 接受 workerUuid。操作手册:../cargo-diagnostics/SKILL.md。
使用 cargo-ai hosting worker logs list --worker-uuid <uuid> --levels error --limit 50 读取日志(按时间从新到旧排列;还支持 --run-uuid、--search、--occurred-after / --occurred-before,以及 logs count / logs get <logUuid> --worker-uuid <uuid>),也可以在 Web 应用中读取,或通过 API 读取:使用 {"workerUuid":"<uuid>","levels":["error"],"limit":50} 调用 POST /v1/hosting/logs/list,或在 TypeScript 中使用 api.hosting.log.list(…)。
部署
一次部署只能归属于一个应用或一个工作器(--app-uuid 和 --worker-uuid 不能同时使用)。
# List / inspect
cargo-ai hosting deployment list --app-uuid <uuid> # or --worker-uuid <uuid>
cargo-ai hosting deployment get <deployment-uuid> # status + metadata
cargo-ai hosting deployment get-promoted --app-uuid <uuid> # what's currently live
# Build & upload a local source directory (point at the package root, NOT dist/)
cargo-ai hosting deployment create --app-uuid <uuid> --source ./my-app
cargo-ai hosting deployment create --worker-uuid <uuid> --source ./my-worker
# default ignores: node_modules,dist,build,.git,.next — override with --ignore "a,b,c"
# Go live
cargo-ai hosting deployment promote --uuid <deployment-uuid>
# Download what a deployment was built from — the uploaded pre-build source
# tree as a tar.gz, not the compiled bundle (default ./deployment-<uuid>-source.tar.gz)
cargo-ai hosting deployment export <deployment-uuid> --output ./live-source.tar.gz
本地副本丢失或与实际部署不一致时,可通过 deployment export 恢复:导出已晋升的部署(get-promoted 会返回其 uuid),解包,然后从中重新部署。
关键规则
--slug在每个工作区内必须唯一,线上主机名格式为<slug>-<workspace prefix>.<root>,应用和 worker 使用不同的根域名。请使用get返回的url,而不要自行拼接(参见 URL)。重复的 slug 会导致create失败,并返回duplicateSlug。- 应用调用 worker 属于跨源请求。 worker 必须针对应用的源发送 CORS 标头。不支持同源挂载。
CARGO_API_TOKEN需要由你提供。 只有为 worker 设置了--run-as-user-uuid时,才会注入此变量;没有此变量时,createCargoApi会抛出错误。请在需要此变量的部署之前,将其设为机密(工作区或 worker 环境变量)。- 更改环境变量后,需要重新部署并提升部署。 变量值在提升部署时绑定,非机密变量会编译进包中,因此编辑变量后,必须等到新的部署上线,更改才会生效。
- 先记录日志,再清理错误。 只有未捕获的错误才会带堆栈信息进入日志。若
catch返回了友好提示,必须先执行console.error(err),否则错误原因就会丢失。 - 部署 ≠ 上线。
deployment create会构建并上传;只有执行deployment promote提升该部署后,URL 才会更改。使用deployment get-promoted查看当前线上版本。 --source指向的是包根目录,而不是dist/。 构建会在 Cargo 沙箱中运行:应用会先执行npm ci --ignore-scripts,再运行应用的build脚本(或框架默认脚本);worker 则会打包入口点。提供预构建的dist/无法正常运行。- 应用环境变量是公开的,绝不会是机密。 它们会编译进包中,且会拒绝
isSecret。请将凭据放在 worker 中。 - Cargo 管理的主机设有
noindex。 公共网站需要自定义域名(仅 API 1.0.96 及以上支持)和预渲染 HTML,才能被索引。 - 构建是异步的——在提升部署之前,轮询
deployment get,直到部署进入终态(见下文)。 deployment create、deployment list和deployment get-promoted中的--app-uuid与--worker-uuid互斥。 必须且只能传入其中一个。remove会级联删除——移除应用或 worker 时,也会移除其所有部署。update --folder-uuid null(字面字符串null)会将资源移回工作区根目录。- 托管服务按资源每月消耗额度。 每个应用和 worker 都有
chargedUntil,每小时运行的扫描任务会将其按月向后推进。因此,线上应用或 worker 会持续消耗托管额度——请对不再提供服务的资源执行remove。通过cargo-billing跟踪用量。
异步轮询
deployment create 会启动沙箱构建。部署的 status 会依次变为 pending → building → success(或 error / cancelled)。轮询直到进入终态,然后提升状态为 success 的部署:
cargo-ai hosting deployment get <deployment-uuid> # poll ~2–5s until status is terminal
终态包括 success、error 和 cancelled——只能提升状态为 success 的部署。若状态为 error,请查看部署的 errorMessage(以及 buildLogS3Filename)以诊断构建问题。有关通用轮询模式(间隔、重试),请参阅 ../cargo-orchestration/references/polling.md。
帮助
每条命令都支持 --help:
cargo-ai hosting app create --help
cargo-ai hosting deployment create --help