mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
refactor: move all shell scripts to /sh directory (#1843)
Reorganizes the project so all shell scripts live under a dedicated
/sh directory, enabling the OpenRouter rewrite URL to point at /sh/
instead of the repository root.
Moves:
- cli/install.sh → sh/cli/install.sh
- shared/*.sh → sh/shared/*.sh
- {cloud}/{agent}.sh → sh/{cloud}/{agent}.sh (48 scripts)
- {cloud}/README.md → sh/{cloud}/README.md
- e2e/*.sh → sh/e2e/*.sh
- test/macos-compat.sh → sh/test/macos-compat.sh
- test/fixtures/**/*.sh → sh/test/fixtures/**/*.sh
Updates all references:
- RAW_BASE path construction in commands.ts, update-check.ts
- GitHub auth URL in agent-setup.ts
- Self-referencing URLs in install.sh, github-auth.sh
- CI workflow paths in lint.yml, cli-release.yml
- Test file paths in install-script-validation, manifest-integrity
- Documentation in README.md, cli/README.md, CLAUDE.md
- QA scripts in .claude/skills/
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8812f693c0
commit
b84adfb74e
88 changed files with 76 additions and 68 deletions
|
|
@ -21,8 +21,8 @@ cd WORKTREE_BASE_PLACEHOLDER
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd REPO_ROOT_PLACEHOLDER
|
cd REPO_ROOT_PLACEHOLDER
|
||||||
chmod +x e2e/fly-e2e.sh
|
chmod +x sh/e2e/fly-e2e.sh
|
||||||
./e2e/fly-e2e.sh --parallel 6
|
./sh/e2e/fly-e2e.sh --parallel 6
|
||||||
```
|
```
|
||||||
|
|
||||||
Capture the full output. Note which agents passed and which failed.
|
Capture the full output. Note which agents passed and which failed.
|
||||||
|
|
@ -43,7 +43,7 @@ For each failed agent, investigate the root cause. The failure categories are:
|
||||||
- Fly.io API auth issues
|
- Fly.io API auth issues
|
||||||
- Agent-specific install script changed upstream
|
- Agent-specific install script changed upstream
|
||||||
3. Read the agent's provisioning code: `cli/src/fly/agents.ts` and `cli/src/shared/agent-setup.ts`
|
3. Read the agent's provisioning code: `cli/src/fly/agents.ts` and `cli/src/shared/agent-setup.ts`
|
||||||
4. Read the E2E provision script: `e2e/lib/provision.sh`
|
4. Read the E2E provision script: `sh/e2e/lib/provision.sh`
|
||||||
|
|
||||||
### Verification failure (app exists but checks fail)
|
### Verification failure (app exists but checks fail)
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ For each failed agent, investigate the root cause. The failure categories are:
|
||||||
```
|
```
|
||||||
2. Check if the binary path changed — read the agent's install script in `cli/src/shared/agent-setup.ts`
|
2. Check if the binary path changed — read the agent's install script in `cli/src/shared/agent-setup.ts`
|
||||||
3. Check if the env var names changed — read the agent's config in `manifest.json`
|
3. Check if the env var names changed — read the agent's config in `manifest.json`
|
||||||
4. Update the verification checks in `e2e/lib/verify.sh` if they are stale
|
4. Update the verification checks in `sh/e2e/lib/verify.sh` if they are stale
|
||||||
|
|
||||||
### Timeout (provision took too long)
|
### Timeout (provision took too long)
|
||||||
|
|
||||||
|
|
@ -65,17 +65,17 @@ For each failed agent, investigate the root cause. The failure categories are:
|
||||||
|
|
||||||
Make fixes in the worktree at WORKTREE_BASE_PLACEHOLDER. Fixes may be in:
|
Make fixes in the worktree at WORKTREE_BASE_PLACEHOLDER. Fixes may be in:
|
||||||
|
|
||||||
- `e2e/lib/provision.sh` — env vars, timeouts, headless flags
|
- `sh/e2e/lib/provision.sh` — env vars, timeouts, headless flags
|
||||||
- `e2e/lib/verify.sh` — binary paths, config file locations, env var checks
|
- `sh/e2e/lib/verify.sh` — binary paths, config file locations, env var checks
|
||||||
- `e2e/lib/common.sh` — API helpers, constants
|
- `sh/e2e/lib/common.sh` — API helpers, constants
|
||||||
- `e2e/lib/teardown.sh` — cleanup logic
|
- `sh/e2e/lib/teardown.sh` — cleanup logic
|
||||||
- `e2e/lib/cleanup.sh` — stale app detection
|
- `sh/e2e/lib/cleanup.sh` — stale app detection
|
||||||
|
|
||||||
After fixing:
|
After fixing:
|
||||||
1. Run `bash -n` on every modified `.sh` file
|
1. Run `bash -n` on every modified `.sh` file
|
||||||
2. Re-run the E2E suite for the failed agent(s) only to verify the fix:
|
2. Re-run the E2E suite for the failed agent(s) only to verify the fix:
|
||||||
```bash
|
```bash
|
||||||
./e2e/fly-e2e.sh AGENT_NAME
|
./sh/e2e/fly-e2e.sh AGENT_NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 5 — Commit and PR
|
## Step 5 — Commit and PR
|
||||||
|
|
|
||||||
|
|
@ -159,8 +159,8 @@ log "Pre-cycle cleanup done."
|
||||||
|
|
||||||
# --- Fixtures mode: load cloud credentials ---
|
# --- Fixtures mode: load cloud credentials ---
|
||||||
if [[ "${RUN_MODE}" == "fixtures" ]]; then
|
if [[ "${RUN_MODE}" == "fixtures" ]]; then
|
||||||
if [[ -f "${REPO_ROOT}/shared/key-request.sh" ]]; then
|
if [[ -f "${REPO_ROOT}/sh/shared/key-request.sh" ]]; then
|
||||||
source "${REPO_ROOT}/shared/key-request.sh"
|
source "${REPO_ROOT}/sh/shared/key-request.sh"
|
||||||
load_cloud_keys_from_config
|
load_cloud_keys_from_config
|
||||||
if [[ -n "${MISSING_KEY_PROVIDERS:-}" ]]; then
|
if [[ -n "${MISSING_KEY_PROVIDERS:-}" ]]; then
|
||||||
log "Missing keys for: ${MISSING_KEY_PROVIDERS}"
|
log "Missing keys for: ${MISSING_KEY_PROVIDERS}"
|
||||||
|
|
@ -172,7 +172,7 @@ if [[ "${RUN_MODE}" == "fixtures" ]]; then
|
||||||
log "All cloud keys available"
|
log "All cloud keys available"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log "shared/key-request.sh not found, skipping key preflight"
|
log "sh/shared/key-request.sh not found, skipping key preflight"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/workflows/cli-release.yml
vendored
2
.github/workflows/cli-release.yml
vendored
|
|
@ -79,7 +79,7 @@ jobs:
|
||||||
--title "${name} bundle v${{ steps.version.outputs.version }}" \
|
--title "${name} bundle v${{ steps.version.outputs.version }}" \
|
||||||
--notes "Pre-built ${name} cloud provider bundle.
|
--notes "Pre-built ${name} cloud provider bundle.
|
||||||
|
|
||||||
Downloaded by \`${name}/*.sh\` shims for \`bash <(curl ...)\` execution.
|
Downloaded by \`sh/${name}/*.sh\` shims for \`bash <(curl ...)\` execution.
|
||||||
|
|
||||||
**Built:** $(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
**Built:** $(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
--prerelease \
|
--prerelease \
|
||||||
|
|
|
||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
|
@ -68,4 +68,4 @@ jobs:
|
||||||
|
|
||||||
# Warn-only for initial rollout — switch to blocking after burn-in
|
# Warn-only for initial rollout — switch to blocking after burn-in
|
||||||
- name: Run macOS compat linter
|
- name: Run macOS compat linter
|
||||||
run: bash test/macos-compat.sh --warn-only
|
run: bash sh/test/macos-compat.sh --warn-only
|
||||||
|
|
|
||||||
38
CLAUDE.md
38
CLAUDE.md
|
|
@ -30,7 +30,7 @@ Look at `manifest.json` → `matrix` for any `"missing"` entry. To implement it:
|
||||||
|
|
||||||
- Find the **agent's** existing script on another cloud — it shows the install steps, config files, env vars, and launch command
|
- Find the **agent's** existing script on another cloud — it shows the install steps, config files, env vars, and launch command
|
||||||
- The agent scripts are thin bash wrappers that bootstrap bun and run the TypeScript CLI
|
- The agent scripts are thin bash wrappers that bootstrap bun and run the TypeScript CLI
|
||||||
- The script goes at `{cloud}/{agent}.sh`
|
- The script goes at `sh/{cloud}/{agent}.sh`
|
||||||
|
|
||||||
**OpenRouter injection is mandatory.** Every agent script MUST:
|
**OpenRouter injection is mandatory.** Every agent script MUST:
|
||||||
- Set `OPENROUTER_API_KEY` in the shell environment
|
- Set `OPENROUTER_API_KEY` in the shell environment
|
||||||
|
|
@ -63,7 +63,7 @@ Steps to add one:
|
||||||
2. Add an entry to `manifest.json` → `clouds`
|
2. Add an entry to `manifest.json` → `clouds`
|
||||||
3. Add `"missing"` entries to the matrix for every existing agent
|
3. Add `"missing"` entries to the matrix for every existing agent
|
||||||
4. Implement at least 2-3 agent scripts to prove the lib works
|
4. Implement at least 2-3 agent scripts to prove the lib works
|
||||||
5. Update the cloud's `README.md`
|
5. Update the cloud's `sh/{cloud}/README.md`
|
||||||
6. **Add test coverage** (mandatory) — add unit tests in `cli/src/__tests__/`
|
6. **Add test coverage** (mandatory) — add unit tests in `cli/src/__tests__/`
|
||||||
|
|
||||||
**DO NOT add GPU clouds** (CoreWeave, RunPod, etc.). Spawn agents call remote LLM APIs for inference — they need cheap CPU instances with SSH, not expensive GPU VMs.
|
**DO NOT add GPU clouds** (CoreWeave, RunPod, etc.). Spawn agents call remote LLM APIs for inference — they need cheap CPU instances with SSH, not expensive GPU VMs.
|
||||||
|
|
@ -108,12 +108,20 @@ spawn/
|
||||||
src/commands.ts # All subcommands (interactive, list, run, etc.)
|
src/commands.ts # All subcommands (interactive, list, run, etc.)
|
||||||
src/version.ts # Version constant
|
src/version.ts # Version constant
|
||||||
package.json # npm package (@openrouter/spawn)
|
package.json # npm package (@openrouter/spawn)
|
||||||
install.sh # One-liner installer (bun → npm → auto-install bun)
|
sh/
|
||||||
shared/
|
cli/
|
||||||
github-auth.sh # Standalone GitHub CLI auth helper
|
install.sh # One-liner installer (bun → npm → auto-install bun)
|
||||||
key-request.sh # API key provisioning helpers (used by QA)
|
shared/
|
||||||
{cloud}/
|
github-auth.sh # Standalone GitHub CLI auth helper
|
||||||
{agent}.sh # Agent deployment scripts (thin bash → bun wrappers)
|
key-request.sh # API key provisioning helpers (used by QA)
|
||||||
|
e2e/
|
||||||
|
fly-e2e.sh # Fly.io E2E test suite
|
||||||
|
lib/*.sh # E2E helper libraries
|
||||||
|
test/
|
||||||
|
macos-compat.sh # macOS compatibility test script
|
||||||
|
{cloud}/
|
||||||
|
{agent}.sh # Agent deployment scripts (thin bash → bun wrappers)
|
||||||
|
README.md # Cloud-specific usage docs
|
||||||
.claude/skills/setup-agent-team/
|
.claude/skills/setup-agent-team/
|
||||||
trigger-server.ts # HTTP trigger server (concurrent runs, dedup)
|
trigger-server.ts # HTTP trigger server (concurrent runs, dedup)
|
||||||
discovery.sh # Discovery cycle script (fill gaps, scout new clouds/agents)
|
discovery.sh # Discovery cycle script (fill gaps, scout new clouds/agents)
|
||||||
|
|
@ -144,17 +152,17 @@ Examples of files that should NOT be committed:
|
||||||
The only documentation files allowed in the repository are:
|
The only documentation files allowed in the repository are:
|
||||||
- `README.md` (user-facing)
|
- `README.md` (user-facing)
|
||||||
- `CLAUDE.md` (contributor guide)
|
- `CLAUDE.md` (contributor guide)
|
||||||
- Cloud-specific `README.md` files in `{cloud}/README.md`
|
- Cloud-specific `README.md` files in `sh/{cloud}/README.md`
|
||||||
|
|
||||||
If you need to create documentation during development, write it to `.docs/` and add `.docs/` to `.gitignore`.
|
If you need to create documentation during development, write it to `.docs/` and add `.docs/` to `.gitignore`.
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
|
|
||||||
All cloud provisioning and agent setup logic lives in TypeScript under `cli/src/`. Agent scripts (`{cloud}/{agent}.sh`) are thin bash wrappers that bootstrap bun and invoke the CLI.
|
All cloud provisioning and agent setup logic lives in TypeScript under `cli/src/`. Agent scripts (`sh/{cloud}/{agent}.sh`) are thin bash wrappers that bootstrap bun and invoke the CLI.
|
||||||
|
|
||||||
**`shared/github-auth.sh`** — Standalone GitHub CLI installer + OAuth login helper. Used by `cli/src/shared/agent-setup.ts` to set up `gh` on remote VMs.
|
**`sh/shared/github-auth.sh`** — Standalone GitHub CLI installer + OAuth login helper. Used by `cli/src/shared/agent-setup.ts` to set up `gh` on remote VMs.
|
||||||
|
|
||||||
**`shared/key-request.sh`** — API key provisioning helpers sourced by the QA harness (`qa.sh`) for loading cloud credentials from `~/.config/spawn/{cloud}.json`.
|
**`sh/shared/key-request.sh`** — API key provisioning helpers sourced by the QA harness (`qa.sh`) for loading cloud credentials from `~/.config/spawn/{cloud}.json`.
|
||||||
|
|
||||||
## Shell Script Rules
|
## Shell Script Rules
|
||||||
|
|
||||||
|
|
@ -176,8 +184,8 @@ macOS ships bash 3.2. All scripts MUST work on it:
|
||||||
### Conventions
|
### Conventions
|
||||||
- `#!/bin/bash` + `set -eo pipefail` (no `u` flag)
|
- `#!/bin/bash` + `set -eo pipefail` (no `u` flag)
|
||||||
- Use `${VAR:-}` for all optional env var checks (`OPENROUTER_API_KEY`, cloud tokens, etc.)
|
- Use `${VAR:-}` for all optional env var checks (`OPENROUTER_API_KEY`, cloud tokens, etc.)
|
||||||
- Remote fallback URL: `https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/{path}`
|
- Remote fallback URL: `https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/{path}` (shell scripts are under `sh/`, e.g., `sh/{cloud}/{agent}.sh`)
|
||||||
- All env vars documented in the cloud's README.md
|
- All env vars documented in the cloud's `sh/{cloud}/README.md`
|
||||||
|
|
||||||
### Use Bun + TypeScript for Inline Scripting — NEVER python/python3
|
### Use Bun + TypeScript for Inline Scripting — NEVER python/python3
|
||||||
When shell scripts need JSON processing, HTTP calls, crypto, or any non-trivial logic:
|
When shell scripts need JSON processing, HTTP calls, crypto, or any non-trivial logic:
|
||||||
|
|
@ -395,7 +403,7 @@ Draft PRs that go stale (no updates for 1 week) will be auto-closed.
|
||||||
1. `bash -n {file}` syntax check on all modified scripts
|
1. `bash -n {file}` syntax check on all modified scripts
|
||||||
2. `cd cli && bunx @biomejs/biome lint src/` — **must pass with zero errors** on all modified TypeScript
|
2. `cd cli && bunx @biomejs/biome lint src/` — **must pass with zero errors** on all modified TypeScript
|
||||||
3. Update `manifest.json` matrix status to `"implemented"`
|
3. Update `manifest.json` matrix status to `"implemented"`
|
||||||
4. Update the cloud's `README.md` with usage instructions
|
4. Update the cloud's `sh/{cloud}/README.md` with usage instructions
|
||||||
5. Commit with a descriptive message
|
5. Commit with a descriptive message
|
||||||
|
|
||||||
## Filing Issues for Discovered Problems
|
## Filing Issues for Discovered Problems
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Launch any AI agent on any cloud with a single command. Coding agents, research
|
||||||
|
|
||||||
**macOS / Linux — and Windows users inside a WSL2 terminal (Ubuntu, Debian, etc.):**
|
**macOS / Linux — and Windows users inside a WSL2 terminal (Ubuntu, Debian, etc.):**
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://openrouter.ai/labs/spawn/cli/install.sh | bash
|
curl -fsSL https://openrouter.ai/labs/spawn/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows PowerShell (outside WSL):**
|
**Windows PowerShell (outside WSL):**
|
||||||
|
|
@ -116,7 +116,7 @@ If spawn fails to install, try these steps:
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://bun.sh/install | bash
|
curl -fsSL https://bun.sh/install | bash
|
||||||
source ~/.bashrc # or ~/.zshrc for zsh
|
source ~/.bashrc # or ~/.zshrc for zsh
|
||||||
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/cli/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **PATH issues**: If `spawn` command not found after install
|
3. **PATH issues**: If `spawn` command not found after install
|
||||||
|
|
@ -160,7 +160,7 @@ If an agent fails to install or launch on a cloud:
|
||||||
|
|
||||||
## Matrix
|
## Matrix
|
||||||
|
|
||||||
| | [Local Machine](local/) | [Hetzner Cloud](hetzner/) | [Fly.io](fly/) | [AWS Lightsail](aws/) | [Daytona](daytona/) | [DigitalOcean](digitalocean/) | [GCP Compute Engine](gcp/) | [Sprite](sprite/) |
|
| | [Local Machine](sh/local/) | [Hetzner Cloud](sh/hetzner/) | [Fly.io](sh/fly/) | [AWS Lightsail](sh/aws/) | [Daytona](sh/daytona/) | [DigitalOcean](sh/digitalocean/) | [GCP Compute Engine](sh/gcp/) | [Sprite](sh/sprite/) |
|
||||||
|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|
|
||||||
| [**Claude Code**](https://claude.ai) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [**Claude Code**](https://claude.ai) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [**OpenClaw**](https://github.com/openclaw/openclaw) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [**OpenClaw**](https://github.com/openclaw/openclaw) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
@ -191,7 +191,7 @@ git config core.hooksPath .githooks
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
{cloud}/{agent}.sh # Agent deployment script (thin bash → bun wrapper)
|
sh/{cloud}/{agent}.sh # Agent deployment script (thin bash → bun wrapper)
|
||||||
cli/ # TypeScript CLI — all provisioning logic (bun)
|
cli/ # TypeScript CLI — all provisioning logic (bun)
|
||||||
manifest.json # Source of truth for the matrix
|
manifest.json # Source of truth for the matrix
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ cli/
|
||||||
│ ├── update-check.ts # Auto-update check (once per day)
|
│ ├── update-check.ts # Auto-update check (once per day)
|
||||||
│ ├── version.ts # Version constant
|
│ ├── version.ts # Version constant
|
||||||
│ └── __tests__/ # Test suite (Bun test runner)
|
│ └── __tests__/ # Test suite (Bun test runner)
|
||||||
├── install.sh # Installer (auto-installs bun if needed)
|
├── ../sh/cli/install.sh # Installer (auto-installs bun if needed, lives in sh/cli/)
|
||||||
├── package.json # Package metadata and dependencies
|
├── package.json # Package metadata and dependencies
|
||||||
└── tsconfig.json # TypeScript configuration
|
└── tsconfig.json # TypeScript configuration
|
||||||
```
|
```
|
||||||
|
|
@ -57,7 +57,7 @@ The TypeScript CLI (`src/*.ts`) provides:
|
||||||
### Quick Install
|
### Quick Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/cli/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer will:
|
The installer will:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@openrouter/spawn",
|
"name": "@openrouter/spawn",
|
||||||
"version": "0.8.5",
|
"version": "0.9.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"spawn": "cli.js"
|
"spawn": "cli.js"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { resolve, join } from "node:path";
|
||||||
import { isString } from "../shared/type-guards";
|
import { isString } from "../shared/type-guards";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validation tests for cli/install.sh.
|
* Validation tests for sh/cli/install.sh.
|
||||||
*
|
*
|
||||||
* install.sh is the critical entry point for all new users
|
* install.sh is the critical entry point for all new users
|
||||||
* (curl -fsSL ... | bash). It has been modified in multiple recent PRs
|
* (curl -fsSL ... | bash). It has been modified in multiple recent PRs
|
||||||
|
|
@ -15,7 +15,7 @@ import { isString } from "../shared/type-guards";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const REPO_ROOT = resolve(import.meta.dir, "../../..");
|
const REPO_ROOT = resolve(import.meta.dir, "../../..");
|
||||||
const INSTALL_SH = join(REPO_ROOT, "cli", "install.sh");
|
const INSTALL_SH = join(REPO_ROOT, "sh", "cli", "install.sh");
|
||||||
const content = readFileSync(INSTALL_SH, "utf-8");
|
const content = readFileSync(INSTALL_SH, "utf-8");
|
||||||
const lines = content.split("\n");
|
const lines = content.split("\n");
|
||||||
|
|
||||||
|
|
@ -450,7 +450,7 @@ describe("install.sh validation", () => {
|
||||||
|
|
||||||
describe("error handling", () => {
|
describe("error handling", () => {
|
||||||
it("should show re-run instructions on bun install failure", () => {
|
it("should show re-run instructions on bun install failure", () => {
|
||||||
expect(content).toContain("curl -fsSL ${SPAWN_RAW_BASE}/cli/install.sh | bash");
|
expect(content).toContain("curl -fsSL ${SPAWN_RAW_BASE}/sh/cli/install.sh | bash");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show manual bun install instructions on failure", () => {
|
it("should show manual bun install instructions on failure", () => {
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ describe("Manifest Integrity", () => {
|
||||||
const missing: string[] = [];
|
const missing: string[] = [];
|
||||||
|
|
||||||
for (const [key] of implemented) {
|
for (const [key] of implemented) {
|
||||||
const scriptPath = join(REPO_ROOT, key + ".sh");
|
const scriptPath = join(REPO_ROOT, "sh", key + ".sh");
|
||||||
if (!existsSync(scriptPath)) {
|
if (!existsSync(scriptPath)) {
|
||||||
missing.push(key + ".sh");
|
missing.push(key + ".sh");
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +216,7 @@ describe("Manifest Integrity", () => {
|
||||||
const badScripts: string[] = [];
|
const badScripts: string[] = [];
|
||||||
|
|
||||||
for (const [key] of sample) {
|
for (const [key] of sample) {
|
||||||
const scriptPath = join(REPO_ROOT, key + ".sh");
|
const scriptPath = join(REPO_ROOT, "sh", key + ".sh");
|
||||||
if (existsSync(scriptPath)) {
|
if (existsSync(scriptPath)) {
|
||||||
const content = readFileSync(scriptPath, "utf-8");
|
const content = readFileSync(scriptPath, "utf-8");
|
||||||
if (!content.trimStart().startsWith("#!")) {
|
if (!content.trimStart().startsWith("#!")) {
|
||||||
|
|
@ -234,7 +234,7 @@ describe("Manifest Integrity", () => {
|
||||||
const badScripts: string[] = [];
|
const badScripts: string[] = [];
|
||||||
|
|
||||||
for (const [key] of sample) {
|
for (const [key] of sample) {
|
||||||
const scriptPath = join(REPO_ROOT, key + ".sh");
|
const scriptPath = join(REPO_ROOT, "sh", key + ".sh");
|
||||||
if (existsSync(scriptPath)) {
|
if (existsSync(scriptPath)) {
|
||||||
const content = readFileSync(scriptPath, "utf-8");
|
const content = readFileSync(scriptPath, "utf-8");
|
||||||
if (!content.includes("set -eo pipefail")) {
|
if (!content.includes("set -eo pipefail")) {
|
||||||
|
|
@ -257,7 +257,7 @@ describe("Manifest Integrity", () => {
|
||||||
const orphaned: string[] = [];
|
const orphaned: string[] = [];
|
||||||
|
|
||||||
for (const [key] of missingEntries) {
|
for (const [key] of missingEntries) {
|
||||||
const scriptPath = join(REPO_ROOT, key + ".sh");
|
const scriptPath = join(REPO_ROOT, "sh", key + ".sh");
|
||||||
if (existsSync(scriptPath)) {
|
if (existsSync(scriptPath)) {
|
||||||
orphaned.push(key + ".sh");
|
orphaned.push(key + ".sh");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -834,7 +834,7 @@ function showDryRunPreview(manifest: Manifest, agent: string, cloud: string, pro
|
||||||
printDryRunSection("Agent", buildAgentLines(manifest.agents[agent]));
|
printDryRunSection("Agent", buildAgentLines(manifest.agents[agent]));
|
||||||
printDryRunSection("Cloud", buildCloudLines(manifest.clouds[cloud]));
|
printDryRunSection("Cloud", buildCloudLines(manifest.clouds[cloud]));
|
||||||
printDryRunSection("Script", [
|
printDryRunSection("Script", [
|
||||||
` URL: ${RAW_BASE}/${cloud}/${agent}.sh`,
|
` URL: ${RAW_BASE}/sh/${cloud}/${agent}.sh`,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const envLines = buildEnvironmentLines(manifest, agent);
|
const envLines = buildEnvironmentLines(manifest, agent);
|
||||||
|
|
@ -1186,7 +1186,7 @@ export async function cmdRunHeadless(agent: string, cloud: string, opts: Headles
|
||||||
|
|
||||||
// Phase 2: Download script (exit code 2)
|
// Phase 2: Download script (exit code 2)
|
||||||
const url = `https://openrouter.ai/labs/spawn/${resolvedCloud}/${resolvedAgent}.sh`;
|
const url = `https://openrouter.ai/labs/spawn/${resolvedCloud}/${resolvedAgent}.sh`;
|
||||||
const ghUrl = `${RAW_BASE}/${resolvedCloud}/${resolvedAgent}.sh`;
|
const ghUrl = `${RAW_BASE}/sh/${resolvedCloud}/${resolvedAgent}.sh`;
|
||||||
|
|
||||||
let scriptContent: string;
|
let scriptContent: string;
|
||||||
try {
|
try {
|
||||||
|
|
@ -1697,7 +1697,7 @@ async function execScript(
|
||||||
spawnName?: string,
|
spawnName?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const url = `https://openrouter.ai/labs/spawn/${cloud}/${agent}.sh`;
|
const url = `https://openrouter.ai/labs/spawn/${cloud}/${agent}.sh`;
|
||||||
const ghUrl = `${RAW_BASE}/${cloud}/${agent}.sh`;
|
const ghUrl = `${RAW_BASE}/sh/${cloud}/${agent}.sh`;
|
||||||
|
|
||||||
let scriptContent: string;
|
let scriptContent: string;
|
||||||
try {
|
try {
|
||||||
|
|
@ -3241,7 +3241,7 @@ async function fetchRemoteVersion(): Promise<string> {
|
||||||
return data.version;
|
return data.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
const INSTALL_CMD = `curl -fsSL ${RAW_BASE}/cli/install.sh | bash`;
|
const INSTALL_CMD = `curl -fsSL ${RAW_BASE}/sh/cli/install.sh | bash`;
|
||||||
|
|
||||||
async function performUpdate(_remoteVersion: string): Promise<void> {
|
async function performUpdate(_remoteVersion: string): Promise<void> {
|
||||||
const { execSync } = await import("node:child_process");
|
const { execSync } = await import("node:child_process");
|
||||||
|
|
@ -3355,7 +3355,7 @@ function getHelpAuthSection(): string {
|
||||||
|
|
||||||
function getHelpInstallSection(): string {
|
function getHelpInstallSection(): string {
|
||||||
return `${pc.bold("INSTALL")}
|
return `${pc.bold("INSTALL")}
|
||||||
curl -fsSL ${RAW_BASE}/cli/install.sh | bash`;
|
curl -fsSL ${RAW_BASE}/sh/cli/install.sh | bash`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHelpTroubleshootingSection(): string {
|
function getHelpTroubleshootingSection(): string {
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ export async function offerGithubAuth(runner: CloudRunner): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ghCmd = "curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/github-auth.sh | bash";
|
let ghCmd = "curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/shared/github-auth.sh | bash";
|
||||||
let localTmpFile = "";
|
let localTmpFile = "";
|
||||||
if (githubToken) {
|
if (githubToken) {
|
||||||
const escaped = githubToken.replace(/'/g, "'\\''");
|
const escaped = githubToken.replace(/'/g, "'\\''");
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ function performAutoUpdate(latestVersion: string): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
executor.execSync(`curl -fsSL ${RAW_BASE}/cli/install.sh | bash`, {
|
executor.execSync(`curl -fsSL ${RAW_BASE}/sh/cli/install.sh | bash`, {
|
||||||
stdio: "inherit",
|
stdio: "inherit",
|
||||||
shell: "/bin/bash",
|
shell: "/bin/bash",
|
||||||
});
|
});
|
||||||
|
|
@ -217,7 +217,7 @@ function performAutoUpdate(latestVersion: string): void {
|
||||||
console.error(pc.red(pc.bold(`${CROSS_MARK} Auto-update failed`)));
|
console.error(pc.red(pc.bold(`${CROSS_MARK} Auto-update failed`)));
|
||||||
console.error(pc.dim(" Please update manually:"));
|
console.error(pc.dim(" Please update manually:"));
|
||||||
console.error();
|
console.error();
|
||||||
console.error(pc.cyan(` curl -fsSL ${RAW_BASE}/cli/install.sh | bash`));
|
console.error(pc.cyan(` curl -fsSL ${RAW_BASE}/sh/cli/install.sh | bash`));
|
||||||
console.error();
|
console.error();
|
||||||
// Continue with original command despite update failure
|
// Continue with original command despite update failure
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Installer for the spawn CLI
|
# Installer for the spawn CLI
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
|
# curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/cli/install.sh | bash
|
||||||
#
|
#
|
||||||
# This installs spawn via bun. If bun is not available, it auto-installs it first.
|
# This installs spawn via bun. If bun is not available, it auto-installs it first.
|
||||||
#
|
#
|
||||||
|
|
@ -62,7 +62,7 @@ ensure_min_bun_version() {
|
||||||
echo " bun upgrade"
|
echo " bun upgrade"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Then re-run:"
|
echo "Then re-run:"
|
||||||
echo " curl -fsSL ${SPAWN_RAW_BASE}/cli/install.sh | bash"
|
echo " curl -fsSL ${SPAWN_RAW_BASE}/sh/cli/install.sh | bash"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
log_info "bun upgraded to ${current}"
|
log_info "bun upgraded to ${current}"
|
||||||
|
|
@ -276,7 +276,7 @@ if ! command -v bun &>/dev/null; then
|
||||||
echo " curl -fsSL https://bun.sh/install | bash"
|
echo " curl -fsSL https://bun.sh/install | bash"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Then reopen your terminal and re-run:"
|
echo "Then reopen your terminal and re-run:"
|
||||||
echo " curl -fsSL ${SPAWN_RAW_BASE}/cli/install.sh | bash"
|
echo " curl -fsSL ${SPAWN_RAW_BASE}/sh/cli/install.sh | bash"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# e2e/fly-e2e.sh — Main E2E test orchestrator for Spawn on Fly.io
|
# sh/e2e/fly-e2e.sh — Main E2E test orchestrator for Spawn on Fly.io
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./e2e/fly-e2e.sh # All agents, sequential
|
# ./sh/e2e/fly-e2e.sh # All agents, sequential
|
||||||
# ./e2e/fly-e2e.sh claude # Single agent
|
# ./sh/e2e/fly-e2e.sh claude # Single agent
|
||||||
# ./e2e/fly-e2e.sh claude codex opencode # Specific agents
|
# ./sh/e2e/fly-e2e.sh claude codex opencode # Specific agents
|
||||||
# ./e2e/fly-e2e.sh --parallel 2 # Parallel (2 at a time)
|
# ./sh/e2e/fly-e2e.sh --parallel 2 # Parallel (2 at a time)
|
||||||
# ./e2e/fly-e2e.sh --skip-cleanup # Skip stale app cleanup
|
# ./sh/e2e/fly-e2e.sh --skip-cleanup # Skip stale app cleanup
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
# Sourceable by any agent script, or executable directly via curl|bash
|
# Sourceable by any agent script, or executable directly via curl|bash
|
||||||
#
|
#
|
||||||
# Usage (sourced):
|
# Usage (sourced):
|
||||||
# source shared/github-auth.sh
|
# source sh/shared/github-auth.sh
|
||||||
# ensure_github_auth
|
# ensure_github_auth
|
||||||
#
|
#
|
||||||
# Usage (direct):
|
# Usage (direct):
|
||||||
# bash shared/github-auth.sh
|
# bash sh/shared/github-auth.sh
|
||||||
# curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/github-auth.sh | bash
|
# curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/shared/github-auth.sh | bash
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Logging helpers
|
# Logging helpers
|
||||||
|
|
@ -6,11 +6,11 @@ set -eo pipefail
|
||||||
# This script itself is bash 3.2 compatible.
|
# This script itself is bash 3.2 compatible.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash test/macos-compat.sh # Scan all .sh files
|
# bash sh/test/macos-compat.sh # Scan all .sh files
|
||||||
# bash test/macos-compat.sh --warn-only # Always exit 0
|
# bash sh/test/macos-compat.sh --warn-only # Always exit 0
|
||||||
# bash test/macos-compat.sh path/to/file.sh # Scan specific file(s)
|
# bash sh/test/macos-compat.sh path/to/file.sh # Scan specific file(s)
|
||||||
|
|
||||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
WARN_ONLY=false
|
WARN_ONLY=false
|
||||||
FILES_CHECKED=0
|
FILES_CHECKED=0
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue