@cloud-cli/on

中文文档
1.79.0发布于 2026/10/15MITTypeScriptES Module近 30 天下载 2197

CLI entry point for the on plugin ecosystem.

原始使用文档
文档目录21 节
  1. 🌟 Key Highlights
  2. 📦 Project Structure
  3. 🚀 Quick Start
  4. 1. Install & Run
  5. 2. Define a Workflow
  6. 💻 CLI Usage & Commands
  7. Commands
  8. CLI and Environment Options
  9. Secrets
  10. ⚙️ Configuration Reference
  11. Background Notifications
  12. 📐 Deterministic Evaluation Rules
  13. Context Scope Available in Expressions
  14. 🌐 Webhook Ingress Gateway & Dashboard
  15. Endpoint Matrix
  16. Dashboard Features
  17. 🔒 Security & Hardening
  18. systemd Deployment
  19. Future Security Work
  20. Planned Runner Rollouts
  21. Development

🏃 @cloud-cli/on

General-purpose workflows

A self-hosted, lightweight, high-performance CI/CD runner engine built for Node.js.

Designed with a strict security-first boundary, native JavaScript AST evaluation, zero-DSL template literals, and a built-in terminal log web dashboard.


🌟 Key Highlights

  • Strict Code/Data Separation: run: steps are executed verbatim as raw process scripts. Expressions and dynamic data bindings are isolated strictly to env:, eliminating shell-injection vectors entirely.
  • Standard ES Template Syntax (${...}): No custom DSL wrappers like ${{ }} or {{ }}. If a field contains ${...}, it evaluates standard JavaScript template string logic via AST.
  • Deterministic Field Evaluation: No silent fallbacks or ambiguous type conversions. Plain strings remain literal strings; conditions in if: fields run as strict JS boolean expressions.
  • Built-in Dark Mode Web UI (/runs): Monitor job statuses live, inspect workspace inputs, and view ANSI-colored terminal log streams rendered in real-time.
  • System & Container Execution Drivers: Run steps directly as detached host process groups or inside isolated Docker/Systemd transient units.
  • DB-authored workflows: Draft, validate, and publish portable YAML workflows through the authenticated API.
  • Scheduled execution: Published workflows can run from cron expressions or local sunrise/sunset events.
  • Encrypted secret storage: Secret ciphertext is stored in the database; the master key remains on the HTTP server.

📦 Project Structure

my-project/
├── runner.config.mjs        # Optional plugin/advanced configuration
└── package.json


🚀 Quick Start

1. Install & Run

Run the engine directly via npx or pnpm dlx:

# Start each role (normally managed by the supplied systemd units)
npx @cloud-cli/on start-server
npx @cloud-cli/on start-scheduler
npx @cloud-cli/on start-workers

2. Define a Workflow

name: Build and Publish Release

on:
  github:
    events:
      - push
    owner: octocat
    repo:
      - example
      - another-example
    branches:
      - main
      - releases/*
    paths:
      - package*.json
    if: inputs.action !== 'deleted'

concurrency:
  group: release-${inputs.repo}
  cancel-in-progress: true

# Every tag must be advertised by the worker that claims this workflow.
tags: [linux, docker]

steps:
  - id: checkout
    name: Checkout Code Repository
    env:
      CLONE_URL: ${inputs.clone_url}
      COMMIT_SHA: ${inputs.commit_sha}
    run: |
      git clone --depth 1 "$CLONE_URL" .
      git checkout "$COMMIT_SHA"

  - id: install-and-build
    name: Install Dependencies & Build
    run: |
      pnpm install
      pnpm run build

  - id: publish
    name: Publish to NPM
    env:
      NPM_TOKEN: ${secrets.NPM_TOKEN}
    run: |
      echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
      npx --yes semantic-release@24 -b main --no-ci

Store the YAML as a draft and publish it through the authenticated API. Only published revisions can receive webhooks or scheduled runs. For an immediate test from the editor, save the draft and use Run now; this queues the latest saved draft revision without changing the schedule or publishing it:

curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/validate \
  -H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
curl -u admin:"$RUNNER_ADMIN_SECRET" -X PUT http://localhost:11235/api/workflows/build-and-publish-release \
  -H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/build-and-publish-release/publish

Time triggers are defined beside a webhook trigger:

on:
  schedule:
    - id: nightly
      cron: "0 2 * * *"
      timezone: Europe/Berlin
  solar:
    - id: morning
      event: sunrise
      latitude: 52.52
      longitude: 13.405
      offset: +15m

GitHub triggers support these preprocessor filters:

FieldMatch behavior
eventsExact webhook event names
owner, repoOne or many exact acceptable names
nameOne or many exact acceptable names, with org/repo combined
branchesBranch/ref glob patterns such as main or v1.*, matches branches and tags
refsAlias for branches
tagtrue requires a tag push; false requires a non-tag event
pathsChanged-file glob patterns across added, modified, and removed files

Configured fields are combined with AND. Values within one list are combined with OR. tags also requires a tag value to match, while branches requires a branch value to match. The generic if expression is evaluated separately after all preprocessor filters pass.

For example, a tag workflow can use tag: true with one or more regular expressions:

on:
  github:
    events: [push]
    owner: octocat
    repo: example
    tag: true
    tags:
      - ^v1\..*

💻 CLI Usage & Commands

npx @cloud-cli/on [command] [options]

Commands

CommandDescription
start-serverRuns Webhook Ingress Gateway (the HTTP server receiving webhooks).
start-schedulerDispatches published cron and solar workflow triggers.
start-workersRuns the event-driven worker scheduler (Scalable Workers).

CLI and Environment Options

FlagOptionDefaultEnvDescription
-h--help—-Prints CLI help message and exits.
-c--config./runner.config.mjsRUNNER_CONFIG_FILEPath to JavaScript configuration file.
-d--database-DATABASE_URLNode.js ES module URL exporting the database API.
-p--port11235PORTPort for the Ingress HTTP server.
-k--workers5RUNNER_WORKERSMaximum concurrent jobs on this node.
RUNNER_ADMIN_SECRETAdmin token to refresh secrets via API
RUNNER_WORKER_SECRETWorker token for job events and secret retrieval
OIDC_ISSUEROIDC issuer URL, for example https://auth.api.apphor.de
OIDC_CLIENT_IDRegistered OIDC client identifier
OIDC_CLIENT_SECRETRegistered OIDC client secret
RUNNER_OIDC_PROVIDER_URL, RUNNER_OIDC_CLIENT_ID, RUNNER_OIDC_CLIENT_SECRETLegacy aliases for the OIDC settings above.
RUNNER_SERVER_URLWebhook server URL used by workers.
RUNNER_TAGSComma-separated worker capability tags.
RUNNER_VAPID_PUBLIC_KEYPublic key for background Web Push notifications.
RUNNER_VAPID_PRIVATE_KEYPrivate key for background Web Push notifications.
RUNNER_VAPID_SUBJECTVAPID contact, such as mailto:[email protected].

Set RUNNER_ADMIN_SECRET only on the HTTP server for dashboard and management APIs. Set the same non-empty RUNNER_WORKER_SECRET on the server and every worker to publish job-status refresh events and retrieve job-scoped secrets. When the three RUNNER_OIDC_* values are configured, /auth/login enables browser sign-in, the browser uses the same bearer-token API path as external consumers, authenticated users can read logs, and the API token settings page issues and revokes scoped tokens through the OIDC provider. Register https://your-runner-host/auth/callback as the OIDC redirect URI. The dashboard workflow APIs accept either Bearer authentication or HTTP Basic authentication with username admin and the admin secret.

Secrets

Secrets are written through PUT /api/secrets/:NAME and are AES-256-GCM encrypted in the database. Set RUNNER_MASTER_KEY only on the HTTP server, or provide an on-master-key systemd credential. Workers receive decrypted values only after claiming a running job; values are not written to job payloads or reports.


⚙️ Configuration Reference

You can customize engine behavior using runner.config.mjs in your project root:

// runner.config.mjs
import { GitHubStatusPlugin } from "@cloud-cli/on";

export default {
  port: 3000,
  workers: 5,
  serverUrl: "https://runner.example.com/",
  tags: ["linux", "docker"],
  storagePath: "/tmp/workspaces",
  database: "https://remote.db.com/",

  // Global environment variables passed to all steps
  env: {
    NODE_ENV: "production",
  },

  plugins: [
    new GitHubStatusPlugin({
      token: process.env.SECRET_GITHUB_TOKEN,
      context: "on",
    }),
  ],
};

The GitHub status plugin publishes commit states when a workflow starts and finishes. Its token needs permission to write commit statuses for the target repository.

Background Notifications

The dashboard can receive job notifications while it is closed through Web Push. Generate a VAPID key pair with npx web-push generate-vapid-keys, configure the three RUNNER_VAPID_* values on the server, and enable notifications from the dashboard over HTTPS. Without VAPID configuration, notifications work only while the dashboard is open.

Run details are available as HTML at /runs/:id and as sanitized JSON at /api/runs/:id. The HTML view refreshes reactively through job-specific SSE events while a run is active. Both representations omit raw webhook bodies, sensitive input fields, execution environment values, and internal rerun state.

Open /workflows to list workflows and manage secret values. Open /workflows/new to create a definition or /workflows/:id to edit one. These pages are protected by the administrator HTTP Basic credential; secret values are write-only.


📐 Deterministic Evaluation Rules

To prevent syntax ambiguity and injection risks, fields in workflow definitions operate under three strict modes:

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. RAW PASSTHROUGH MODE (`run:`)                                         │
│ • Executed verbatim as a shell process command.                          │
│ • No string replacements or engine parsing performed.                    │
│ • Access environment variables strictly via shell syntax: $MY_VAR.       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 2. EXPRESSION MODE (`if:`, `eval:`)                                      │
│ • Evaluated strictly as pure JavaScript expressions via Acorn AST.       │
│ • Must be valid JS syntax (e.g. `inputs.branch === 'main'`).             │
│ • Automatically coerced to boolean in `if:` conditions.                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 3. DETERMINISTIC VALUE MODE (`env:`, `name:`, `image:`, `group:`)        │
│ • Plain strings WITHOUT `${}` remain 100% raw literal strings.            │
│ • Strings WITH `${...}` evaluate as standard ES Template Literals.       │
│ • Example: `node:${inputs.node_version}-alpine`                          │
└──────────────────────────────────────────────────────────────────────────┘

Context Scope Available in Expressions

Within ${...}, if:, and eval: contexts, the following object scopes are exposed:

  • inputs: Payload key-values received from incoming webhooks.
  • env: Merged environment variables from global config and workflow definitions.
  • secrets: Unmasked job-scoped values from encrypted central secret storage.
  • steps: Execution statuses and outputs from previous steps in the workflow (steps.<id>.status, steps.<id>.outputs).
  • files: Workspace-bound file helpers for if: and eval: expressions: files.exists('Dockerfile'), files.readFile('package.json'), and files.join('dist', 'artifact'). Paths outside the job workingDir are rejected.
  • BUILTIN_HELPERS: JS utilities including String, Number, Boolean, and JSON.parse / JSON.stringify.

🌐 Webhook Ingress Gateway & Dashboard

The Ingress Gateway listens for incoming HTTP requests and serves the live web UI.

Endpoint Matrix

MethodEndpointDescription
POST/webhooks/githubWebhook endpoint for GitHub events. Applies GitHub filters and evaluates on.github.if.
GET/runsDashboard: Public live dark-mode monitoring page listing recent jobs.
GET/api/jobs?afterId=<id>&beforeId=<id>&limit=<n>Dashboard jobs with exclusive lower and upper ID cursors and a limit from 1 to 500 (default 50).
GET/runs/:jobIdJob Report: Authenticated interactive trace with step timings and terminal log output.
GET/workflowsAuthenticated workflow list and encrypted secret management UI.
GET/workflows/new, /workflows/:idAuthenticated YAML workflow editor.
POST/api/workflows/validateAuthenticated YAML validation without persistence.
GET, PUT, DELETE/api/workflows/:idAuthenticated revisioned workflow read, draft save, and deletion.
POST/api/workflows/:id/publishAuthenticated publication of the latest draft revision.
POST/api/workflows/:id/runAuthenticated immediate run of the latest saved draft revision.
GET, PUT, DELETE/api/secrets, /api/secrets/:nameAuthenticated encrypted secret-name and write-only value management.

Dashboard Features

  • Real-time Auto-Refresh: /runs automatically refreshes job queue statuses (PENDING, RUNNING, SUCCESS, FAILED, CANCELLED).
  • ANSI Terminal Rendering: Uses ansi_up to render bash colors, bold highlights, and console outputs accurately in step log boxes.
  • Payload Inspection: View JSON inputs received from webhooks for easy debugging.
  • Workflow Management: Use the authenticated /workflows pages to create, validate, publish, edit, and delete DB-authored workflow revisions.

🔒 Security & Hardening

  1. Environment Variable Boundary: By forcing shell steps to consume data via process environment variables ($CLONE_URL), malicious webhook payloads containing shell delimiters (e.g. ; rm -rf /) cannot mutate shell script execution trees.
  2. Prototype Pollution Protection: AST evaluation explicitly blocks access to dangerous JS properties (constructor, __proto__, prototype).
  3. Payload Size Guard: The Ingress server enforces a strict 5MB payload limit to prevent Out-Of-Memory (OOM) denial-of-service attacks.
  4. Scoped API authentication: Workflow validation, publishing, and secret management require RUNNER_ADMIN_SECRET over HTTPS. OIDC sessions and provider-issued scoped tokens can read job logs when granted logs:read. Workers use the separate RUNNER_WORKER_SECRET only for lifecycle events and secrets of jobs they have claimed.

systemd Deployment

Install the unit files from systemd/ and create root-owned environment files in /etc/on/: server.env for DATABASE_URL, RUNNER_SERVER_URL, RUNNER_ADMIN_SECRET, RUNNER_WORKER_SECRET, and the three RUNNER_OIDC_* values; scheduler.env for DATABASE_URL; and worker.env for DATABASE_URL, RUNNER_SERVER_URL, RUNNER_WORKER_SECRET, RUNNER_TAGS, and RUNNER_TMP. Workers receive only RUNNER_WORKER_SECRET; the admin and OIDC secrets stay on the HTTP server. The supplied units run as root because the systemd execution driver creates transient system services. Use systemctl edit for per-machine overrides.

Place the server master key in /etc/on/credentials/on-master-key with root-only permissions. runner-server.service exposes it privately through systemd's credentials directory. Start the primary control plane with systemctl enable --now runner.target; enable runner-worker.service separately on worker machines. Use systemctl edit runner-worker.service for machine-specific labels and paths.

For package-managed remote workers, enable runner-worker-update.timer. It runs daily at 04:00 with up to 45 minutes of per-machine jitter, installs @cloud-cli/on@latest into /opt/on, then restarts the worker only when installation succeeds. runner-worker.service allows up to one hour for running jobs to drain during that restart. Do not enable this timer on a source-managed development worker; deploy that worker from its checked-out build instead.

To drain a worker manually without leaving it stopped, send SIGUSR1 only to its main process: systemctl kill --kill-who=main --signal=SIGUSR1 runner-worker.service. The worker stops claiming new jobs, waits for running jobs to complete, exits with status 0, and Restart=always starts a replacement. Use systemctl restart runner-worker.service for an intentional version change; systemd waits for the same drain behavior up to TimeoutStopSec=1h.

Workers now resolve the immutable revision when they claim a job. A workflow can safely use a checkout step followed by if: files.exists('Dockerfile'); files is constrained to the job's workingDir, so it cannot inspect files outside that workspace.

Future Security Work

This MVP intentionally supports one trusted operator. Before sharing the UI or exposing it beyond a private deployment, add individual accounts and roles, secure browser sessions and CSRF protection, audit logs, worker enrollment credentials, secret key rotation, encrypted systemd credentials, rate limiting, and database high availability.

Planned Runner Rollouts

Package-managed workers currently update on a staggered daily timer. Before enabling automatic promotion of newly released runner versions, implement a controlled canary rollout:

  1. Publish a new immutable npm version and resolve the exact version behind the candidate tag.
  2. Install that exact version on one designated canary worker, never a moving tag during the rollout.
  3. Stage releases in versioned directories and atomically switch a current symlink only after installation and a local smoke check succeed.
  4. Drain the canary with SIGUSR1, restart it, and retain prior release directories for rollback.
  5. Require the canary to report a heartbeat containing its worker ID, running version, health state, and last-seen time.
  6. Run a dedicated runner-health workflow with tags: [canary] to verify queue claiming, revision lookup, job-scoped secret retrieval, and the execution driver.
  7. Record rollout state durably: candidate version, canary worker, status, promotion time, and failure reason.
  8. Promote only a healthy candidate with npm dist-tag add @cloud-cli/on@<version> stable.
  9. Configure normal daily worker updates to install @cloud-cli/on@stable, with a rollout concurrency limit of one worker.
  10. On failed installation, restart, heartbeat, or health check, mark the candidate rejected, leave stable unchanged, and roll the canary back to its prior version.

SSE should notify workers that an update is available, but the control-plane database must remain the authoritative rollout state so disconnected workers converge safely after reconnecting.

Development

pnpm i
pnpm run lint
pnpm run test
pnpm run build

版本与发布记录

@cloud-cli/on 共发布 219 个版本,首次发布于 2026/03/17;过去 12 个月发布了 219 个版本。

版本发布日期距今
1.79.0latest2026/10/1526 天前
1.78.02026/10/1426 天前
1.77.62026/10/1426 天前
1.77.52026/10/1327 天前
1.77.42026/10/1327 天前
1.77.32026/10/1130 天前

关于 @cloud-cli/on 的常见问题

@cloud-cli/on 是什么?

CLI entry point for the on plugin ecosystem.

如何安装 @cloud-cli/on?

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

@cloud-cli/on 支持 TypeScript 吗?

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

@cloud-cli/on 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@cloud-cli/on 可以商用吗?

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

@cloud-cli/on 的下载量有多少?

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

@cloud-cli/on 还在维护吗?

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