@appshell/cli

中文文档
1.0.0-alpha.148发布于 2026/10/30MITCommonJS构建来源已验证近 30 天下载 6117

cli utility to generate a global appshell artifacts for module federation micro-frontends

原始使用文档
文档目录12 节
  1. Getting Started
  2. Usage
  3. Vocabulary
  4. Authenticating
  5. Publishing a package
  6. Trusted publishing
  7. Declaring an application
  8. Trusted deployment
  9. Inspecting an application
  10. Developing against a deployed application
  11. Analyzing shared dependencies
  12. Generate manifest

Appshell CI

@appshell/cli

Utility for building micro-frontends with Appshell and Webpack Module federation.

Working examples can be found here.

Getting Started

To begin, you'll need to install @appshell/cli:

npm install @appshell/cli --save-dev

or

yarn add -D @appshell/cli

or

pnpm add -D @appshell/cli

Usage

appshell [command]

Commands:
  appshell generate [target]           Generates a resource
  appshell config [target]             Configures the appshell cli
  appshell app <target>                Manage appshell applications
  appshell dev                         Point an application at this package running
                                       locally, for this browser only
  appshell login                       Authenticate with an appshell registry
  appshell logout                      Discard the stored credential for a registry
  appshell publish                     Publish a package to the appshell registry
  appshell unpublish <name> <version>  Remove a published package version, restorable for
                                       the registry's window (30 days by default)
  appshell restore <name> <version>    Bring back an unpublished version inside its window
  appshell purge <name> <version>      Drop an unpublished version's content for good
                                       (needs --yes)
  appshell trust                       Trust CI workflows to publish into a scope, or
                                       workloads to apply its applications, without a
                                       stored secret
  appshell outdated                    Analyzes shared dependencies for outdated versions

These global options apply to every command:

OptionDefaultPurpose
-r, --registryhttp://localhost:7070Registry to operate against
-a, --application—Application, as name or scope/name
--scopeIddefaultScope owning unqualified packages and applications
-k, --apiKey""Api key, when the registry expects one
-v, --verbosefalseVerbose output

--registry and --application fall back to APPSHELL_REGISTRY / APPSHELL_APPLICATION, then to ~/.appshell/config. Set them once instead of passing them every time:

appshell config set registry http://localhost:7070
appshell config set application storefront
appshell config list

Vocabulary

TermIdentityWhat it is
ScopeacmeNamespace and permission boundary
Packageacme/[email protected]What you publish. Immutable, digest-addressed
Applicationacme/storefrontThe composition the registry serves
RemotePingModule/PingWhat the browser loads, over Module Federation

Authenticating

appshell login                       # device flow against the configured issuer
appshell login --clientSecret ...    # client credentials, for CI
appshell logout

The token is stored per registry. A registry running AUTH_MODE=none needs none — commands only fail when the registry says so, and a 401 tells you to log in. APPSHELL_TOKEN overrides the stored credential. A GitHub Actions job can publish with neither — see Trusted publishing.

Publishing a package

appshell publish --template dist/appshell.template.json

Publishing is idempotent: the same content under the same version is a no-op. Different content under an existing version is rejected unless the registry allows forcing. When --application resolves, publish also activates the version there.

OptionWhen omittedPurpose
-t, --templateappshell.template.jsonTemplate to process
--namethe unscoped package.json namePackage name
--package-versionthe package.json versionVersion to publish as
--visibilitythe registry defaults to privateWhether other scopes may activate it
-w, --watchfalseRepublish whenever the template changes
appshell unpublish checkout 1.5.0

Unpublishing takes the version out of every read, but the registry keeps its content for a window, 30 days unless it is configured otherwise. It refuses a version an application has activated. The output says until when the version can be restored, and how:

appshell packages unpublished checkout   # what can still be restored, and until when
appshell restore checkout 1.5.0          # back exactly as it was, same digest
appshell purge checkout 1.5.0 --yes      # gone for good; only after an unpublish

The version's coordinates stay spent either way: the same version can't be published again with other content.

Trusted publishing

A GitHub Actions workflow can publish into a scope with its own OIDC identity, so no secret is stored anywhere. The scope's owner — or an organization admin, for a scope an organization owns — records which workflow may:

appshell trust add acme --github acme/widgets --workflow pipeline.yml --environment production
appshell trust list acme
appshell trust remove acme 1b2c3d4e     # the id as `list` shows it

trust add looks the repository up on GitHub and records it by its immutable ids, so a renamed or transferred repository, or a new one taking the old name, does not inherit the trust. Set GITHUB_TOKEN (or GH_TOKEN) to look up a private repository; --github-api https://<host>/api/v3 and --issuer cover GitHub Enterprise Server. Naming an --environment is recommended: its protection rules then decide which branches and reviewers can publish.

The workflow needs id-token: write and the registry's address, nothing else:

permissions: { id-token: write, contents: read }
env: { APPSHELL_REGISTRY: https://registry.appshell.org }
jobs:
  publish:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      # ...build...
      - run: npx appshell publish

With no APPSHELL_TOKEN and no stored login for the registry, appshell publish asks the runner for a token whose audience is the registry url as configured, without a trailing slash — the registry's TRUSTED_PUBLISHING_AUDIENCE must be exactly that — and presents it for that one request. Each publish mints its own and none is stored. An explicit APPSHELL_TOKEN or stored login wins, so a job that sets one publishes as that account.

The package must name its scope, as @acme/widgets: a workflow has no namespace of its own. A refusal says what to check — appshell trust list <scope> and the job's environment, or that the registry does not have trusted publishing enabled, in which case publish retries once without a token for a registry that needs none (AUTH_MODE=none).

Declaring an application

appshell app apply reconciles an application against a declared resource, creating it when absent. It is the declarative counterpart to app create plus publish --application, and the same file works against any registry via --registry.

appshell.app.yaml

apiVersion: registry.appshell.org/v1
kind: Application
name: storefront
spec:
  shell:
    root: ContainerModule/Container
    title: Storefront
  packages:
    - acme/[email protected]
    - acme/[email protected]
appshell app apply -f appshell.app.yaml

spec.packages is the full desired set — anything activated but absent from it is deactivated, and the command prints what moved:

Updated acme/storefront
  activated acme/[email protected]
  deactivated acme/search

Omit spec.packages entirely and package state is left alone, which is what you want locally where the webpack plugin already publishes and activates on every build. ${VAR} placeholders expand from the environment exactly as they do in appshell.config.yaml.

Trusted deployment

A workload can apply an application with its own OIDC identity, so the job or controller that keeps it at its declared state holds no secret either: a GitHub Actions workflow, or a Kubernetes service account. The application's scope owner, or an organization admin, records which workload may — the application must exist first:

# A GitHub Actions workflow, by repository ids as for publishing
appshell trust add acme --application storefront \
  --github acme/deploy --workflow deploy.yml --environment production

# A Kubernetes service account, under its cluster's OIDC issuer
appshell trust add acme --application storefront \
  --kubernetes https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLE \
  --namespace appshell --service-account deployer

appshell trust list acme --application storefront
appshell trust remove acme 1b2c3d4e --application storefront

The cluster issuer is taken exactly as given and must be what the cluster's service account tokens carry (kubectl get --raw /.well-known/openid-configuration prints it). The registry fetches its keys from there, so the issuer's discovery document must be public: EKS, GKE and AKS serve one, and a self-managed cluster must publish its own discovery document and keys at a public https address.

From GitHub Actions, with id-token: write and no APPSHELL_TOKEN or stored login, appshell app apply asks the runner for a token for the registry's deploy audience — the registry url with /deploy, or APPSHELL_DEPLOY_AUDIENCE when the registry's operator set TRUSTED_DEPLOYMENT_AUDIENCE to something else — and applies with it:

permissions: { id-token: write, contents: read }
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: npx appshell app apply -f appshell.app.yaml --registry https://registry.appshell.org

From Kubernetes, project a token for that audience into the pod, running as the trusted service account, and present it to the same registry:

spec:
  serviceAccountName: deployer
  containers:
    - name: apply
      # ...
      volumeMounts:
        - name: appshell-token
          mountPath: /var/run/appshell
          readOnly: true
  volumes:
    - name: appshell-token
      projected:
        sources:
          - serviceAccountToken:
              audience: https://registry.appshell.org/deploy
              expirationSeconds: 3600 # at most an hour is accepted
              path: token
APPSHELL_TOKEN=$(cat /var/run/appshell/token) \
  appshell app apply -f appshell.app.yaml --registry https://registry.appshell.org

A workload may only apply, only an application that already exists, and only one named as scope/name in the resource, since it has no scope of its own. A refusal says what to check: appshell trust list <scope> --application <name> against the job or account, or the audience setting. Against a registry without trusted deployment enabled (AUTH_MODE=none, every local one), apply retries once without a token.

Inspecting an application

appshell app list                     # every application in the scope
appshell app get storefront           # packages, revision, visibility
appshell app composition storefront   # the resolved payload the shell receives
appshell app revisions storefront     # revision history
appshell app rollback storefront --to 3
appshell app open storefront          # print the shell url

Every mutation produces a revision, so revisions is the audit trail and rollback is how you undo one.

appshell app create storefront        # imperative equivalent of a bare apply
appshell app delete storefront
appshell app deactivate acme/checkout
appshell app clone --from prod --to my-sandbox
appshell app sync --from prod --to staging --include packages

A clone never inherits its source's permission to take overlays. Ask for it with --allow-overlays, and the copy is yours to point at your machine with appshell dev:

appshell app clone --from appshell/appshell-example --to <scope>/example --allow-overlays

Developing against a deployed application

appshell dev opens an overlay: a per-developer, per-browser redirect that points some of an application's remotes at your machine. It is resolved per request, expires on its own, and never reaches the application's composition or its revision history.

appshell dev --port 3002          # redirect this package's remotes at localhost:3002
appshell dev status               # what is open on the application
appshell dev stop --package       # stop redirecting just this package
appshell dev stop <id>            # close one, or --all

An overlay can only redirect remote keys the application already publishes — adding one outright would be a way to run code the application was never composed with. For a new package, publish and activate it first, or work in a clone.

--shell dev additionally serves the development shell build, which is what supports hot reloading remotes in place.

Analyzing shared dependencies

appshell outdated

Compares this package's dependencies against the shared-dependency baselines the application declares, and reports conflicts, missing entries and matches.

Generate manifest

Processes a template into an appshell manifest. appshell publish does this for you; run it directly when you want the manifest as a build artifact.

appshell generate manifest --template dist/appshell.template.json
OptionDefaultPurpose
-t, --templateappshell.template.jsonTemplate to process
-o, --outDirdistOutput location
-f, --outFileappshell.manifest.jsonOutput filename

Where does the template come from?

Each micro-frontend configured with @appshell/webpack-plugin emits appshell.template.json at build time. appshell publish turns that into a manifest and publishes it as a package version.

Sample config template appshell.template.json:

{
  "remotes": {
    "CraModule/App": {
      "url": "${CRA_MFE_URL}",
      "metadata": {
        "route": "/cra",
        "displayName": "Example App",
        "displayGroup": "${CRA_MFE_DISPLAY_GROUP}",
        "order": 10,
        "icon": "ViewList"
      },
      "id": "3eb81a0c"
    }
  },
  "module": {
    "exposes": {
      "./App": "./src/App"
    },
    "filename": "remoteEntry.js",
    "name": "CraModule",
    "shared": {
      "react": {
        "singleton": true,
        "requiredVersion": "^18.2.0"
      },
      "react-dom": {
        "singleton": true,
        "requiredVersion": "^18.2.0"
      }
    }
  },
  "vars": {
    "RUNTIME_ARG_1": "${RUNTIME_ARG_1}",
    "RUNTIME_ARG_2": "${RUNTIME_ARG_2}"
  }
}

How does my runtime environment get reflected in the appshell manifest?

Note the variable expansion syntax ${CRA_MFE_URL}. When appshell generate manifest is called the actual runtime environment values are injected in order to produce the remote module's appshell manifest.

Note the vars section defines runtime configuration values a package reads with getVars() from @appshell/runtime/vars once it is loaded. The package must share @appshell/runtime as a singleton to receive them. See the examples for a use case.

Sample appshell manifest produced by the appshell generate manifest function:

{
  "remotes": {
    "CraModule/App": {
      "id": "3eb81a0c",
      "url": "http://localhost:3001/remoteEntry.js",
      "scope": "CraModule",
      "module": "./App",
      "metadata": {
        "route": "/cra",
        "displayName": "Example App",
        "displayGroup": "primary",
        "order": 10,
        "icon": "ViewList"
      }
    },
    "VanillaModule/Vanilla": {
      "id": "8232ce86",
      "url": "http://localhost:3002/remoteEntry.js",
      "scope": "VanillaModule",
      "module": "./Vanilla",
      "metadata": {
        "route": "/vanilla",
        "displayName": "Example React App",
        "displayGroup": "primary",
        "order": 10,
        "icon": "ViewList"
      }
    }
  },
  "modules": {
    "Appshell": {
      "name": "Appshell",
      "shared": {
        "react": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        },
        "react-dom": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        }
      }
    },
    "CraModule": {
      "exposes": {
        "./App": "./src/App"
      },
      "filename": "remoteEntry.js",
      "name": "CraModule",
      "shared": {
        "react": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        },
        "react-dom": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        }
      }
    },
    "VanillaModule": {
      "exposes": {
        "./Vanilla": "./src/App"
      },
      "filename": "remoteEntry.js",
      "name": "VanillaModule",
      "shared": {
        "react": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        },
        "react-dom": {
          "singleton": true,
          "requiredVersion": "^18.2.0"
        }
      }
    }
  },
  "vars": {
    "CraModule": {
      "RUNTIME_ARG_1": "Foo",
      "RUNTIME_ARG_2": "Biz"
    },
    "VanillaModule": {
      "RUNTIME_ARG_1": "Bar"
    }
  }
}

appshell publish sends this manifest to the registry as a package version. The registry composes every activated package into the payload it serves to the shell.

版本与发布记录

@appshell/cli 共发布 220 个版本,首次发布于 2023/03/04;过去 12 个月发布了 142 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.0.0-alpha.148latest2026/10/309 天前
1.0.0-alpha.1472026/10/2910 天前
1.0.0-alpha.1462026/10/2810 天前
1.0.0-alpha.1452026/10/2810 天前
1.0.0-alpha.1442026/10/2712 天前
1.0.0-alpha.1432026/10/2613 天前

关于 @appshell/cli 的常见问题

@appshell/cli 是什么?

cli utility to generate a global appshell artifacts for module federation micro-frontends

如何安装 @appshell/cli?

在项目目录运行 npm install @appshell/cli,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 appshell,可以用 npx appshell 直接运行。

@appshell/cli 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/appshell__cli 是否存在。

@appshell/cli 支持 ESM 还是 CommonJS?

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

@appshell/cli 可以商用吗?

@appshell/cli 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@appshell/cli 的下载量有多少?

根据 npm 官方下载统计,@appshell/cli 在 2026/10/07 – 2026/11/05 这 30 天内被下载 6,117 次,日均约 204 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@appshell/cli 还在维护吗?

最新版本 1.0.0-alpha.148 发布于 2026/10/30(9 天前);至今共发布 220 个版本,其中过去 12 个月发布了 142 个;首次发布于 2023/03/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。