spawn/INTERACTIVE_CURL.md
Sprite 7ff684ca1e Document interactive curl execution with process substitution
## Problem
The command `curl URL | bash` isn't interactive because curl's output
consumes bash's stdin, preventing user prompts from working.

## Solution
Use bash process substitution instead: `bash <(curl URL)`

This keeps stdin available for the script while downloading from curl.

## Changes

- Added INTERACTIVE_CURL.md - Complete guide to interactive execution
- Added NON_INTERACTIVE_MODE.md - Guide to automation/CI usage
- Updated README.md to recommend `bash <(curl ...)` format
- Documented OpenRouter URL alias pattern

## Recommended Usage

Interactive (best UX):
  bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/claude.sh)

Non-interactive (CI/CD):
  SPRITE_NAME=dev-mk1 curl URL | bash

## Why Process Substitution?

- Stdin available for prompts 
- Works like normal bash script 
- No /dev/tty workarounds needed 
- Better user experience 

Both methods are supported for maximum compatibility.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 04:59:26 +00:00

241 lines
5.6 KiB
Markdown

# Interactive Execution via curl
## The Problem with Piping
When you run `curl URL | bash`:
- curl outputs the script to stdout
- stdout is piped to bash's stdin
- bash reads the script from stdin
- **stdin is no longer available for the script to read user input**
This is why `/dev/tty` tricks and other workarounds are needed.
## The Solution: Process Substitution
Use bash process substitution instead of piping:
```bash
bash <(curl -fsSL URL)
```
### How It Works
1. `<(curl -fsSL URL)` - Process substitution creates a temporary file descriptor
2. curl downloads the script and writes to this descriptor
3. bash reads the script from the file descriptor
4. **stdin remains connected to your terminal** for interactive input!
## Comparison
### ❌ Piping (Not Interactive)
```bash
curl URL | bash
# - Script goes to bash's stdin
# - No stdin available for user input
# - Need /dev/tty workarounds
```
### ✅ Process Substitution (Fully Interactive)
```bash
bash <(curl -fsSL URL)
# - Script goes to file descriptor
# - stdin available for user input
# - Works like a normal bash script
```
## Updated Usage Examples
### Claude Code Setup (Interactive)
```bash
# Recommended - Fully interactive
bash <(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sprite/claude.sh)
# Alternative - Non-interactive with env vars
SPRITE_NAME=dev-mk1 bash <(curl -fsSL https://raw.githubusercontent.com/.../claude.sh)
```
### OpenClaw Setup (Interactive)
```bash
# Recommended - Fully interactive
bash <(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sprite/openclaw.sh)
# Alternative - Non-interactive
SPRITE_NAME=dev-mk1 bash <(curl -fsSL https://raw.githubusercontent.com/.../openclaw.sh)
```
## Why This Is Better
### Process Substitution Advantages
**Fully interactive** - stdin available for prompts
**No special code needed** - Regular `read` commands work
**Cleaner implementation** - No `/dev/tty` fallbacks required
**Works everywhere** - bash/zsh on Linux/macOS
**Better UX** - Users can see prompts and type naturally
### Piping Disadvantages
**Not interactive by default** - stdin consumed by bash
**Requires workarounds** - Need `/dev/tty`, `safe_read()`, etc.
**Fragile** - TTY may not be available (Docker, CI/CD)
**Poor UX** - Needs env vars or fails with cryptic errors
## Implementation Notes
### Current Implementation (Supports Both)
Our scripts now support BOTH approaches:
1. **Process substitution** (recommended):
```bash
bash <(curl -fsSL URL)
# Uses regular read commands, fully interactive
```
2. **Piping** (fallback):
```bash
curl URL | bash
# Fails gracefully, shows helpful error:
# "Set SPRITE_NAME environment variable for non-interactive usage"
```
3. **Non-interactive** (CI/CD):
```bash
SPRITE_NAME=dev-mk1 curl URL | bash
# Works without prompts
```
### Simplification Opportunity
If we only recommend process substitution, we could:
- Remove `safe_read()` complexity
- Remove environment variable checks
- Use simple `read -p "prompt: " var` everywhere
- Simpler codebase
**But we keep both because:**
- Some tutorials/docs use piping pattern
- CI/CD needs non-interactive mode
- Environment variables are useful anyway
- Graceful degradation is better UX
## Platform Support
### ✅ Supported Platforms
- **bash** (v3.0+) - Linux, macOS, WSL, Git Bash
- **zsh** - macOS, Linux
- Anywhere with `/dev/fd` support
### ❌ Unsupported
- Very old shells without process substitution
- `sh` (POSIX shell) - doesn't support `<(...)`
- Some embedded/minimal environments
**Fallback:** Download the script first:
```bash
curl -fsSL URL -o script.sh
bash script.sh
rm script.sh
```
## Examples
### Test Interactive Mode
```bash
# This will prompt you for sprite name and guide through OAuth
bash <(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sprite/claude.sh)
```
Expected output:
```
Claude Code on Sprite
Installing sprite CLI...
Enter sprite name: █
```
### Test with Pre-Set Name
```bash
# Skips sprite name prompt, but OAuth is interactive
SPRITE_NAME=dev-mk1 bash <(curl -fsSL https://raw.githubusercontent.com/.../claude.sh)
```
### Fully Automated
```bash
# No prompts at all
SPRITE_NAME=ci-test \
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
bash <(curl -fsSL https://raw.githubusercontent.com/.../claude.sh)
```
## Recommended Documentation Update
We should update README.md to show process substitution first:
### Before (Current)
```bash
curl https://openrouter.ai/lab/spawn/sprite/claude.sh | bash
```
### After (Recommended)
```bash
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/claude.sh)
```
This gives users the best experience by default while still supporting non-interactive usage.
## Technical Details
### What is Process Substitution?
In bash, `<(command)` is replaced with a file descriptor path like `/dev/fd/63`:
```bash
# This:
bash <(curl -fsSL URL)
# Becomes:
bash /dev/fd/63
# where /dev/fd/63 contains the curl output
```
### stdin Flow
**With piping:**
```
Terminal stdin → curl (unused)
curl stdout → bash stdin (script)
bash needs input → NO STDIN AVAILABLE ❌
```
**With process substitution:**
```
Terminal stdin → bash stdin (available!) ✅
curl stdout → /dev/fd/63 (script)
bash reads script from /dev/fd/63
bash needs input → reads from stdin ✅
```
## Security Considerations
Both methods have similar security profiles:
```bash
# Review before executing
curl -fsSL URL -o script.sh
cat script.sh # Review
bash script.sh
# Or review inline
curl -fsSL URL | less # Review
bash <(curl -fsSL URL) # Execute
```
**Best practice:** Always review scripts from the internet before executing them.