diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 885d528..c268312 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,9 @@ name: CI on: push: + branches: [main] pull_request: + types: [opened, synchronize, reopened] jobs: ci: diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..3f6ff74 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "categories": { + "correctness": "warn" + }, + "rules": { + "curly": "error", + "no-unused-vars": "allow" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..399470f --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Jupiter CLI + +CLI for interacting with Jupiter's products on Solana: Spot, Perps, Lend, Prediction Markets and more. + +## Install + +Install via npm: + +```bash +npm i -g @jup-ag/cli +``` + +Or use the install script to auto-detect the best method: + +```bash +curl -fsSL https://raw.githubusercontent.com/jup-ag/cli/main/scripts/install.sh | bash +``` + +## Quick start + +```bash +# Generate a new key called 'default' +jup keys add default + +# Or import an existing Solana CLI keypair +jup keys solana-import + +# Get a swap quote +jup spot quote --from SOL --to USDC --amount 1 + +# Execute a swap +jup spot swap --from SOL --to USDC --amount 1 + +# Check your portfolio +jup spot portfolio +``` + +## Docs + +See the [`/docs`](./docs/) directory for specific guides and workflows: + +- [Setup](docs/setup.md): Installation of the CLI +- [Config](docs/config.md): CLI settings and configurations +- [Keys](docs/keys.md): Private key management +- [Spot](docs/spot.md): Swaps, transfers, token and portfolio data + +> This CLI is designed to be LLM friendly and all commands are non-interactive. Set JSON output mode for structured responses: `jup config set --output json`. diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..9459ffa --- /dev/null +++ b/docs/config.md @@ -0,0 +1,22 @@ +# Config + +Settings are stored at `~/.config/jup/settings.json`. + +## View current settings + +```bash +jup config list +``` + +## Set output format + +```bash +jup config set --output json +jup config set --output table +``` + +## Set active key + +```bash +jup config set --active-key +``` diff --git a/docs/keys.md b/docs/keys.md new file mode 100644 index 0000000..8245547 --- /dev/null +++ b/docs/keys.md @@ -0,0 +1,64 @@ +# Keys + +A key is required for signing transactions (swaps, transfers). Keys are stored locally at `~/.config/jup/keys/`. + +## Generate a new key + +```bash +jup keys add +``` + +## Import a Solana CLI keypair + +```bash +jup keys solana-import +jup keys solana-import --name mykey --path ~/.config/solana/id.json +``` + +## Recover from seed phrase or private key + +```bash +jup keys add --recover --seed-phrase "word1 word2 ..." +jup keys add --recover --private-key +``` + +`--private-key` accepts hex, base58, base64, or a JSON byte array. + +## List keys + +```bash +jup keys list +``` + +```js +// Example JSON response: +[ + { + "name": "default", + "address": "ABC1...xyz", // Solana wallet address + "active": true // if true, key is used by default for signing transactions + } +] +``` + +## Set the active key + +```bash +jup keys use +``` + +## Edit a key + +```bash +jup keys edit --name +jup keys edit --seed-phrase "word1 word2 ..." +jup keys edit --private-key +``` + +Rename a key and/or replace its credentials. Options can be combined. `--seed-phrase` and `--private-key` are mutually exclusive. + +## Delete a key + +```bash +jup keys delete +``` diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 0000000..691fea9 --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,41 @@ +# Setup + +## Install + +### Option 1: npm (recommended if npm is available) + +```bash +npm i -g @jup-ag/cli +``` + +### Option 2: Install script + +Auto-detects the best install method (volta > npm > standalone binary): + +```bash +curl -fsSL https://raw.githubusercontent.com/jup-ag/cli/main/scripts/install.sh | bash +``` + +### Option 3: Standalone binary + +Download the latest binary from GitHub releases: + +```bash +curl -fsSL https://github.com/jup-ag/cli/releases/latest/download/jup-linux-x64 -o jup +chmod +x jup +sudo mv jup /usr/local/bin/ +``` + +Replace `jup-linux-x64` with the appropriate binary for your platform (`jup-darwin-arm64`, `jup-darwin-x64`, etc.). + +### Option 4: Build from source + +Requires [Bun](https://bun.sh). + +```bash +git clone https://github.com/jup-ag/cli.git +cd cli +bun install +bun build src/index.ts --compile --outfile jup +sudo mv jup /usr/local/bin/ +``` diff --git a/skills/spot.md b/docs/spot.md similarity index 100% rename from skills/spot.md rename to docs/spot.md diff --git a/llms.txt b/llms.txt index 5a100f0..af61423 100644 --- a/llms.txt +++ b/llms.txt @@ -1,15 +1,17 @@ # Jupiter CLI (`jup`) -CLI for interacting with Jupiter's products on Solana: Spot, Perps, Lend, Prediction Markets. +CLI for interacting with Jupiter's products on Solana: Spot, Perps, Lend, Prediction Markets and more. ## Error handling -On failure, commands exit with non-zero code with an error message. In JSON mode (`--output json`), errors are returned as `{ "error": "" }`. +On failure, commands exit with non-zero code with an error message. In JSON mode (`--format json`), errors are returned as `{ "error": "" }`. ## Skills -- [Setup](skills/setup.md): Install, configure, and manage keys -- [Spot Trading](skills/spot.md): Token search, quotes, swaps, transfers, and portfolios -- Perps Trading: Perpetual futures (coming soon) +- [Setup](docs/setup.md): Installation of the CLI +- [Config](docs/config.md): CLI settings and configurations +- [Keys](docs/keys.md): Private key management +- [Spot](docs/spot.md): Swaps, transfers, token and portfolio data +- Perps: Perpetual futures (coming soon) - Lend: Lending and borrowing (coming soon) -- Prediction Markets: Create and trade prediction markets (coming soon) +- Predictions: Create and trade prediction markets (coming soon) diff --git a/oxlint.config.ts b/oxlint.config.ts deleted file mode 100644 index a4df876..0000000 --- a/oxlint.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from "oxlint"; - -export default defineConfig({ - categories: { - correctness: "warn", - }, - rules: { - curly: "error", - "no-unused-vars": "allow", - }, -}); diff --git a/package.json b/package.json index c29e1ad..40b0d6d 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,15 @@ "jup": "dist/index.js" }, "files": [ - "dist" + "dist/index.js", + "docs", + "llms.txt", + "README.md" ], "scripts": { "dev": "bun run src/index.ts", "build": "bun build src/index.ts --outdir dist --target node --format esm --minify", - "build:binary": "bun build src/index.ts --compile --outfile dist/jup", + "build:binary": "bash scripts/build.sh", "typecheck": "bunx tsc --noEmit", "lint": "bunx oxlint src", "test": "bun test", diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..f496505 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENTRY="src/index.ts" +OUTDIR="dist" + +TARGETS=( + bun-linux-x64 + bun-linux-arm64 + bun-darwin-x64 + bun-darwin-arm64 +) + +rm -rf "$OUTDIR" +mkdir -p "$OUTDIR" + +for target in "${TARGETS[@]}"; do + # bun-linux-x64 -> jup-linux-x64 + outfile="${OUTDIR}/jup-${target#bun-}" + echo "Building $outfile..." + bun build "$ENTRY" --compile --target="$target" --outfile="$outfile" +done + +echo "Done. Binaries:" +ls -lh "$OUTDIR"/jup-* diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..b531beb --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +PACKAGE="@jup-ag/cli" +BINARY="jup" +REPO="jup-ag/cli" + +info() { printf '\033[1;34m%s\033[0m\n' "$*"; } +error() { printf '\033[1;31merror: %s\033[0m\n' "$*" >&2; exit 1; } + +# Volta +if command -v volta &>/dev/null; then + info "Installing $PACKAGE via volta..." + volta install "$PACKAGE" + exit 0 +fi + +# npm +if command -v npm &>/dev/null; then + info "Installing $PACKAGE via npm..." + npm install -g "$PACKAGE" + exit 0 +fi + +# Binary fallback +info "No package manager found, installing standalone binary..." + +OS="$(uname -s)" +ARCH="$(uname -m)" + +case "$OS" in + Linux) os="linux" ;; + Darwin) os="darwin" ;; + *) error "Unsupported OS: $OS" ;; +esac + +case "$ARCH" in + x86_64|amd64) arch="x64" ;; + aarch64|arm64) arch="arm64" ;; + *) error "Unsupported architecture: $ARCH" ;; +esac + +ASSET="${BINARY}-${os}-${arch}" +INSTALL_DIR="${JUP_INSTALL_DIR:-/usr/local/bin}" +URL="https://github.com/${REPO}/releases/latest/download/${ASSET}" + +info "Downloading $URL..." +curl -fsSL "$URL" -o "/tmp/${BINARY}" +chmod +x "/tmp/${BINARY}" + +if [ -w "$INSTALL_DIR" ]; then + mv "/tmp/${BINARY}" "${INSTALL_DIR}/${BINARY}" +else + info "Elevated permissions required to install to $INSTALL_DIR" + sudo mv "/tmp/${BINARY}" "${INSTALL_DIR}/${BINARY}" +fi + +info "Installed $BINARY to ${INSTALL_DIR}/${BINARY}" diff --git a/skills/setup.md b/skills/setup.md deleted file mode 100644 index ba69523..0000000 --- a/skills/setup.md +++ /dev/null @@ -1,121 +0,0 @@ -# Setup - -## Install - -### Option 1: npm (recommended if npm is available) - -```bash -npm i -g @jup-ag/cli -``` - -### Option 2: Standalone binary - -Download the latest binary from GitHub releases: - -```bash -curl -fsSL https://github.com/jup-ag/cli/releases/latest/download/jup-linux-x64 -o jup -chmod +x jup -sudo mv jup /usr/local/bin/ -``` - -Replace `jup-linux-x64` with the appropriate binary for your platform (`jup-darwin-arm64`, `jup-darwin-x64`, etc.). - -### Option 3: Build from source - -Requires [Bun](https://bun.sh). - -```bash -git clone https://github.com/jup-ag/cli.git -cd cli -bun install -bun build src/index.ts --compile --outfile jup -sudo mv jup /usr/local/bin/ -``` - -## Configuration - -Settings are stored at `~/.config/jup/settings.json`. - -### View current settings - -```bash -jup config list -``` - -### Set output format - -```bash -jup config set --output json -jup config set --output table -``` - -### Set active key - -```bash -jup config set --active-key -``` - -## Key management - -A key is required for signing transactions (swaps, transfers). Keys are stored locally at `~/.config/jup/keys/`. - -### Generate a new key - -```bash -jup keys add -``` - -### Import a Solana CLI keypair - -```bash -jup keys solana-import -jup keys solana-import --name mykey --path ~/.config/solana/id.json -``` - -### Recover from seed phrase or private key - -```bash -jup keys add --recover --seed-phrase "word1 word2 ..." -jup keys add --recover --private-key -``` - -`--private-key` accepts hex, base58, base64, or a JSON byte array. - -### List keys - -```bash -jup keys list -``` - -```js -// Example JSON response: -[ - { - "name": "default", - "address": "ABC1...xyz", // Solana wallet address - "active": true // if true, key is used by default for signing transactions - } -] -``` - -### Set the active key - -```bash -jup keys use -``` - -### Edit a key - -```bash -jup keys edit --name -jup keys edit --seed-phrase "word1 word2 ..." -jup keys edit --private-key -``` - -Rename a key and/or replace its credentials. Options can be combined. `--seed-phrase` and `--private-key` are mutually exclusive. - -### Delete a key - -```bash -jup keys delete -``` diff --git a/src/commands/ConfigCommand.ts b/src/commands/ConfigCommand.ts index 9b5561f..a970dbd 100644 --- a/src/commands/ConfigCommand.ts +++ b/src/commands/ConfigCommand.ts @@ -5,7 +5,7 @@ import { Output } from "../lib/Output.ts"; export class ConfigCommand { public static register(program: Command): void { - const config = program.command("config").description("Manage CLI settings"); + const config = program.command("config").description("CLI settings and configurations"); config .command("list") .description("List all settings") diff --git a/src/commands/KeysCommand.ts b/src/commands/KeysCommand.ts index 0f42181..fd988d2 100644 --- a/src/commands/KeysCommand.ts +++ b/src/commands/KeysCommand.ts @@ -15,7 +15,7 @@ import { Signer } from "../lib/Signer.ts"; export class KeysCommand { public static register(program: Command): void { - const keys = program.command("keys").description("Manage keypairs"); + const keys = program.command("keys").description("Private key management"); keys .command("list") .description("List all keys") diff --git a/src/commands/SpotCommand.ts b/src/commands/SpotCommand.ts index 23e9021..e8d8e33 100644 --- a/src/commands/SpotCommand.ts +++ b/src/commands/SpotCommand.ts @@ -17,7 +17,7 @@ export class SpotCommand { public static register(program: Command): void { const spot = program .command("spot") - .description("Spot trading: token search, quotes, and swaps"); + .description("Swaps, transfers, token and portfolio data"); spot .command("tokens") .description("Search for tokens by symbol or mint address") diff --git a/src/index.ts b/src/index.ts index 285ccf3..b8db198 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,14 +15,14 @@ program .name("jup") .description("Jupiter CLI for agentic workflows") .version("0.1.0") - .option("-o, --output ", "Output format ('table' or 'json')") + .option("-f, --format ", "Output format ('table' or 'json')") .hook("preAction", (thisCommand) => { const opts = thisCommand.opts(); - if (opts.output) { - if (opts.output !== "table" && opts.output !== "json") { - throw new Error("Invalid --output format. Must be 'table' or 'json'."); + if (opts.format) { + if (opts.format !== "table" && opts.format !== "json") { + throw new Error("Invalid --format value. Must be 'table' or 'json'."); } - Output.outputOverride = opts.output; + Output.outputOverride = opts.format; } });