# Gram

A CLI tool for interacting with Instagram via their undocumented GraphQL/REST API using cookie-based authentication.

## Disclaimer

This project uses Instagram's **undocumented** web API (and cookie auth). Instagram can change endpoints, query IDs, and anti-bot behavior at any time — **expect this to break without notice**.

## Install

```bash
npm install -g @arein/gram
# or
pnpm add -g @arein/gram
# or
bun add -g @arein/gram

# one-shot (no install)
bunx @arein/gram whoami
```

## Quickstart

```bash
# Show the logged-in account
gram whoami

# View your home feed
gram feed
gram feed --count 50

# View the explore/discover feed
gram explore

# View a post
gram post CxyzABC1234
gram https://www.instagram.com/p/CxyzABC1234/

# View a user profile
gram user instagram
gram user @instagram --json

# Search for users, hashtags, places
gram search "coffee shop"
gram search "photography" --type hashtags

# Like/unlike posts
gram like CxyzABC1234
gram unlike CxyzABC1234
```

## Commands

### Feed Commands

- `gram feed [-n count] [--json] [--json-full]` — get your home feed
- `gram explore [-n count] [--json] [--json-full]` — get the explore/discover feed

### Post Commands

- `gram post <shortcode-or-url> [--json] [--json-full]` — view a post by shortcode or URL
- `gram comments <shortcode> [-n count] [--json]` — view comments on a post
- `gram likers <shortcode> [--json]` — view users who liked a post
- `gram <shortcode-or-url>` — shorthand for `gram post <shortcode-or-url>`

### User Commands

- `gram user <username> [--json]` — view a user profile
- `gram posts <username> [-n count] [--json]` — get a user's posts
- `gram following [username] [-n count] [--json]` — get users that someone follows (defaults to you)
- `gram followers [username] [-n count] [--json]` — get someone's followers (defaults to you)

### Search

- `gram search <query> [--type <type>] [--json]` — search for users, hashtags, and places
  - Types: `blended` (default), `users`, `hashtags`, `places`

### Engagement

- `gram like <shortcode>` — like a post
- `gram unlike <shortcode>` — unlike a post
- `gram save <shortcode>` — save/bookmark a post
- `gram unsave <shortcode>` — unsave/unbookmark a post
- `gram comment <shortcode> "<text>"` — comment on a post
- `gram follow <username>` — follow a user
- `gram unfollow <username>` — unfollow a user

### Account

- `gram whoami [--json]` — show the currently authenticated user
- `gram check` — check credential availability and sources

### Query IDs

- `gram query-ids [--refresh] [--json]` — inspect or refresh cached GraphQL query IDs

## Authentication

Gram uses your existing Instagram web session. It resolves credentials in this order:

1. **CLI flags**: `--session-id`, `--csrf-token`, `--ds-user-id`
2. **Environment variables**: `SESSION_ID`, `CSRF_TOKEN`, `DS_USER_ID` (or prefixed with `INSTAGRAM_`/`IG_`)
3. **Browser cookies** via `@steipete/sweet-cookie` (Safari, Chrome, Firefox)

### Required Cookies

- `sessionid` — Your Instagram session cookie
- `csrftoken` — Cross-site request forgery token
- `ds_user_id` — Your user ID (optional but recommended)

### Browser Cookie Sources

- **Safari**: `~/Library/Cookies/Cookies.binarycookies`
- **Chrome**: `~/Library/Application Support/Google/Chrome/<Profile>/Cookies`
- **Firefox**: `~/Library/Application Support/Firefox/Profiles/<profile>/cookies.sqlite`

Override browser order via `--cookie-source` (repeatable).

## Config (JSON5)

Config precedence: CLI flags > env vars > project config > global config.

- Global: `~/.config/gram/config.json5`
- Project: `./.gramrc.json5`

Example `~/.config/gram/config.json5`:

```json5
{
  // Cookie source order for browser extraction
  cookieSource: ["safari", "chrome", "firefox"],
  chromeProfile: "Profile 1",
  chromeProfileDir: "/path/to/Chrome/Profile",
  firefoxProfile: "default-release",
  cookieTimeoutMs: 30000,
  timeoutMs: 60000
}
```

Environment shortcuts:
- `GRAM_TIMEOUT_MS`
- `GRAM_COOKIE_TIMEOUT_MS`

## Global Options

- `--session-id <token>`: Instagram sessionid cookie
- `--csrf-token <token>`: Instagram csrftoken cookie
- `--ds-user-id <id>`: Instagram ds_user_id cookie
- `--cookie-source <safari|chrome|firefox>`: browser cookie source (repeatable)
- `--chrome-profile <name>`: Chrome profile name
- `--chrome-profile-dir <path>`: Chrome/Chromium profile directory
- `--firefox-profile <name>`: Firefox profile name
- `--cookie-timeout <ms>`: cookie extraction timeout (milliseconds)
- `--timeout <ms>`: request timeout (milliseconds)
- `--plain`: stable output (no emoji, no color)
- `--no-emoji`: disable emoji output
- `--no-color`: disable ANSI colors (or set `NO_COLOR=1`)

## Output Formats

- Default: Human-readable terminal output with emoji
- `--json`: Machine-readable JSON
- `--json-full`: JSON with raw API response in `_raw` field
- `--plain`: Stable output without emoji or colors

## Library Usage

Gram can be used as a library:

```typescript
import { InstagramClient, resolveCredentials } from '@arein/gram';

const { cookies } = await resolveCredentials({ cookieSource: 'safari' });
const client = new InstagramClient({ cookies });

// Get user profile
const userResult = await client.getUser('instagram');
if (userResult.success) {
  console.log(userResult.user);
}

// Get home feed
const feedResult = await client.getHomeFeed();
if (feedResult.success) {
  console.log(feedResult.posts);
}

// Like a post
const post = await client.getPost('CxyzABC1234');
if (post.success) {
  await client.like(post.post.id);
}

// Search
const searchResult = await client.search('coffee', 'users');
```

## Query IDs

Instagram's web app uses query IDs that rotate periodically. Gram:

1. Ships with baseline IDs in `src/lib/query-ids.json`
2. Caches runtime-discovered IDs at `~/.config/gram/query-ids-cache.json` (24h TTL)
3. Can refresh on demand via `gram query-ids --refresh`

Override cache path: `GRAM_QUERY_IDS_CACHE=/path/to/file.json`

## Development

```bash
pnpm install
pnpm run build        # dist/ + bun binary
pnpm run build:dist   # dist/ only
pnpm test
pnpm run lint
```

## License

MIT
