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

@ticatec/omniflow-core

v0.2.1

Published

Core primitives, execution context, and toolchain SPI for OmniFlow CI/CD orchestrator

Readme

@ticatec/omniflow-core

License: MIT Node.js Version

中文文档

@ticatec/omniflow-core is the foundational core package for the OmniFlow CI/CD orchestrator. It provides execution context management, standard operational primitives (shell, ssh, git, docker), sensitive credential masking, and an extensible Toolchain SPI.


Highlights

  • Static Decoupled Primitives: Import shell, ssh, git, and docker directly. No cumbersome ctx.commands or object passing required.
  • Transparent Context Injection (AsyncLocalStorage): Primitives automatically resolve cwd, environment variables env, and logger from the active async call context.
  • Automatic --dry-run Interception: When running in simulation mode, destructive operations are intercepted by the core layer automatically, logged safely, and mocked without extra boilerplate in plugins.
  • Automatic Secret Masking: Credentials and sensitive tokens in logs and commands are automatically masked (********).
  • Extensible Toolchain SPI: Built-in support for Maven, Gradle, and Node.js (pnpm / yarn / npm / bun), with priority-based override and registration for custom language toolchains (e.g. Go, Rust, Python).

Architecture Overview

┌────────────────────────────────────────────────────────┐
│             Plugin / Pipeline Command                  │
│  import { shell, ssh, git, docker } from '@ticatec/omniflow-core'│
└─────────────────────────┬──────────────────────────────┘
                          │ calls primitives
                          ▼
┌────────────────────────────────────────────────────────┐
│                   @ticatec/omniflow-core                       │
│  ┌──────────────────────────────────────────────────┐  │
│  │ AsyncLocalStorage Context (cwd, env, dryRun, log)│  │
│  └──────────────────────┬───────────────────────────┘  │
│                         ▼                              │
│       [Dry-Run Check] ──► [Credential Masker]          │
│                         ▼                              │
│  ┌───────────────┬──────────────┬───────────────────┐  │
│  │ shell.run/sh  │ ssh.exec/cp  │ git & docker ops  │  │
│  └───────────────┴──────────────┴───────────────────┘  │
│                         ▼                              │
│  ┌──────────────────────────────────────────────────┐  │
│  │ Toolchain SPI (Maven, Gradle, Node, Custom...)   │  │
│  └──────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────┘

Installation

pnpm add @ticatec/omniflow-core
# or
npm install @ticatec/omniflow-core

Requires Node.js >= 20.0.0.


Core Primitives

1. shell

Executes local shell commands using safe tagged template literals or string execution.

import { shell } from '@ticatec/omniflow-core'

// Tagged template execution (safely splits arguments without shell injection vulnerabilities)
await shell.run`mvn clean package -DskipTests`

// Explicit cwd or options
await shell.run({ cwd: '/workspace/service-a' })`npm run build`

// Shell execution for compound commands (pipes, redirections, &&, ||)
await shell.sh('cat coverage/lcov.info | grep -v "test" > coverage/filtered.info')

// Capture output without throwing on non-zero exit
const res = await shell.output('git status --porcelain')
console.log(res.stdout, res.exitCode)

Dry-Run & Context Awareness: Inside a runWithContext scope with dryRun: true, shell.run, shell.sh, and ssh.exec will print the planned command to the context logger and return clean mock results ({ stdout: '', stderr: '', exitCode: 0, failed: false }) without executing or polluting stdout.

[!NOTE] Credential Masking & Tail Buffering: Output streams piped to logFile pass through a stateful transform that prevents secret leakage across chunk boundaries by buffering a sliding tail up to maxSecretLen - 1 bytes. When exceptionally large single-line secrets (e.g. 64KB tokens or keys) are configured in the environment, log output chunks smaller than the tail buffer are retained until the buffer capacity is reached or the stream completes (flush), guaranteeing 100% masking coverage before emitting to disk.


2. ssh

Executes remote commands and securely copies files via SSH/SCP with configuration-based target resolution.

import { ssh } from '@ticatec/omniflow-core'

// Option A: Explicit connection target
const target = {
  host: 'prod-app-01.internal',
  user: 'deploy',
  port: 22,
  privateKey: process.env.SSH_PRIVATE_KEY
}

// Remote command execution
await ssh.exec(target, 'systemctl restart my-app.service')

// File transfer (SCP)
await ssh.cp(target, 'dist/app.tar.gz', '/opt/deploy/app.tar.gz')

// Option B: Named targets via ssh.configure()
ssh.configure('prod', {
  host: 'prod-app-01.internal',
  user: 'deploy',
  port: 22,
  privateKeyFile: '/home/deploy/.ssh/id_rsa'
})

// Call by target name directly
await ssh.exec('prod', 'uptime')
await ssh.cp('prod', 'dist/app.tar.gz', '/opt/deploy/app.tar.gz')

// Clean up singleton config in tests:
ssh.reset()

// Option C: Isolated instance without touching module-level singleton
const client = ssh.createClient({
  staging: { host: 'staging.internal', user: 'ci', privateKeyFile: '~/.ssh/id_rsa' }
})
await client.exec('staging', 'hostname')

3. git

Provides essential Git repository inspection and workspace lifecycle management.

import { git } from '@ticatec/omniflow-core'

const branch = await git.currentBranch()
const commit = await git.currentCommit()
const dirty = await git.isDirty()

// Workspace promotion and synchronization
await git.fetch({ remote: 'origin', branch: 'main' })
await git.resetHard('origin/main')
await git.clean()
await git.checkout('release/v1.0')

4. docker

Encapsulates common container build, tag, push, and compose orchestration.

import { docker } from '@ticatec/omniflow-core'

// Build and tag
await docker.build({
  image: 'registry.internal/api:v1.2.0',
  dockerfile: 'Dockerfile.prod',
  buildArgs: { NODE_ENV: 'production' }
})

// Push to registry
await docker.push('registry.internal/api:v1.2.0')

// Docker Compose management
await docker.composeUp({ files: 'docker-compose.prod.yml', detach: true })
await docker.composeDown({ files: 'docker-compose.prod.yml' })

Execution Context & AsyncLocalStorage

The execution context manages runtime configuration, logging, and environment variables across asynchronous chains.

Runner / Orchestrator Example:

import { runWithContext, createMockContext } from '@ticatec/omniflow-core'

const ctx = createMockContext({
  runId: 'run-20260912-001',
  project: 'order-service',
  environment: 'staging',
  dryRun: false,
  workspace: '/workspaces/order-service',
  projectRoot: '/workspaces/order-service',
  env: {
    REGISTRY: 'harbor.company.com',
    DOCKER_TOKEN: 'secret-token-value'
  }
})

await runWithContext(ctx, async () => {
  // Any function called here (directly or deeply nested) can call getContext()
  // and primitives automatically use ctx.workspace, ctx.env, ctx.dryRun, and ctx.logger
  await executePipelineSteps()
})

Plugin / Step Example:

import { getContext, tryGetContext, shell } from '@ticatec/omniflow-core'

export async function myPluginTask() {
  const ctx = getContext() // Throws if outside runWithContext
  ctx.logger.info(`Building in ${ctx.workspace} for environment ${ctx.environment}`)

  await shell.run`npm test`
}

Toolchain SPI

OmniFlow Core abstracts language build systems using a clean Service Provider Interface (SPI).

Built-in Toolchains

  1. Maven (MavenToolchain): Detects pom.xml, parses groupId/artifactId/version, runs ./mvnw or mvn (priority: 30).
  2. Gradle (GradleToolchain): Detects build.gradle / build.gradle.kts, resolves Gradle Wrapper ./gradlew, generates build commands (priority: 20).
  3. Node (NodeToolchain): Detects package.json, auto-selects pnpm, yarn, npm, or bun from lockfiles, parses package coordinates (priority: 10).

Detecting and Using Toolchains:

import { resolveToolchain } from '@ticatec/omniflow-core'

const { provider, detection } = await resolveToolchain('/path/to/project')
console.log(`Detected toolchain ${provider.name} because: ${detection.reason}`)

const info = await provider.projectInfo('/path/to/project')
console.log(`Project: ${info.name}@${info.version}`)

// Execute dependency install and build
await provider.install('/path/to/project')
await provider.build('/path/to/project', ['-DskipTests'])

[!NOTE] Reproducible CI Installation: NodeToolchain.install() automatically enforces immutable lockfile installations (npm ci, pnpm install --frozen-lockfile, yarn install --frozen-lockfile / --immutable, bun install --frozen-lockfile) whenever a lockfile is present in the project directory, preventing accidental lockfile mutation or dependency drift during automated CI/CD runs. If no lockfile exists in a new project, it falls back to standard package installation without error.

Registering Custom / Overriding Toolchains:

import {
  registerToolchain,
  shell,
  type ToolchainProvider,
  type DetectionResult,
  type ProjectInfo
} from '@ticatec/omniflow-core'
import fs from 'node:fs/promises'
import path from 'node:path'

export class GoToolchain implements ToolchainProvider {
  readonly name = 'go'
  readonly priority: number = 50 // Higher priority takes precedence during automatic detection

  async detect(projectDir: string): Promise<DetectionResult | null> {
    try {
      await fs.access(path.join(projectDir, 'go.mod'))
      return { name: this.name, reason: 'found go.mod' }
    } catch {
      return null
    }
  }

  async projectInfo(projectDir: string): Promise<ProjectInfo> {
    return { name: 'user-service', version: '1.0.0', fullName: 'user-service' }
  }

  async install(projectDir: string, flags: string[] = []): Promise<void> {
    await shell.run({ cwd: projectDir })`go mod download ${flags}`
  }

  async build(projectDir: string, flags: string[] = []): Promise<void> {
    await shell.run({ cwd: projectDir })`go build -v -o dist/app ${flags}`
  }
}

registerToolchain(new GoToolchain())

For a complete step-by-step guide on creating custom toolchains (Go, Rust) and enterprise overrides, see Toolchain Extension Guide.


Breaking Changes in v0.2.0

If upgrading from an earlier experimental version, note the following breaking API and behavior changes:

  1. --dry-run simulation output: In dry-run mode, commands now return { stdout: '', stderr: '', exitCode: 0, failed: false } instead of synthetic strings like "[DRY-RUN] ...". The simulated command is logged cleanly to ctx.logger.info without polluting stdout.
  2. docker.build image requirement: The image property in DockerBuildOptions is now strictly required. Implicit defaulting to 'latest' and undocumented alias parameters (tag, tags) have been removed to avoid silent misconfiguration.
  3. docker.composeUp property names: Standardized on files?: string | string[] and detach?: boolean. Legacy aliases (file, detached) have been removed.
  4. ssh configuration naming: Target SSH configuration adheres strictly to camelCase: privateKey, privateKeyFile. Deprecated snake_case properties (private_key, private_key_file) have been removed.
  5. resolveToolchain options: Options are normalized to { preferred?: string; fallback?: string }. Deprecated alias configured has been removed.
  6. Command spawn failure exit code: When a subprocess fails to spawn (e.g. executable not found), the runner returns exitCode: -1 and failed: true instead of mistakenly reporting exit code 0.
  7. git.resetHard & git.checkout string argument: In v0.1.x, passing a single string argument was interpreted as the working directory (resetHard(dir?, target?), checkout(dir?, ref?)). In v0.2.0, a bare string argument designates the target commit/ref (resetHard(target?: string | GitResetOptions), checkout(ref?: string | GitCheckoutOptions)). To specify an explicit working directory, pass an options object with dir: { dir: '/path/to/repo' }.

Subpath Exports

@ticatec/omniflow-core supports clean subpath imports in accordance with modern ESM standards:

| Export Path | Description | |:---|:---| | @ticatec/omniflow-core | Full bundle: context, primitives, toolchain SPI, and utils | | @ticatec/omniflow-core/context | Execution context, AsyncLocalStorage, and mocking utilities | | @ticatec/omniflow-core/primitives | Core commands (shell, ssh, git, docker) | | @ticatec/omniflow-core/toolchain | Toolchain SPI, registry, and built-in providers |


License

MIT © Ticatec