首页
/ Puter CLI 实战指南:从终端部署站点与 Worker,管理云端文件与 KV 存储

Puter CLI 实战指南:从终端部署站点与 Worker,管理云端文件与 KV 存储

2026-09-08 16:29:48作者:范靓好Udolf

导读

Puter CLI(@heyputer/cli)是 Puter 官方提供的开发者命令行工具,让你不必离开 Shell 就能管理 Puter 云上的全部资源:一键把静态目录发布为 <subdomain>.puter.site 站点、把单个 JavaScript 文件部署为 <name>.puter.work 的 Serverless Worker、像使用本地 cp/mv/rm 一样操作云端文件系统,还能直接打开一个指向某个 App 或 Worker 的 Key-Value 存储的交互式 JavaScript REPL。读完本文,你将掌握 Puter CLI 的安装、认证、全部子命令的参数与语义,以及其底层与 Puter.js SDK 交互的实现细节,足以在本地与 CI 中完成部署与数据管理。

注意:Puter CLI 当前处于 Beta(0.x)阶段,行为可能在版本间变化。本仓库内对应版本为 src/cli/package.json 中的 0.4.0


安装

CLI 以 npm 全局包形式分发,包名为 @heyputer/cli,命令入口为 puter。仓库内源码位于 src/cli/,其 package.json 声明了 engines.node >= 18,因此需要 Node.js 18+。

npm install -g @heyputer/cli

安装完成后先登录一次,令牌会被保存下来供后续命令复用:

puter login

login 会打开浏览器完成与 Puter 的认证。登录成功后即可开始部署。命令实现与子命令注册可参考 src/cli/src/commands/ 下的 login.jssite.jsworker.jsfs.jskv.js 等模块。


认证与会话管理

puter login 走的是浏览器 OAuth 流程。从 src/cli/src/lib/auth.js 的注释可以看到,平台已不再支持用户名/密码登录("username/password login is no longer supported by the platform"),因此只保留 Web 浏览器认证与令牌注入两种方式。

无浏览器环境(如远程服务器):通过 stdin 传入令牌:

echo "$TOKEN" | puter login --with-token

src/cli/src/lib/auth.js 的实现中,readStdin() 逐块读取 process.stdin 后去除首尾空白,目的就是让令牌不出现在 argv、Shell 历史或 ps 输出中。

自动化与 CI:设置环境变量 PUTER_AUTH_TOKEN 后,CLI 每次命令都会直接读取它而完全跳过登录:

puter whoami     # 显示当前令牌对应的账户
puter logout     # 清除本地保存的令牌

令牌的解析顺序在 src/cli/src/lib/config.js 中清晰可见:PUTER_AUTH_TOKEN 优先,其次才是本地存储的令牌。令牌持久化使用 conf 库(projectName: 'puter-cli'),保存形状为未来多账户预留的 { accounts: { default: { token } }, active: 'default' },并尝试将配置文件权限收紧为 0o600(仅属主可读写)。作者在注释中特别说明:纯文本 + 文件权限是部署 CLI 可接受的基准,conf 自带的 encryptionKey 只是混淆而非安全,因此刻意未使用。

logout 仅清除本地存储的令牌(config.js 中的 clearToken()),不会吊销该令牌本身。


站点(Sites):静态托管一键部署

将本地静态目录部署为 <subdomain>.puter.site 地址,然后即可查看、管理或删除自己的站点:

puter site deploy ./dist my-app

不带参数直接运行 puter site deploy 时,CLI 会进入交互模式,逐项提示你输入目录与子域名,并为你建议一个可用的名字。

版本化部署:每次 deploy 都会上传到独立文件夹,因此历史版本会被保留,不会因为重复部署而覆盖旧版本——这让"部署出错回滚到上一版"成为可能。

常用子命令:

puter site deploy [dir] [subdomain]   # 部署一个目录
puter site list                       # 列出你的站点
puter site get <subdomain>            # 查看某个站点的详情
puter site delete <subdomain>         # 删除一个站点

约束与行为:

  • 非交互模式下 dirsubdomain 均为必填参数;
  • 子域名只允许小写字母、数字与连字符(连字符不能位于首尾);交互模式下直接粘贴完整的 my-app.puter.site 形式的域名也可以被接受;
  • delete 支持 -y / --yes 跳过确认提示。

站点托管的域名配置可参考 src/cli/src/lib/env.js 中的 SITE_DOMAIN(默认 puter.site),也可通过环境变量覆盖以对接自托管实例(见文末"环境变量"一节)。


Worker:部署 Serverless JavaScript

把单个 JavaScript 文件部署为 Serverless Worker,运行在 <name>.puter.work 地址上。若部署时使用的名字已存在,则原地替换该 Worker 的代码:

puter worker deploy ./api.js my-api

与站点一样,不带参数运行 puter worker deploy 会交互式提示你输入文件名与名字。

puter worker deploy [file] [name]   # 部署或替换一个 worker
puter worker list                   # 列出你的 workers
puter worker get <name>             # 查看某个 worker 的详情
puter worker delete <name>          # 删除 worker

约束与行为:

  • 非交互模式下 filename 均为必填参数;
  • 名字允许字母、数字与连字符(连字符不能位于首尾);
  • delete 支持 -y / --yes 跳过确认,命令会删除 Worker 及其背后的文件;
  • 从你自己的账户部署 Worker 时,Puter 会为它创建一个独立的沙箱 App(见 src/cli/src/lib/appTarget.jsresolveWorker()app_uid 的读取),该 Worker 的 puter.kv 数据就存放在这个 App 的 KV 存储中——这正是下文"用 Worker 名字连接 KV"能够成立的原因。

关于 Worker 事件模型与运行时,可进一步阅读 Worker 文档 与仓库中的 worker/ 运行时实现。


Apps:查看账户下的已注册应用

以下命令为只读操作,用于浏览注册到你账户下的应用:

puter app list           # 列出你的 apps
puter app get <name>     # 查看某个 app 的详情

该子命令对应的底层 API 封装见 src/cli/src/lib/apps.js


Files:在终端操作云端文件系统

文件子命令让你从终端直接操作 云存储:列出目录、读取文件、双向拷贝文件/文件夹、移动、删除与查看元信息。

路径模型是本组命令的核心:

  • 远端路径带有 puter: 前缀,且相对于你的主目录(home)绝对定位;
  • - 表示 stdin 或 stdout;
  • 其余任何形式一律视为本地路径。

因此,传输方向由路径本身决定,而不是由某个 flag 决定

puter fs ls puter:/Desktop
puter fs cat puter:/notes.txt
puter fs cp -r ./dist puter:/Documents/backup

cp 依据你给出的路径对自动决定行为:

From(源) To(目标) 发生什么
本地 puter:/… 上传
puter:/… 本地 下载
puter:/… puter:/… 在服务器端直接复制,不经由你的机器
- puter:/… 将 stdin 写入该文件
puter:/… - 将文件字节写入 stdout

两个本地路径——或任意其他无法判定的配对——会直接报错,而不是替你猜测。mv 仅在你的 Puter 存储内部生效;若要在本机与 Puter 之间"移动",请先 cp、检查副本无误后再删除源文件。

src/cli/src/lib/remotePath.js 中可以看到这一模型的落地细节:

  • HOME_ROOT 被定义为 '~'(注释说明:SDK 会在服务端展开 ~,而 / 在 Puter 上是系统根、列出的是用户名,不是用户键入 puter:/ 时想要的语义);
  • copyDirection() 根据 local / remote / stdio 三种形态的组合返回 upload / download / remoteCopy / writeStdin / readStdout,并对 local→localstdio→stdio 等歧义组合抛出带 hint 的错误(src/cli/src/lib/remotePath.js);
  • 远端路径是绝对定位的,不存在"远端工作目录",因此相对路径会被拒绝;同时路径会被钳制在根之下,.. 会被拒绝而不是静默改写(assertRemoteOperandsrc/cli/src/lib/remotePath.js)。

Piping(管道)

状态信息、进度与提示一律输出到 stderr,数据输出到 stdout,因此可以安全地管道拼接而不必担心混入状态文本:

puter fs ls puter:/logs | xargs -n1 puter fs cat

交互终端中你看到的是可读视图:裸文件名;-l 时对齐的列;传输过程中的进度 spinner。当输出被管道化或重定向时,ls 改为逐行打印完整的 puter: 路径(对应 remotePath.jstoOperand(),让每行都能直接喂给下一条命令),而 --json 则打印完整的条目数据——lsstat 均支持。

App 存储(--app

每个 App 都有自己的存储目录。--app 会让 puter:/ 解析到该目录(而非你的主目录),从而直接读取与编辑某个 App 正在使用的文件。它接受的标识符与 puter kv connect 完全一致——app 名、app uid、worker 名或 worker URL:

$ puter fs ls --app notes puter:/
puter:/settings.json

remotePath.js 可见其底层解析目标:~/AppData/<uid>,这与 puter.js 把 App 的相对路径解析到 ~/AppData/<uid> 的规则一致。--app 只是一个 flag:它没有对应的环境变量,也没有可保存的默认值——因为它改变的是"绝对路径指代什么"这种根本语义。任何写入或删除命令都会打印它实际解析到的路径,方便你确认它带来的差异:

$ puter fs rm -r --app notes puter:/cache
rm -r puter:/cache → ~/AppData/app-1f2e3d4c…/cache (412 entries) [notes (app-1f2e3d4c…)]

沙箱是强制的:路径会被限制在该目录内部,puter:/../../Documents 会报错,而不是成为越狱出口。

删除文件

rm 删除单个文件。删除目录需要 -r,它会先打印解析后的路径与包含的条目数,然后在终端中请你确认;在没有交互对象时则必须加 --yesputer:/ 本身永远被拒绝删除。任何递归操作都支持 --dry-run,只列出将被删除的内容而不会真正删除。

被删除的文件不会进入回收站(Trash)——puter fs rm 是直接移除。

传输(Transfers)

复制文件夹时默认每次并发传输 8 个文件,--concurrency 可调整(范围 1–32);对看起来暂时性失败的单个文件会自动重试。对应实现见 src/cli/src/lib/transfer.js

  • 默认并发 DEFAULT_CONCURRENCY = 8;每个批次上限 100 个文件或 8 MiB(MAX_BATCH_FILES / MAX_BATCH_BYTES,文件在内存中打包,故以数量与体积双上限约束);
  • 重试上限 3 次,退避基数为 500ms 并逐次翻倍(ATTEMPTS / BASE_BACKOFF_MS);
  • 只有 5xx、429 与网络类错误(ECONN*ETIMEDOUTENOTFOUNDEAI_AGAINEPIPEENETUNREACHESOCKET 等)才值得重试,4xx 不会改变主意(isRetryabletransfer.js);
  • 上传走 SDK 的 puter.fs.upload() 签名批量写路径(每次调用是一次签名批量写,而非逐文件请求);失败时服务端会回报 failedPaths/failedItems,重试只补发失败的那部分(attributeFailures);
  • 大于 32 MiB 的远端文件在下载时按 8 MiB 分块(走 offset/byte_count 的 Range 请求)以保持内存平稳(CHUNKED_READ_THRESHOLD / READ_CHUNK_BYTES)。

即使仍有文件持续失败,其余文件会继续复制,失败清单在结束时统一列出,命令以非零状态退出——因此一次大型上传不会因为单个文件失败而整体重来。-n / --no-clobber 跳过已存在的文件;不带它时 cp 会覆盖。


Key-Value 存储:交互式 JavaScript 终端

puter kv connect 打开一个指向某个 App 或 Workerkey-value store 的交互式 JS Shell,让你绕过应用直接读写它的数据。参数可以是 app 名、worker 名或其 *.puter.work URL,也可以是 uid:

puter kv connect my-app

CLI 会解析对应 App、确认存储可达,然后进入提示符界面:

$ puter kv connect notes
✔ Connected to notes (app-1f2e3d4c…) · 12 keys
kv(notes)> set("greeting", "hi")
true
kv(notes)> get("greeting")
'hi'
kv(notes)> list("gre", true)
[ { key: 'greeting', value: 'hi' } ]

Worker 的接入方式相同。从你的账户部署 Worker 时,它会获得自己独立的沙箱 App,而该 Worker 的 puter.kv 数据正是存放于此——所以输入 worker 名即可连接到它,提示符也会标明当前身处哪个命名空间:

$ puter kv connect my-api
✔ Connected to worker my-api (app-9a8b7c6d…) · 3 keys
kv(worker:my-api)> list()
[ 'visits' ]

名字冲突规则(实现见 src/cli/src/lib/appTarget.jsresolveTarget()):名字优先按 App 解析;若 App 与 Worker 同名,你得到的是 App;此时传入 Worker 的 URL(puter kv connect https://my-api.puter.work)即可显式指定 Worker。URL 会先被 workerNameFromUrl() 解析——它只接受扁平的 <name>.puter.work 形态(剥去 scheme、路径、查询串与端口后按 .puter.work 后缀匹配)。一个由 App 部署(而非由你直接部署)的 Worker 没有自己的存储(无 app_uid),这时应改连拥有它的那个 App。

绑定与求值语义:每一个 puter.kv 方法都会被绑定到所连 App,并可以三种形式裸调用——直接方法名、kv.set(…)puter.kv.set(…),因此从文档中复制的示例可以原样运行。结果会自动 await——get("k") 打印的是值而不是 pending 的 Promise;_ 保存上一次的结果。相关实现见 src/cli/src/commands/kv.js:REPL 复用 Node 内置 node:repl(多行输入、历史、Ctrl-C/Ctrl-D、_util.inspect 格式皆由 Node 提供),CLI 只在其上叠加两层:结果在回显前被 await;失败只打印单行错误而非堆栈。

这是完整的 JavaScript REPL:多行输入、变量与会话间历史记录都能工作。.help 列出 kv 方法,.clear 重置会话,.exit(或 Ctrl-D)退出。会话历史保存在配置文件目录旁的 kv-history 文件中(kv.js),所以跨会话历史是持久的。连接横幅中的键数量是一次轻量探测(单页取 100 条,满则显示 100+ keys),避免对每个键做计费式的精确统计。

通过 puter kv connect 的写入流向所连 App 的存储,而不是你的用户级存储——也就是说,写入的就是该 App 或 Worker 自己读写的那份数据。

非交互上下文下,puter kv connect 会直接以错误退出而非挂起(kv.jsneeds a terminal)。


CLI 参考

全局选项

选项 说明
-v, --version 打印 CLI 版本。
-h, --help 查看任意命令的帮助,例如 puter site deploy --help

CLI 会检测自身是否在交互式环境下运行。终端中它会为缺失的参数逐一提示;非交互环境(CI、输出被管道化,或设置了 CI)中永不提示,此时必填参数必须显式传入。该检测逻辑在 src/cli/src/lib/env.js 中:isInteractive() 要求 stdin 与 stdout 都是 TTY,canPrompt() 只要求 stdin 是 TTY,二者均以 process.env.CI 为硬开关;canAnimate()(spinner/动画/颜色)则仅以 stdout 是否 TTY 判定,因此即使 stdin 仍是终端,输出被重定向到文件时也会关闭动画。

puter login

登录 Puter 并保存令牌供后续命令使用。

参数 / 选项 说明
--with-token 从 stdin 读取认证令牌,而非打开浏览器。

puter logout

清除已保存的认证令牌。无参数。

puter whoami

显示当前令牌对应的账户。无参数。

puter site deploy

将静态目录部署到 <subdomain>.puter.site

参数 说明
[dir] 要部署的目录。交互模式下省略时会被提示输入。
[subdomain] 目标子域名。交互模式下省略时会被提示输入;粘贴完整主机名如 my-app.puter.site 也可被接受。

非交互模式下两个参数均必填。子域名允许小写字母、数字与连字符(连字符不能位于首尾)。

puter site list

列出你拥有的子域名及其 URL。无参数。

puter site get

查看单个站点的详情。

参数 说明
<subdomain> 要查看的子域名。

puter site delete

删除一个子域名。

参数 / 选项 说明
<subdomain> 要删除的子域名。
-y, --yes 跳过确认提示。

puter worker deploy

将一个 JavaScript 文件部署为 <name>.puter.work 下的 Serverless Worker,或替换已存在的同名 Worker。

参数 说明
[file] Worker 的 JavaScript 文件。交互模式下省略时会被提示输入。
[name] Worker 名字。交互模式下省略时会被提示输入。

非交互模式下两个参数均必填。名字允许字母、数字与连字符(连字符不能位于首尾)。

puter worker list

列出你的 Worker 及其 URL。无参数。

puter worker get

查看单个 Worker 的详情。

参数 说明
<name> 要查看的 Worker。

puter worker delete

删除一个 Worker 及其背后的文件。

参数 / 选项 说明
<name> 要删除的 Worker。
-y, --yes 跳过确认提示。

puter app list

列出注册到你账户下的 App。无参数。

puter app get

查看单个 App 的详情。

参数 说明
<name> 要查看的 App。

puter fs ls

列出远端目录。

参数 / 选项 说明
<path> 要列出的远端路径(puter:/…)。
-l, --long 显示类型、大小与修改时间。
--json 以 JSON 打印完整条目。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

puter fs cat

将远端文件内容写到 stdout。

参数 / 选项 说明
<path> 要读取的远端文件(puter:/…)。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

puter fs cp

在本机与 Puter 之间、或 Puter 存储内部复制。

参数 / 选项 说明
<source> 本地路径、远端路径(puter:/…),或读取 stdin 的 -
<destination> 本地路径、远端路径(puter:/…),或写 stdout 的 -
-r, --recursive 复制目录。
-n, --no-clobber 跳过已存在的文件而非覆盖。
--concurrency <n> 同时传输的文件数,1 到 32。默认为 8。
--dry-run 只列出将被复制的内容而不真正复制。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

两个路径中必须有一个是远端路径;两个本地路径之间的复制是错误。

puter fs mv

在你的 Puter 存储内部移动或重命名。

参数 / 选项 说明
<source> 要移动的远端路径(puter:/…)。
<destination> 移动到的远端路径(puter:/…)。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

两个路径都必须是远端路径,且 puter:/ 本身不能被移动。

puter fs rm

删除远端文件或目录。

参数 / 选项 说明
<path> 要删除的远端路径(puter:/…)。
-r, --recursive 删除目录及其全部内容。
-y, --yes 跳过确认提示。非终端下使用 -r 时必须提供。
--dry-run 只列出将被删除的内容而不真正删除。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

puter:/ 本身无论是否带 --app 都会被拒绝删除。

puter fs mkdir

创建远端目录。

参数 / 选项 说明
<path> 要创建的远端目录(puter:/…)。
-p, --parents 创建缺失的父目录;目录已存在时也视为成功。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

puter fs stat

查看单个远端文件或目录的详情。

参数 / 选项 说明
<path> 要查看的远端路径(puter:/…)。
--json 以 JSON 打印完整条目。
--app <id> puter:/ 解析到某个 App 的存储而非你的主目录。

puter kv connect

打开针对某个 App 或 Worker 的 key-value store 的交互式 Shell。

参数 说明
<identifier> 要连接的存储:app 名、app uid(app-…)、worker 名或 worker URL(https://my-api.puter.work)。名字歧义时解析为 App。

需要终端——非交互上下文中该命令以错误退出,而非挂起等待。


环境变量

变量 说明
PUTER_AUTH_TOKEN 代替登录使用的认证令牌,优先级高于已存储的令牌。
CI 设置后 CLI 以非交互方式运行,永不提示。

除文档表格外,从 src/cli/src/lib/env.js 的源码还可以看到三个面向自托管部署的端点覆盖变量:PUTER_API_ORIGIN(默认 https://api.puter.com)、PUTER_SITE_DOMAIN(默认 puter.site)与 PUTER_WORKER_DOMAIN(默认 puter.work)。自托管 Puter 实例时,通过这三个变量即可让 CLI 指向你自己的服务端、站点域名与 Worker 域名,而无需改动任何命令。关于自托管的具体步骤,可参考 doc/self-hosting.md


小结

Puter CLI 的设计处处体现出"面向脚本正确性"的工程取舍:数据走 stdout、状态走 stderr 的管道友好约定;由路径形态而非 flag 决定传输方向的 cp 语义;对歧义配对"响亮报错而非猜测";远端路径绝对定位且钳制在根内(.. 被拒绝);对 puter:/ 根目录的删除保护;上传失败时逐文件续传而不整体重来;以及 PUTER_AUTH_TOKEN / CI 支撑的无人值守运行。若要深入理解命令背后的 API 语义,可继续阅读仓库内的 FS 文档KV 文档Worker 文档,或直接查看 Puter CLI 源码Puter.js SDK

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391