npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vchar/nos-cli

v0.1.18

Published

NetEase NOS upload CLI

Readme

lnos-cli

NetEase NOS (网易对象存储) 命令行工具。Node.js 18+,支持单文件 PUT / 大文件分片上传(断点续传)、同桶重命名、图片/音视频信息查询,以及 CDN 缓存刷新。提供 AK/SK 直签后端 token 两种鉴权。


安装

# 官方 npm(npmjs.com)
npm i -g @vchar/nos-cli

# 或从源码
git clone <repo> && cd nos-cli && npm install && npm run build
npm i -g .          # 或 npm link

安装后即可使用 lnos-cli 命令(bin 名为 lnos-cli)。

前置要求

  • Node.js >= 18
  • 鉴权二选一:
    • AK/SK 模式:NOS AccessKey / SecretKey(网易云控制台获取)
    • token 模式:后端 token 签发端点(默认指向 https://ai-service-lofter.hz.netease.com/api/mcp/nos/token,由 virtual-service-mcp 提供)

配置管理

配置持久化保存在 ~/.nos-cli/config.json,文件权限 0600(仅当前用户可读写)。

# —— AK/SK 直签模式 ——
lnos-cli config set accessKey <你的 AccessKey>
lnos-cli config set secretKey <你的 SecretKey>
lnos-cli config set bucket <bucketName>          # AK/SK 模式必填

# —— 后端 token 模式(凭证由后端签发,无需本地 AK/SK)——
# 配了 tokenEndpoint 即启用 token 模式;默认指向公网 MCP 服务,可不配
lnos-cli config set tokenEndpoint <url>          # 可选,默认 https://ai-service-lofter.hz.netease.com/api/mcp/nos/token(可覆盖)
lnos-cli config set tokenHeaders '<json>'         # 鉴权头(Bearer skt-)
# skt- token 获取:访问 https://ai-service-lofter.hz.netease.com/skill-tokens 申请(需 nos-upload 权限)

# —— 通用 ——
lnos-cli config set net <auto|inner|outer>      # 网络模式,默认 auto
lnos-cli config set partSize <16>                 # 分片大小 MB,默认 16
lnos-cli config set encrypt <true|false>          # secretKey AES-256-GCM 加密存储

lnos-cli config get <key>                          # 查看单项
lnos-cli config list                                # 列出全部(secretKey 显示为 ****)

说明:

  • 鉴权优先级:配了 tokenEndpoint → token 模式(凭证后端签发、x-nos-token 头、object key 由后端生成 UUID);否则 AK/SK 直签(本地 HMAC-SHA256 签名、object key 本地命名)。token 模式下 bucket 可不配(后端返回;配置则为 -b 提示后端签进 token)。
  • net 取值:
    • auto(默认)— 自动探测:TCP 连接 nos-jd.service.163.org:80(800ms 超时),可达则用内网,否则用外网。
    • inner — 强制内网端点 nos-jd.service.163.org(http)。
    • outer — 强制外网端点 nos-jd.163yun.com(https)。内网 443 暴露的是外网证书,故内网走 http 避免 TLS 域名校验失败。
  • encrypt true 启用后,secretKey 由机器指纹派生密钥加密,写入磁盘时为密文,读取时自动解密。注意这是防窥屏级别的保护,不是强加密。
  • config list 显示时 secretKey 固定输出 ****,不会泄漏明文。

上传文件

基本用法

lnos-cli upload <file...>

支持同时上传一个或多个文件。上传成功后会打印三个 URL(均不含 URL 编码,可直接访问):

✅ 上传成功:photo.jpg
   内网: https://my-bucket.nos-jd.service.163.org/20260711234200000_a1b2c3/photo.jpg
   外网: https://my-bucket.nos-jd.163yun.com/20260711234200000_a1b2c3/photo.jpg
   CDN : https://my-bucket.nosdn.127.net/20260711234200000_a1b2c3/photo.jpg

选项

| 选项 | 简写 | 说明 | |------|------|------| | --bucket <bucket> | -b | 覆盖默认 Bucket | | --concurrency <n> | -c | 上传并发数(默认 = min(CPU 核数, 6);不设上限) | | --net <mode> | | 网络模式:auto / inner / outer | | --key <objectName> | | 自定义对象名(仅单个文件时可用) | | --part-size <mb> | | 分片阈值和分片大小,单位 MB | | --abort | | 中止指定文件的分片上传并清理状态(需同时使用 --key) |

分片上传与断点续传

  • 文件大小 ≤ 分片大小时,走单次 PUT 上传。
  • 文件大小 > 分片大小时,自动走 Multipart Upload(分片上传)。
  • 上传过程中的状态(uploadId、已上传分片的 ETag)实时写入 ~/.nos-cli/state/ 目录。
  • 中断后重跑同一文件:自动检测已有的分片状态,跳过已上传分片,只上传缺失部分(断点续传)。
  • 文件 mtime 发生变化(被修改后重试),旧状态自动作废,从头重新上传。
  • 上传成功后,状态文件自动清理。

--key 和 --abort 约束

  • --key 仅能用于单个文件,否则会报错。
  • --abort 需要同时指定 --key(即之前上传时使用的对象名),因为随机生成的对象名无法回推。示例:
lnos-cli upload large-file.iso --key custom/object-name --abort

真实示例(端到端)

# 1. 配置
lnos-cli config set accessKey "your-ak"
lnos-cli config set secretKey "your-sk"
lnos-cli config set bucket "my-bucket"
lnos-cli config set net "auto"

# 2. 上传
lnos-cli upload ./screenshot.png ./report.pdf

# 3. 输出示例
✅ 上传成功:screenshot.png
   内网: https://my-bucket.nos-jd.service.163.org/20260711234200000_x1y2z3/screenshot.png
   外网: https://my-bucket.nos-jd.163yun.com/20260711234200000_x1y2z3/screenshot.png
   CDN : https://my-bucket.nosdn.127.net/20260711234200000_x1y2z3/screenshot.png
✅ 上传成功:report.pdf
   内网: https://my-bucket.nos-jd.service.163.org/20260711234200000_x1y2z3/report.pdf
   外网: https://my-bucket.nos-jd.163yun.com/20260711234200000_x1y2z3/report.pdf
   CDN : https://my-bucket.nosdn.127.net/20260711234200000_x1y2z3/report.pdf

对象管理

除上传外,还支持删除、重命名和查询对象信息。这些命令的目标对象都可用两种形式指定:

  • objectKey(如 dir/photo.png)— 使用配置或 -b 指定的 Bucket;
  • 完整 NOS URL(内网 / 外网 / CDN 任一)— 自动解析出 Bucket 和 objectKey。

所有命令均支持 -b/--bucket--net 选项。

删除对象

⚠️ 此命令不在 --help 中显示,但可用。

lnos-cli delete <objectKey|url>...
  • 可一次删除多个对象。
  • 直接删除,无二次确认(删除不可逆,请谨慎)。
lnos-cli delete clitest/pic.png
lnos-cli delete "https://gz-vchar-pub.nosdn.127.net/clitest/a.png" clitest/b.png
# 🗑️  已删除:gz-vchar-pub/clitest/pic.png

重命名 / 移动对象

lnos-cli rename <源> <新完整key>
  • 基于 NOS 的 PUT Object Move(改元数据,不拷贝物理文件)。
  • 仅支持同一 Bucket 内(NOS 不支持跨桶 move)。
  • 新完整key 是完整的目标对象名(不保留源目录前缀)。
  • 成功后打印新对象的三个 URL。
lnos-cli rename clitest/pic.png clitest/renamed.png
# ✅ 重命名成功:clitest/pic.png → clitest/renamed.png
#    内网/外网/CDN: ...

查询对象信息

lnos-cli imageinfo <target>   # 图片基本信息:Width / Height / Size / Type / Orientation
lnos-cli exif <target>        # 图片 EXIF 信息(无 EXIF 时返回空对象)
lnos-cli vinfo <target>       # 音视频信息:时长 / 码率 / 编解码 / 分辨率等

以 JSON 打印结果。示例:

lnos-cli imageinfo clitest/pic.png
# {
#   "Width": "1210",
#   "Height": "1726",
#   "Size": "1758637",
#   "Type": "PNG",
#   "Orientation": ""
# }

lnos-cli vinfo clitest/test.mp4
# { "GetVideoInfo": { "VideoInfo": { "Duration": 14149, "Width": 846, "Height": 1920, ... } } }

注:imageinfo / exif 的 NOS 响应为 XML(工具会解析为扁平对象),vinfo 响应为 JSON,工具自动识别两种格式。


CDN 缓存刷新

提交 CDN 缓存刷新任务(文件 / 目录 / 混合)。

lnos-cli cdn refresh <url...> [--type <file|dir|mix>] [--domain-id <id>] [--isp <isp>]
  • 走公网 MCP 服务 https://ai-service-lofter.hz.netease.com/api/mcp/cdn/refresh(virtual-service-mcp 在内网代调上游刷新接口,无需 VPN/内网,token 由后端内部复用,lnos-cli 不单独取 token)。
  • --typefile(文件,默认) / dir(目录) / mix(混合,mix 时目录 URL 需以 / 结尾)。
  • URL 最多 100 条,需以 http(s):// 开头。
  • --domain-id / --isp 可省略;省略时刷新所有匹配域名。
# 刷新单个文件
lnos-cli cdn refresh "https://my-bucket.nosdn.127.net/abc.jpg"

# 刷新目录
lnos-cli cdn refresh --type dir "https://example.com/dir/"

# 混合(文件 + 目录)
lnos-cli cdn refresh --type mix "https://example.com/a.jpg" "https://example.com/dir/"

# 指定域名 ID / 厂家
lnos-cli cdn refresh --domain-id 123123 --isp cmcc "https://example.com/a.jpg"

成功输出:

✅ CDN 刷新任务已提交:fusion域名my-bucket.nosdn.127.net新增文件刷新
   任务 ID: 7870403197

失败时逐条列出上游返回的 {url, detail, suggest}


对象命名规则

不指定 --key 时,对象名自动生成:

{timestamp}_{rand6}/{baseName}.{ext}
  • timestamp — 上传时刻的北京时间,格式 yyyyMMddHHmmssSSS(17 位数字)。
  • rand6 — 6 位随机字母数字(a-z, 0-9)。
  • baseName — 原文件名去除扩展名,中文转无调拼音(通过 pinyin-pro 库),非法字符(非字母数字和下划线)被移除。
  • .ext — 原文件扩展名保留。

示例:我的照片 (v2).jpg20260711234200000_a1b2c3/wodezhaopianv2.jpg

注:token 模式下 object key 由后端生成(UUID + 原扩展名),不走上述本地命名规则。


Content-Type 适配

上传时按 object 扩展名自动设置 Content-Type(参考后端 FileTypeEnum + RFC/IANA 注册类型补齐)。

  • NOS 服务端有 Content-Type 白名单:白名单外的类型(即使 RFC 注册)会被 NOS 强制回落 application/octet-stream。故 text/markdownapplication/yamlapplication/tomlapplication/sql被拒的文本类型这里回落 text/plain(RFC 2046,NOS 接受),使其在浏览器能在线渲染而非强制下载;font/*(二进制)保留 RFC 真值。
  • 无扩展名 → 不设 Content-Type(交给 NOS 嗅探);未知扩展名 → application/octet-stream

NOS 签名说明

本工具不依赖 NOS 官方 SDK,签名逻辑完全自实现:

  • 使用 HMAC-SHA256 对规范字符串进行签名,输出 Base64。
  • 规范字符串格式为 METHOD\nContent-MD5\nContent-Type\nDate\n[x-nos-* 头]\nResource,与 NOS 文档一致。其中 x-nos-* 头(如重命名用的 x-nos-move-source)按头名排序后参与签名。
  • Date 头使用上海时区(Asia/Shanghai)的 RFC 822 格式,与官方 Java SDK 对齐。
  • Resource(签名字符串中的对象路径)中的对象 key 做了 URL 编码,保证特殊字符不破坏签名。
  • 部分子资源参与签名(如分片上传的 ?uploads?uploadId),而信息查询子资源(?imageInfo?exif?vinfo参与签名——这些差异均已通过真机验证确认。

签名实现已通过官方 NOS Java SDK 生成的签名向量做字节级交叉验证:向量固化在 test/fixtures/nos-sign-vectors.json,TypeScript 实现在同一输入上输出与官方 SDK 完全一致。所有对象操作(上传 / 删除 / 重命名 / 信息查询)均已针对真实 NOS 服务端到端验证通过。


开发 / 迭代

npm test              # 运行单元测试(vitest)
npm run typecheck     # TypeScript 类型检查
npm run build         # 构建,产出 dist/cli.js

项目技术栈:TypeScript + commander(CLI 框架)+ undici(HTTP 客户端)+ vitest(测试框架)。

当前代码覆盖 55 个单元测试,涵盖签名、命名、配置、断点续传、并发池、端点探测、分片上传、URL 解析、对象操作、token 模式、mimetype、CDN 刷新等核心逻辑。


阶段二 (Go)

计划将相同的上传协议逻辑移植为 Go 单二进制工具,消除 Node.js 运行时依赖,实现更小的分发体积和更简单的部署。签名、续传、分片等已验证的协议逻辑将直接复用。