···32323333## Usage
34343535+### Commands
3636+3737+The CLI supports three main commands:
3838+- **deploy**: Upload a site to your PDS (default command)
3939+- **pull**: Download a site from a PDS to a local directory
4040+- **serve**: Serve a site locally with real-time firehose updates
4141+3542### Basic Deployment
36433744Deploy the current directory:
38453946```bash
4040-wisp-cli nekomimi.ppet --path . --site my-site
4747+wisp-cli nekomimi.pet --path . --site my-site
4148```
42494350Deploy a specific directory:
···4653wisp-cli alice.bsky.social --path ./dist/ --site my-site
4754```
48555656+Or use the explicit `deploy` subcommand:
5757+5858+```bash
5959+wisp-cli deploy alice.bsky.social --path ./dist/ --site my-site
6060+```
6161+6262+### Pull a Site
6363+6464+Download a site from a PDS to a local directory:
6565+6666+```bash
6767+wisp-cli pull alice.bsky.social --site my-site --path ./downloaded-site
6868+```
6969+7070+This will download all files from the site to the specified directory.
7171+7272+### Serve a Site Locally
7373+7474+Serve a site locally with real-time updates from the firehose:
7575+7676+```bash
7777+wisp-cli serve alice.bsky.social --site my-site --path ./site --port 8080
7878+```
7979+8080+This will:
8181+1. Download the site to the specified path
8282+2. Start a local server on the specified port (default: 8080)
8383+3. Watch the firehose for updates and automatically reload files when changed
8484+4985### Authentication Methods
50865187#### OAuth (Recommended)
···7911580116## Command-Line Options
81117118118+### Deploy Command
119119+82120```
8383-wisp-cli [OPTIONS] <INPUT>
121121+wisp-cli [deploy] [OPTIONS] <INPUT>
8412285123Arguments:
86124 <INPUT> Handle (e.g., alice.bsky.social), DID, or PDS URL
···90128 -s, --site <SITE> Site name (defaults to directory name)
91129 --store <STORE> Path to auth store file (only used with OAuth) [default: /tmp/wisp-oauth-session.json]
92130 --password <PASSWORD> App Password for authentication (alternative to OAuth)
131131+ --directory Enable directory listing mode for paths without index files
132132+ --spa Enable SPA mode (serve index.html for all routes)
133133+ -y, --yes Skip confirmation prompts (automatically accept warnings)
93134 -h, --help Print help
94135 -V, --version Print version
136136+```
137137+138138+### Pull Command
139139+140140+```
141141+wisp-cli pull [OPTIONS] --site <SITE> <INPUT>
142142+143143+Arguments:
144144+ <INPUT> Handle (e.g., alice.bsky.social) or DID
145145+146146+Options:
147147+ -s, --site <SITE> Site name (record key)
148148+ -p, --path <PATH> Output directory for the downloaded site [default: .]
149149+ -h, --help Print help
150150+```
151151+152152+### Serve Command
153153+154154+```
155155+wisp-cli serve [OPTIONS] --site <SITE> <INPUT>
156156+157157+Arguments:
158158+ <INPUT> Handle (e.g., alice.bsky.social) or DID
159159+160160+Options:
161161+ -s, --site <SITE> Site name (record key)
162162+ -p, --path <PATH> Output directory for the site files [default: .]
163163+ -P, --port <PORT> Port to serve on [default: 8080]
164164+ -h, --help Print help
95165```
9616697167## How It Works
+15-16
cli/src/main.rs
···4141struct Args {
4242 #[command(subcommand)]
4343 command: Option<Commands>,
4444-4444+4545 // Deploy arguments (when no subcommand is specified)
4646 /// Handle (e.g., alice.bsky.social), DID, or PDS URL
4747- #[arg(global = true, conflicts_with = "command")]
4847 input: Option<CowStr<'static>>,
49485049 /// Path to the directory containing your static site
5151- #[arg(short, long, global = true, conflicts_with = "command")]
5050+ #[arg(short, long)]
5251 path: Option<PathBuf>,
53525453 /// Site name (defaults to directory name)
5555- #[arg(short, long, global = true, conflicts_with = "command")]
5454+ #[arg(short, long)]
5655 site: Option<String>,
57565857 /// Path to auth store file
5959- #[arg(long, global = true, conflicts_with = "command")]
5858+ #[arg(long)]
6059 store: Option<String>,
61606261 /// App Password for authentication
6363- #[arg(long, global = true, conflicts_with = "command")]
6262+ #[arg(long)]
6463 password: Option<CowStr<'static>>,
65646665 /// Enable directory listing mode for paths without index files
6767- #[arg(long, global = true, conflicts_with = "command")]
6666+ #[arg(long)]
6867 directory: bool,
69687069 /// Enable SPA mode (serve index.html for all routes)
7171- #[arg(long, global = true, conflicts_with = "command")]
7070+ #[arg(long)]
7271 spa: bool,
73727473 /// Skip confirmation prompts (automatically accept warnings)
7575- #[arg(short = 'y', long, global = true, conflicts_with = "command")]
7474+ #[arg(short = 'y', long)]
7675 yes: bool,
7776}
7877···122121123122 /// Output directory for the downloaded site
124123 #[arg(short, long, default_value = ".")]
125125- output: PathBuf,
124124+ path: PathBuf,
126125 },
127126 /// Serve a site locally with real-time firehose updates
128127 Serve {
···135134136135 /// Output directory for the site files
137136 #[arg(short, long, default_value = ".")]
138138- output: PathBuf,
137137+ path: PathBuf,
139138140139 /// Port to serve on
141141- #[arg(short, long, default_value = "8080")]
140140+ #[arg(short = 'P', long, default_value = "8080")]
142141 port: u16,
143142 },
144143}
···156155 run_with_oauth(input, store, path, site, directory, spa, yes).await
157156 }
158157 }
159159- Some(Commands::Pull { input, site, output }) => {
160160- pull::pull_site(input, CowStr::from(site), output).await
158158+ Some(Commands::Pull { input, site, path }) => {
159159+ pull::pull_site(input, CowStr::from(site), path).await
161160 }
162162- Some(Commands::Serve { input, site, output, port }) => {
163163- serve::serve_site(input, CowStr::from(site), output, port).await
161161+ Some(Commands::Serve { input, site, path, port }) => {
162162+ serve::serve_site(input, CowStr::from(site), path, port).await
164163 }
165164 None => {
166165 // Legacy mode: if input is provided, assume deploy command