惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
L
LangChain Blog
A
About on SuperTechFans
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
美团技术团队
博客园 - 三生石上(FineUI控件)
N
Netflix TechBlog - Medium
D
DataBreaches.Net
P
Proofpoint News Feed
小众软件
小众软件
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
WordPress大学
WordPress大学
雷峰网
雷峰网
G
Google Developers Blog

Echo JS

billboard.js 4.1.0: Live resizing, configurable subchart, React subpath & CSP-safe worker From 1,256ms to 96ms: Fixing INP in a Massive React Dropdown GitHub - evoluteur/cymatics: Play a frequency and watch the sand settle into its Chladni figure, computed from the wave equation. Memdeklaro - The Basics of Decentralized Identity (DID) and Self-Sovereign Identity (SSI) How Railmid Works GitHub - evoluteur/platonic-solids: Turn the five Platonic solids in 3D, show their duals, read their measurements, and print the nets to fold your own. Sharing Application State in a URL GitHub - evoluteur/sacred-geometry: Sacred Geometry Generator: draw, tune, and export Vesica Piscis, Seed of Life, Flower of Life, Metatron's Cube, and the Golden Spiral as SVG Best of Self-Sovereign Identity: Digitalcourage, World Passport and Memdeklaro Reads Are Subscriptions - Migrating from Zustand to Coaction GitHub - evoluteur/binaural-beats: Simple web page to play binaural beats for sleep, meditation, relaxation, and focus: Delta, Theta, Alpha, Beta, and Gamma brainwave frequencies, with an optional pink or brown noise bed. toast-queue — Accessible, customizable toast notifications Building a High-Performance Data Grid in React, Vue, and Svelte I built a flight recorder for AI sessions React Authentication With JWT, Zustand, and Axios | JavaScript Tools Blog My idempotency library had one job. A dropped connection made it run the payment twice. "half-open" twice is not the same state: the bug that shaped breakwater 1.0 GitHub - evoluteur/evolutility-server-node: Framework for building REST APIs for CRUD with models rather than code (using Node.js, Express, and PostgreSQL). React Router v8 in Action: Lazy Loading and Nested Routes My test suite had 100% coverage. Mutation testing still found real bugs. The type-safe data layer for Kysely | Kysera What JavaScript Obfuscation in the AI Era | JavaScript Tools Blog Using Mongoose Studio with Apache Cassandra via Data API GitHub - trekhleb/yesbrainer: 🧠 A council of AI models for the decisions that aren't no-brainers — they answer in parallel, debate to consensus, or get judged to a verdict. Browser-only, open source, bring your own keys (BYOK), no backend. Node.js has plenty of circuit breakers. So why did I build another one? My Redis library said the write succeeded. Redis was down. GitHub - Techthos/gadget: Prebuilt, interactive HTML widgets for MCP Apps in Go — data tables and forms, self-contained in a single binary, host-themed, spec-compliant. GitHub - evoluteur/react-morph-charts: React component for bubble chart, bar chart, and pie chart, with animated morphing transitions between charts, on hover, and on window resize. Interactive Metaballs Tutorial
One $ for every environment | Xec
2026-08-05 · via Echo JS

A typed execution layer for TypeScript infrastructure. Run commands on your laptop, an SSH fleet, Docker containers and Kubernetes pods through one API — with the same result type, the same errors and the same streaming everywhere.

npm i @xec-sh/core

deploy.ts

import { $ } from '@xec-sh/core';

// the same command, four environments
await $`npm run build`;
await $.ssh('deploy@web-1')`systemctl restart api`;
await $.docker('api')`python migrate.py`;
await $.k8s('prod/api-pod')`./healthcheck.sh`;

1runtime dependencyssh2 — loaded only when an SSH target is used

4environments, one APIlocal, SSH, Docker, Kubernetes

4,500+testsacross the engine, CLI, loader and UI kit

~70msCLI startupagainst a ~28ms floor for an empty Node process

The seam this closes

Running a command somewhere other than your own machine means assembling four libraries with four APIs, four error shapes and four streaming models — then keeping them in step.

Assembled by hand

  • execalocal processes
  • ssh2remote hosts
  • dockerodecontainers
  • @kubernetes/client-nodepods

Four result types. Four ways a failure surfaces. Moving a service from a container to a host means rewriting the code that talks to it.

With xec

const result = await $.ssh(host)`systemctl status api`;

result.ok         // exit 0 and not signalled
result.stdout     // string
result.stdall     // both streams, in arrival order
result.duration   // ms

// same shape for local, docker and k8s

One result type, one error hierarchy, one streaming model. The target changes; the code does not.

The contract

Each of these is enforced by a test in this repository. They are written as promises about what will not happen to you, because that is what you need to know before running something against a production host.

An option works or it fails loudly

.cd() on a container changes the directory in the container. .env() on a pod exports in the pod, and never leaks into your own process. Nothing is accepted and quietly dropped.

No silent data loss

Output past maxBuffer kills the producer and fails with the truncated head kept — never an empty result with exit code 0. A process killed by a signal is never ok, and reports 128 + signum.

Interpolation is safe by default

Interpolated values are quoted for the shell that will actually parse them, so a value can never change the structure of a command. $.raw exists for when you mean it.

Secrets stay out of logs

Tokens, API keys, URL credentials and PEM blocks are redacted in output, events, error messages and the verbose echo — with one rule set, including across stream chunk boundaries.

Killing a command kills its tree

sh -c "node server.js" is a process tree. Kill, abort, timeout and buffer overflow all signal the whole group, so nothing is orphaned holding a port.

A cached result belongs to its target

Cache keys carry the host, container, pod, namespace and cluster. One machine’s answer is never served for another, so a health check cannot report on the wrong box.

Two ways in

As a library
npm i @xec-sh/core
const staging = $.ssh('deploy@staging')
  .cd('/srv/app')
  .env({ NODE_ENV: 'staging' })
  .timeout('60s')
  .retry({ maxRetries: 3 });

await staging`pnpm migrate`;

for await (const line of staging`tail -f app.log`) {
  if (line.includes('ERROR')) alert(line);
}

Every environment takes the same chain. Output streams as it arrives, so a follow works the way you expect.

As a CLI
npm i -g @xec-sh/cli
xec on deploy@prod-1 'systemctl restart api'
xec in postgres-main 'pg_dump mydb'
xec in production/api-7f9d 'cat app.log'

xec run deploy.ts          # a script, with $ in scope
xec forward hosts.prod 8080:80

Targets, defaults and tasks live in .xec/config.yaml. Scripts get the same API the library exposes — nothing is CLI-only.

What Xec is not

Knowing where a tool stops is worth as much as knowing what it does.

  • Not an Ansible replacement. No inventory graph, no declarative convergence. Xec is imperative TypeScript for the automation you would otherwise write in bash — with types, tests and one API instead of four.
  • Not an SDK wrapper. Adapters speak the native tools — the ssh2 protocol, the docker and kubectl CLIs — so behaviour matches what you would get by hand, exit codes included.
  • Not a dependency tree. The execution core declares one runtime dependency, ssh2, and loads it only when an SSH target is used. Running a command locally loads no third-party code at all.

Packages