Run small JavaScript or TypeScript snippets and regular commands on another machine over SSH.
It is designed for modern runtimes and modern hosts:
- caller runtime: latest Bun
- remote runtimes: latest Bun, Node or Deno
- remote operating systems: Windows 11, Debian-like Linux, Arch Linux and NixOS
- accepts either a raw function or a script string
- normalizes TypeScript and TSX/JSX with Babel before execution
- supports
export default, named exports and top-levelreturn - preserves structured values like
Maps andSets across exports and return values - injects globals through
serialize-javascript, including self-contained functions and values likeMap,Set,Date,URL,RegExpandBigInt - discovers the remote OS, login shell and available runtimes
- executes plain argv-style commands without shell quoting surprises
- supports globals injection for snippets
- includes a
localpseudo-target for tests and local tooling
bun add remote-targetimport RemoteTarget from 'remote-target'
const remoteTarget = new RemoteTarget('vps')
const result = await remoteTarget.run(async () => {
const fs = await import('node:fs/promises')
return await fs.readdir('/')
})import RemoteTarget from 'remote-target'
const remoteTarget = new RemoteTarget({
host: 'nas',
runtimeCandidates: ['bun', 'node'],
})
const result = await remoteTarget.run(`
import os from 'node:os'
export const platform = os.platform()
export const arch = os.arch()
`)import RemoteTarget from 'remote-target'
const remoteTarget = new RemoteTarget('pi')
const result = await remoteTarget.exec(['fastfetch', '--json'])import RemoteTarget from 'remote-target'
const remoteTarget = new RemoteTarget('tower')
await remoteTarget.init()
const runtime = remoteTarget.getRuntime()import RemoteTarget from 'remote-target'
const remoteTarget = new RemoteTarget('cloud', {
globals: {
password: 'correct horse battery staple',
},
})import RemoteTarget from 'remote-target'
const result = await RemoteTarget.run('local', () => ({
runtime: typeof Bun,
user: process.env.USERNAME,
}))run()throws when the remote snippet fails.exec()does not throw on non-zero exit codes. It returns the structured invocation result.- shell builtins still require an explicit shell invocation, for example
['pwsh', '-Command', 'echo hello']. - JSX is normalized to a tiny built-in object-based runtime so simple TSX works without React.
- exported and returned values preserve common structured types like
Map,Set,Date,URL,RegExpand typed arrays. - globals are embedded as JavaScript source, not as installed dependencies – imported module namespace objects and closure-dependent functions are still not portable. Import packages inside the remote script when needed.