@everystack/cli

中文文档
0.4.107发布于 2026/11/01AGPL-3.0-onlyTypeScriptCommonJS

博览会应用的CLI和OTA更新

中文使用文档
文档目录25 节
  1. 安装
  2. 入口点
  3. 服务器:处理程序
  4. 越来越多
  5. 处理程序端点
  6. 处理程序选项
  7. 存储适配器
  8. 文件系统
  9. S3
  10. 自定义适配器
  11. 命令行界面
  12. 发布更新
  13. 代码签名
  14. 渠道管理
  15. 部署
  16. 数据库管理
  17. 交互式控制台
  18. 客户端:React Native
  19. UpdatesProvider
  20. AppStateUpdateProvider
  21. 模式
  22. 博览会更新协议
  23. 它是如何工作的
  24. 同行依赖性
  25. 许可证

@everystack / cli

博览会应用的CLI和OTA更新。数据库管理(db:migrate, db:seed), OTA发布,Expo Updates协议(v0/v1),可插拔存储,RSA-SHA256代码签名和通道。

安装

pnpm add @everystack/cli drizzle-orm structured-headers

对于S3存储:

pnpm add @aws-sdk/client-s3

入口点

进口描述
@everystack/cli服务器:处理程序、存储适配器
@everystack/cli/client客户端:UpdatesProvider, AppStateUpdateProvider
@everystack/cli/schema细雨表(渠道、发布、资产)

服务器:处理程序

创建一个实现Expo Updates清单协议的Web标准处理程序:

import { createUpdatesHandler, createStorage } from '@everystack/cli';
import { db } from './db';

const storage = createStorage({
  type: 'filesystem',
  directory: './updates',
});

const handler = createUpdatesHandler({
  db,
  storage,
  baseUrl: 'https://myapp.com',
  basePath: '/api/updates',
  defaultChannel: 'production',
  auth: {
    verifyToken: async (token) => verifyJWT(token),
  },
  privateKey: process.env.CODE_SIGNING_PRIVATE_KEY, // PEM for RSA-SHA256
});

越来越多

// app/api/updates/[...path]+api.ts
export function GET(request: Request) { return handler(request); }
export function POST(request: Request) { return handler(request); }

处理程序端点

处理程序提供:

  • 清单要求 - Expo以多部分/混合格式更新协议v0/v1清单响应
  • 资产下载 -二进制资产从您配置的存储
  • 发布端点 —经过认证的新版本上传(CLI使用)

处理程序选项

interface UpdatesHandlerOptions {
  db: DrizzleDb;                // Drizzle instance
  storage: StorageAdapter;      // Filesystem or S3
  baseUrl: string;              // Public URL (for asset references in manifests)
  basePath?: string;            // URL prefix to strip
  auth?: {
    verifyToken: (token: string) => Promise<Record<string, unknown> | null>;
  };
  privateKey?: string;          // PEM for RSA-SHA256 code signing
  defaultChannel?: string;      // Default channel (default: 'production')
}

存储适配器

文件系统

import { createStorage } from '@everystack/cli';

const storage = createStorage({
  type: 'filesystem',
  directory: './updates',
});

S3

const storage = createStorage({
  type: 's3',
  bucket: 'my-updates-bucket',
  region: 'us-east-1',
  endpoint: 'https://s3.us-east-1.amazonaws.com', // Optional
});

S3客户端为 默认有界 所以单个调用永远不会挂起:a 连接超时(3秒),每个请求超时(10秒),有界重试(3次尝试), 还有一个帽子 list() (有页面挡板的页面)。这是最重要的 可观察性卷起- S3 503风暴下的无界客户端阻塞直到 Lambda自己的超时,并且每个挂起的调用都固定一个并发插槽,因此 它可以耗尽账户池并使它所观察到的应用程序饿死。

每个字段通过调优 options,或通过env var覆盖超时/重试 无需更改代码(env可以方便地在Lambda上转动旋钮):

const storage = createStorage({
  type: 's3',
  bucket: 'my-updates-bucket',
  region: 'us-east-1',
  options: {
    connectionTimeoutMs: 3000, // env: EVERYSTACK_S3_CONNECT_TIMEOUT_MS
    requestTimeoutMs: 10000,    // env: EVERYSTACK_S3_REQUEST_TIMEOUT_MS
    maxAttempts: 3,             // env: EVERYSTACK_S3_MAX_ATTEMPTS
    listMaxKeys: 1000,          // keys per ListObjectsV2 page
    listMaxPages: 1000,         // hard backstop before list() truncates (warns)
  },
});

每个字段的优先级:显式 options > env var > default。

自定义适配器

实现 StorageAdapter 接口:

interface StorageAdapter {
  put(key: string, data: Buffer | Uint8Array, contentType?: string): Promise<void>;
  get(key: string): Promise<{ data: Buffer; contentType: string } | null>;
  exists(key: string): Promise<boolean>;
  list(prefix: string): Promise<string[]>;
  delete(key: string): Promise<void>;
}

命令行界面

从命令行发布更新、管理证书和通道。

发布更新

everystack update \
  --channel production \
  --message "Fix login bug" \
  --platform ios          # ios | android | web | all (default: all)

这将捆绑您的应用程序,将资产上传到存储,创建发布记录,并签署清单。

中配置的媒体资产 app.config.js 下 extra.everystack.assets 使用8个字符的内容哈希(例如, logo.png → logo-a1b2c3d4.png),并同步到媒体桶。一个 media-manifest.json 将逻辑名映射到指纹路径写入桶根。以前部署的过时指纹文件将被自动修剪。

// app.config.js
extra: {
  everystack: {
    assets: [
      { from: 'server/email/assets', to: 'email/', include: '*.{png,jpg}' },
      { from: 'assets/og', to: 'og/' },
    ],
  },
}

使用 @everystack/server/media 在SSR、电子邮件模板和工作程序的运行时解析指纹url。

代码签名

生成用于舱单签名的RSA密钥对:

everystack certs:generate --output ./certs
# Creates ./certs/private-key.pem and ./certs/certificate.pem

everystack certs:configure --input ./certs --keyid main
# Configures your app to use the generated certificates

渠道管理

everystack channels list
everystack channels create --name staging
everystack channels create --name production

部署

everystack deploy --stage dev           # provision infrastructure (wraps `sst deploy`)

在SST上有一个薄包装,所以你永远不会打字 sst 直接。仅限基础设施—迁移和包发布保持显式(db:migrate, update)。

数据库管理

这些命令通过IAM直接调用Lambda(没有数据库凭据离开AWS):

# Migrations
everystack db:migrate                   # apply Drizzle migrations
everystack db:seed                      # seed (dev only)
everystack db:psql --stage dev [-c SQL] # interactive psql / one-off query via Lambda

# Model-driven schema (@everystack/model)
everystack db:generate                  # diff Models vs the live DB → next migration
everystack db:pull                      # introspect a live DB → field() Models (brownfield on-ramp)
everystack db:reconcile                 # deploy the derived layer (functions/views/matviews/triggers) from the declared descriptors in db/models — no migrations
everystack db:fingerprint               # content-address the live base schema vs the Models — MATCH/MISMATCH
everystack db:sync                      # make the database match your checkout — state + compute, one verb (dev DBs)
everystack db:diff                      # the state edge between two declared states — no DB, CI-pure; reverse = computed rollback
everystack db:plan | db:apply           # mint a reviewable plan against a target, apply it fingerprint-verified at both ends + the fast-forward rule (checkout must descend from the commit declaring the target's state); destructive plans: --confirm + snapshot + approver set when declared
everystack db:check                     # the CI gate per PR: merged declared state composes + artifacts match regeneration (+ ephemeral from-scratch build w/ a scratch PG)
everystack db:approvers                 # declare who can DESTROY: the stage's destructive-approver set (SSM, STS-verified at apply)
everystack db:backfill                  # one-shot data jobs in db/backfills/*.sql — content-hash identity, own per-DB record, never a schema side effect
everystack db:template:refresh          # (re)build the dev template from the declared state + your db:seed script — never a data copy
everystack db:branch                    # per-branch dev DB minted from the template, keyed on the git branch (--list / --prune --confirm)
everystack db:fork                      # fork a deployed stage's DB into a feature stage (backup → presigned hand-off → restore); production is never a target

# Security
everystack db:doctor                    # is the DB least-privilege + RLS-subject?
everystack db:provision                 # role chain + converge declared ownership (ENACT grants, ADOPT plain objects)
everystack db:provision --confirm       # + ADOPT SECURITY DEFINER functions / views / matviews (runtime change)
everystack db:authz:pull|diff|test|owner|report   # the authorization contract + red-team

# Backups (see docs/backups.md)
everystack db:backup | db:backups | db:restore --from <id> --confirm   # logical pg_dump → S3
everystack db:backup:download <id>      # presigned URL to a dump (1h)
everystack db:snapshot | db:snapshots   # physical RDS snapshots

完整的命令参考-包括 secrets, logs:*, status, cache:purge, branches每一面旗帜——都在 文档/ cli.md.

交互式控制台

everystack console --stage dev          # Connect to deployed database
everystack console --stage dev --sandbox  # Sandbox mode: rolls back after each expression

连接到已部署数据库的REPL db, schema, eq/and/or 范围内的helper。支持多行输入、持久历史记录和制表符完成。

点命令:

  • .tables / .schema [table] -检查数据库模式
  • .login -使用电子邮件/密码(隐藏输入)进行身份验证
  • .user -显示当前授权上下文
  • .logout —清除授权上下文
  • .sandbox -切换沙盒模式

后 .login,查询通过应用RLS策略运行 pgSettings。这个 current_user 变量在作用域中可用。密码是根据历史编辑的。

客户端:React Native

UpdatesProvider

包装你的应用来检查和应用OTA更新:

import { UpdatesProvider } from '@everystack/cli/client';

function App() {
  return (
    <UpdatesProvider
      url="https://myapp.com/api/updates"
      channel="production"
      checkInterval={60000} // Check every 60 seconds
      onUpdateAvailable={(update) => {
        // Optional: prompt user or auto-apply
        console.log('Update available:', update.message);
      }}
      onUpdateApplied={() => {
        console.log('Update applied, restarting...');
      }}
    >
      <MyApp />
    </UpdatesProvider>
  );
}

AppStateUpdateProvider

当应用程序从后台返回时检查更新:

import { AppStateUpdateProvider } from '@everystack/cli/client';

function App() {
  return (
    <AppStateUpdateProvider url="https://myapp.com/api/updates" channel="production">
      <MyApp />
    </AppStateUpdateProvider>
  );
}

模式

将更新表添加到Drizzle迁移中:

import { channels, releases, assets } from '@everystack/cli/schema';

表:

  • 渠道 -命名的发布渠道(生产、发布等)
  • 释放 -发布带有元数据的更新包
  • 资产 -发布引用的单个资产文件

博览会更新协议

处理程序实现了完整的Expo Updates清单协议:

  • 协议v0:旧的Expo SDK版本的遗留格式
  • 协议v1:现代多部分/混合响应格式
  • 代码签名: manifest指令上的RSA-SHA256签名
  • 平台过滤:根据请求头提供特定于平台的包
  • 通道路由:多个发行渠道与独立的版本轨道

它是如何工作的

  1. Expo应用程序发送带有平台、运行时版本和当前更新ID的清单请求
  2. 处理程序查找所请求通道和平台的最新版本
  3. 如果存在更新的版本,它将返回带有资产url的签名清单
  4. Expo运行时下载资产并应用更新

同行依赖性

包版本要求
drizzle-orm>=0.30.0是
structured-headers^1.0.0是(运行时深度)
@aws-sdk/client-s3>=3.0.0对于S3存储
expo-updates>=0.25.0客户端SDK
react>=18.0.0客户端SDK
react-native>=18.0.0客户端SDK

许可证

agpl - 3.0 ©Scalable Technology, Inc.

对于不能或不希望使用商业许可证的组织,可以使用商业许可证 遵守AGPL-3.0条款。如需商业许可,请联系 [email protected].

版本与发布记录

@everystack/cli 共发布 160 个版本,首次发布于 2026/05/15;过去 12 个月发布了 160 个版本。

版本发布日期距今
0.4.107latest2026/11/012 天前
0.4.1062026/11/012 天前
0.4.1052026/11/012 天前
0.4.1042026/10/313 天前
0.4.1032026/10/259 天前
0.4.1022026/10/2410 天前

其他发布通道:v0-4 → 0.4.64。

关于 @everystack/cli 的常见问题

@everystack/cli 是什么?

博览会应用的CLI和OTA更新

如何安装 @everystack/cli?

在项目目录运行 npm install @everystack/cli,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:drizzle-orm(0.41.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 everystack,可以用 npx everystack 直接运行。

@everystack/cli 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@everystack/cli 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@everystack/cli 可以商用吗?

@everystack/cli 使用 AGPL-3.0-only 许可。最严格的 Copyleft:除分发外,通过网络向用户提供基于它的服务也需要开源对应源码。闭源 SaaS 使用前务必评估。以上为概括,不构成法律意见,请以许可原文为准。

@everystack/cli 还在维护吗?

最新版本 0.4.107 发布于 2026/11/01(2 天前);至今共发布 160 个版本,其中过去 12 个月发布了 160 个;首次发布于 2026/05/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。