Why
Installing apps on a fresh jspod is a 5-line shell recipe today:
```bash
TOKEN=$(curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded'
-d 'username=me&password=me'
http://localhost:5444/idp/credentials | jq -r .access_token)
git clone https://github.com/solid-apps/ /tmp/
git -C /tmp/ -c http.extraHeader="Authorization: Bearer $TOKEN"
push http://localhost:5444/public/apps/ HEAD:main
```
It works, but it has at least three sharp edges:
- No `--depth=1` — shallow clones are rejected on push (`shallow update not allowed`). New users hit this immediately because shallow is the obvious "save bandwidth" instinct.
- `HEAD:main`, not `HEAD:gh-pages` — JSS 0.0.197+ pins auto-init to `-b main` (#471). Pushing to `HEAD:gh-pages` creates a ref but doesn't trigger `updateInstead`, so the directory is created but empty. Confusing silent failure.
- Pilot is already bundled — `/public/apps/pilot/` is non-empty from jspod's seeding, so auto-init refuses. Need to know to skip it.
A wrapper hides all three.
Proposed UX
```bash
jspod install chrome
jspod install chrome vellum win98 pdf hub # multi-arg
jspod install # curated default set
jspod install --from solid-apps chrome # registry override (default: solid-apps)
jspod install --pod http://localhost:5444 chrome # target override (default: this jspod)
```
Behavior:
- Fetch a token from the local pod's `/idp/credentials` (or accept `JSS_SINGLE_USER_PASSWORD` env / a `--token` flag)
- For each app:
- `git clone --bare` (full history) into a tmp dir
- HEAD-probe the target path: if non-empty and not a valid auto-init candidate, skip with a clear message ("pilot is bundled — already at /public/apps/pilot/")
- `git push --mirror-style` to `/public/apps/` with the correct ref name (probe target's `info/refs` to pick the right branch, default `main`)
- Clean up the tmp dir
- Report per-app status (`✓ chrome installed`, `⊘ pilot skipped (already present)`, `✗ vellum failed: …`)
Implementation
~80-120 lines in `index.js`. New `install` arg handler that branches before the existing JSS startup logic so the command can run without spawning a server (it talks to a running jspod). If no pod is reachable, error out with a clear message.
Curated default set
`chrome vellum win98 pdf hub` for v1 — anything else can come from `--from ` or a future `install-all` flag with a remote manifest. Open to a different default set.
Test plan
Why
Installing apps on a fresh jspod is a 5-line shell recipe today:
```bash
TOKEN=$(curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded'
-d 'username=me&password=me'
http://localhost:5444/idp/credentials | jq -r .access_token)
git clone https://github.com/solid-apps/ /tmp/
git -C /tmp/ -c http.extraHeader="Authorization: Bearer $TOKEN"
push http://localhost:5444/public/apps/ HEAD:main
```
It works, but it has at least three sharp edges:
A wrapper hides all three.
Proposed UX
```bash
jspod install chrome
jspod install chrome vellum win98 pdf hub # multi-arg
jspod install # curated default set
jspod install --from solid-apps chrome # registry override (default: solid-apps)
jspod install --pod http://localhost:5444 chrome # target override (default: this jspod)
```
Behavior:
Implementation
~80-120 lines in `index.js`. New `install` arg handler that branches before the existing JSS startup logic so the command can run without spawning a server (it talks to a running jspod). If no pod is reachable, error out with a clear message.
Curated default set
`chrome vellum win98 pdf hub` for v1 — anything else can come from `--from ` or a future `install-all` flag with a remote manifest. Open to a different default set.
Test plan