Running processes
Run shell commands inside Strapkit and react to their output.
Strapkit gives you two ways to run things:
sk.exec(script)— run a script, get back{ stdout, stderr, exitCode }. Use this for builds, tests, or any one-shot command where you want the output as a value.sk.shellExec(command)— type a command into anxterm.js- attached shell. Use this for dev servers, TUIs, or anything you want the user to see in a terminal.
For full method signatures see the Processes API reference.
Run a script and use its output
const { stdout, exitCode } = await sk.exec('ls /app');
if (exitCode === 0) {
console.log(stdout);
}Multi-line scripts work the same way. Pipes, redirects, conditionals — all the normal shell tricks:
const result = await sk.exec('cd /app && npm install --silent 2>&1 && npm run build');
console.log(result.stdout);
console.log('exited', result.exitCode);Start a dev server and preview it
Servers don't exit, so exec would block forever waiting for
them. Use shellExec to start them, and the
onPortOpen callback
to know when they're ready:
sk.onPortOpen((port) => {
if (port === 3000) sk.showPreview(port, 'my-preview');
});
sk.shellExec('cd /app && npm run dev');onPortOpen fires the moment the server binds the port — no log
scraping, no guessing.
Pass input via stdin
const { stdout } = await sk.exec('grep TODO', {
stdin: 'a\nTODO: do the thing\nb\n',
});
// stdout: "TODO: do the thing\n"For larger inputs, write to a file first and cat it — that's the
same pattern you'd use in any shell.
Run a command without shell parsing
When arguments come from user input or some other data source,
building a shell string yourself is risky. Use the args option:
const search = 'TODO';
const { stdout } = await sk.exec('grep', {
args: ['-r', search, '/app/src'],
});
console.log(stdout);Each entry in args is shell-quoted automatically — spaces, quotes,
and metacharacters in search won't be re-interpreted by the shell.
Capture output while it's running
exec returns its output all at once when the script
finishes. If you need to stream output to the user as it happens (a
log panel during a long build), run the command via shellExec and
read the output through the terminal that's already attached.
If you only need the final result, exec is the cleaner
choice.
Run a few things in sequence
exec calls are queued — a later one waits for the earlier ones — so
sequences just chain naturally:
await sk.exec('mkdir -p /app/dist');
const build = await sk.exec('cd /app && npm run build');
if (build.exitCode !== 0) throw new Error(build.stderr);
await sk.exec('cp /app/dist/index.html /var/www/');If you want to fail fast inside a single script, use set -e:
await sk.exec(`
set -e
cd /app
npm run build
cp dist/index.html /var/www/
`);Detect failures
exec resolves on every exit. Check exitCode (and stderr) to
decide:
const { exitCode, stderr } = await sk.exec('npm test');
if (exitCode !== 0) {
console.error(stderr);
}exec only rejects if Strapkit itself can't run the script (the
shell isn't running, or the WASM build doesn't include the runner).
A non-zero exit code is a normal completion.
When to use shellExec instead
shellExec is right when:
- You want the command running visibly in an
xterm.jsterminal. - The command is interactive (REPL, TUI, prompts).
- The command is a long-running server you'll stop later.
For one-shot commands where you want the output as a return value,
exec is simpler.