CursorPool
← 返回首页

Blink

Build and host full-stack apps with managed database, auth, storage, backend, queue, and custom domains — all from your editor.

cursor.directory·0
MCP

blink

MCP server: blink

{
  "command": "npx",
  "args": [
    "-y",
    "@blinkdotnew/mcp"
  ],
  "env": {
    "BLINK_API_KEY": "${BLINK_API_KEY}"
  }
}
规则

backend-developer

Builds the data and logic layer — database schema, edge functions, auth config, queue tasks, secrets, and integrations. Use for backend, API, and data tasks.

You are a backend developer building data and logic layers on Blink infrastructure.

## Your scope

- Database schema design and SQL migrations via `blink db query`
- Hono edge functions in `backend/index.ts` for server-side logic
- Auth provider configuration (`blink auth-config set`)
- Queue task scheduling (`blink queue enqueue`)
- Environment secrets (`blink env set`)
- Third-party integrations (Stripe, Resend, etc.) via backend routes

## What you do NOT do

- UI components, CSS, or visual design — that's the frontend developer's job
- You only edit frontend files to add data hooks (e.g., connecting `blink.db` calls)

## Backend architecture

Blink backend is a **Hono server** deployed to Cloudflare Workers for Platforms:

```typescript
// backend/index.ts
import { Hono } from 'hono'
const app = new Hono()

app.get('/api/health', (c) => c.json({ ok: true }))
app.post('/api/queue', async (c) => {
  const { taskName, payload } = await c.req.json()
  // Handle queue-delivered tasks here
  return c.json({ received: true })
})

export default app
```

Deploy: `blink backend deploy`

## Database rules

- Use `blink_db_query` or CLI `blink db query` for schema changes (CREATE TABLE, ALTER TABLE)
- Use `blink.db.<table>` from the SDK for client-side CRUD
- SQLite: booleans are `1`/`0`, use TEXT for UUIDs, INTEGER for timestamps (unix ms)
- Always add indexes on columns used in WHERE clauses and foreign keys

## Queue tasks

Enqueue: `blink queue enqueue --destination /api/queue --payload '{"taskName":"send-email"}'`
Schedule: `blink queue schedule --cron "0 * * * *" --destination /api/queue --payload '{"taskName":"hourly-cleanup"}'`
Tasks are HTTP POST requests delivered to the backend.
规则

frontend-developer

Builds and refines the visual layer — UI components, layouts, responsive design, animations, and routing. Use for visual design work, component creation, and styling tasks.

You are a frontend developer building the visual layer on Blink infrastructure.

## Your scope

- Layout, styling, and responsive design
- UI components and animations
- Routing and navigation (React Router, file-based routing)
- Theme and design system setup
- Accessibility and visual polish

## What you do NOT do

- Database schema or queries — that's the backend developer's job
- Edge function deployment or server-side logic
- Auth provider configuration (just use the already-configured `blink.auth` in components)

## Supported frameworks

- React/Vite (primary)
- Next.js (static export only for Blink hosting)
- Vue 3 + Vite
- Svelte/SvelteKit
- Astro
- Expo React Native (mobile)

## Component library

If the project uses `@blinkdotnew/ui`, import components from there. Otherwise use shadcn/ui + Tailwind CSS.

## How to add new pages

1. Create the component in your routing folder
2. Register the route in the router config
3. Link to it from navigation
4. Run `blink deploy ./dist --prod` when ready

When you need backend data, wire up `blink.db.<table>` hooks and note what tables/columns are required so the backend developer can create them.
规则

full-stack-builder

Builds complete apps on Blink infrastructure — database, auth, backend, frontend, deploy, and custom domains. Use when the user wants to create or extend a full-stack application.

You are a full-stack developer building on Blink infrastructure. You have access to managed database (SQLite/libSQL), authentication, file storage, serverless backend (Hono on CF Workers), task queues, custom domains, and production hosting.

## How you work

1. **Create the project**: Use `blink_project_create` or `blink init --name "app-name"`
2. **Set up auth**: Enable providers with `blink_auth_set_config`, then use `@blinkdotnew/sdk` auth in the frontend
3. **Design the database**: Create tables with `blink_db_query`, use `blink.db.<table>` in client code
4. **Build the frontend**: React/Vite, Next.js (static export), Vue, Svelte, or Astro
5. **Add backend if needed**: Hono server in `backend/index.ts` for webhooks, server-side secrets, third-party callbacks
6. **Deploy**: `npm run build && blink deploy ./dist --prod`
7. **Connect domain**: `blink_domains_add` then configure DNS

## Key rules

- Initialize SDK with `projectId` and `publishableKey` from environment variables
- Use `blink.db` for CRUD — never raw SQL from client code
- Backend must export a default Hono app from `backend/index.ts`
- SQLite booleans are `1`/`0`, IDs are strings with prefix (`usr_`, `post_`)
- Store secrets with `blink_env_set`, never hardcode
- Build before deploy — the deploy command uploads pre-built static files
规则

verifier

Reviews code for correctness, bugs, broken imports, type errors, missing implementations, integration issues, and security problems. Returns PASS or FAIL with actionable fixes.

You are a code reviewer verifying work done on a Blink-hosted project. Read all modified files end-to-end and check for issues.

## Checklist

1. **Imports**: No broken imports, no missing dependencies, no circular refs
2. **Types**: No type errors, correct TypeScript usage, proper null handling
3. **SDK usage**: `createClient()` called with `projectId` AND `publishableKey`, correct `blink.db` method signatures
4. **Auth**: Providers enabled before use, auth state checked before protected operations
5. **Database**: SQL syntax correct for SQLite, booleans as integers, IDs as strings, proper indexes
6. **Backend**: Hono server exports default, routes handle errors, secrets not hardcoded
7. **Deploy**: Build step exists, output dir matches deploy command, no dev-only code in production
8. **Security**: No secrets in client code, auth checks on protected routes, input validation on backend

## Output format

If everything passes:
```
**PASS** — All checks passed. No blocking issues found.
```

If issues found:
```
**FAIL** — Found N blocking issues:

1. [file:line] Description of issue
   Fix: What to do

2. [file:line] Description of issue
   Fix: What to do
```

Non-blocking suggestions (style, accessibility) go in a separate "Suggestions" section — never block on them.
Skill

blink-agents

Blink Claw agent management — managed AI agent hosting on Fly.io. List agents, check status, manage secrets. For autonomous agents, Telegram/Discord bots, and scheduled workflows.

## What is Blink Claw?

Blink Claw provides **managed AI agent hosting** on Fly.io. Each agent runs in its own isolated Fly machine with persistent storage, pre-installed tools (browser, file system, CLI), and always-on connectivity for platforms like Telegram, Discord, and Slack.

## Getting Started

```bash
# List all agents in the workspace
blink agent list

# Check agent status
blink agent status <agent-id>

# Manage agent secrets
blink secrets list <agent-id>
blink secrets set <agent-id> MY_API_KEY=sk-123
blink secrets delete <agent-id> MY_API_KEY
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_agent_list` | List all Claw agents in the workspace |
| `blink_agent_status` | Get agent status, machine info, and health |
| `blink_secrets_list` | List secret names for an agent |
| `blink_secrets_set` | Set a secret on an agent |
| `blink_secrets_delete` | Delete a secret from an agent |

## Agent Lifecycle

Agents are created and configured via the Blink dashboard at `blink.new`. Once created:

1. **Running** — Agent machine is active, processing tasks
2. **Stopped** — Machine paused, can be resumed
3. **Error** — Machine crashed, check logs

## CLI Usage

```bash
# List agents with status
blink agent list
# Output:
# ID              Name            Status    Platform    Created
# ag_a1b2c3d4     Support Bot     running   telegram    2026-03-15
# ag_e5f6g7h8     Data Scraper    stopped   none        2026-04-01

# Get detailed status
blink agent status ag_a1b2c3d4
# Output:
# Name: Support Bot
# Status: running
# Platform: telegram
# Machine: blink-claw-a1b2c3d4 (iad)
# Uptime: 3d 14h 22m
# Memory: 512MB / 1GB

# Set environment secrets
blink secrets set ag_a1b2c3d4 OPENAI_API_KEY=sk-abc123
blink secrets set ag_a1b2c3d4 TELEGRAM_BOT_TOKEN=123456:ABC

# List secrets (values hidden)
blink secrets list ag_a1b2c3d4
# Output:
# OPENAI_API_KEY     ****abc123
# TELEGRAM_BOT_TOKEN ****ET:ABC

# Remove a secret
blink secrets delete ag_a1b2c3d4 OPENAI_API_KEY
```

## Use Cases

- **Telegram/Discord/Slack bots** — always-on agents connected to messaging platforms
- **Autonomous research agents** — web browsing, data collection, report generation
- **Scheduled workflows** — periodic data processing, monitoring, alerts
- **Customer support** — AI-powered ticket triage and response
- **DevOps automation** — deployment monitoring, incident response

## Agent Architecture

Each Claw agent runs on a dedicated Fly.io machine with:
- **Persistent volume** at `/data` (workspace files, agent state)
- **Pre-installed tools**: Chromium browser, Blink CLI, Node.js
- **Networking**: public HTTPS endpoint + WebSocket gateway
- **Skills**: bundled in Docker image at `/app/skills/`

## Notes

- Agent creation/configuration is done via the Blink dashboard, not CLI
- Secrets are stored in the Fly machine's `env` config — not Fly app secrets
- Agents are billed per-hour of machine uptime
- Each agent gets its own Fly app: `blink-claw-{last8ofAgentId}`
Skill

blink-ai

AI Gateway for text generation, image generation/editing, video generation, text-to-speech, audio transcription, and AI phone calls. Unified access to 50+ models.

## Getting Started

```bash
# Generate text
blink ai text "Write a poem about coding"

# Generate image
blink ai image "A sunset over mountains" --model fal-ai/nano-banana-pro

# Generate video
blink ai video "A cooking tutorial scene" --aspect-ratio 9:16

# Text-to-speech
blink ai speech "Hello world" --voice nova

# Transcribe audio
blink ai transcribe ./recording.mp3

# AI phone call
blink ai call +15551234567 --prompt "Remind about appointment tomorrow at 3pm"
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_ai_text` | Generate text from prompt or messages |
| `blink_ai_image` | Generate or edit images |
| `blink_ai_video` | Generate video from text or image |
| `blink_ai_speech` | Convert text to audio |
| `blink_ai_transcribe` | Convert audio to text |
| `blink_ai_call` | Make AI-powered phone call |

## SDK Methods

```typescript
const { text } = await blink.ai.generateText({ prompt: 'Hello', search: true })
const { data } = await blink.ai.generateImage({ prompt: 'A robot', model: 'fal-ai/nano-banana-pro' })
const { result } = await blink.ai.generateVideo({ prompt: 'Ocean waves', model: 'fal-ai/veo3.1/fast' })
const { url } = await blink.ai.generateSpeech({ text: 'Hello', voice: 'nova' })
const { text } = await blink.ai.transcribeAudio({ audio: audioData, language: 'en' })
```

## Text Generation

```bash
# Simple prompt
blink ai text "Summarize quantum computing"

# With web search for real-time info
blink ai text "Latest AI news today" --search

# Stream output
blink ai text "Write a story" --stream
```

```typescript
await blink.ai.streamText(
  { prompt: 'Write a story...', search: true },
  (chunk) => setText(prev => prev + chunk)
)
```

## Image Models

| Model | Speed | Quality | Best For |
|-------|-------|---------|----------|
| `fal-ai/nano-banana` (default) | Fast | Good | Prototypes |
| `fal-ai/nano-banana-pro` | Standard | Excellent | Marketing, high-fidelity |
| `fal-ai/nano-banana/edit` | Fast | Good | Quick adjustments, style transfer |
| `fal-ai/nano-banana-pro/edit` | Standard | Excellent | Detailed retouching |

## Video Models

| Model | Type | Best For |
|-------|------|----------|
| `fal-ai/veo3.1/fast` (default) | T2V | Fast, good quality |
| `fal-ai/veo3.1` | T2V | Best quality |
| `fal-ai/sora-2/text-to-video/pro` | T2V | Cinematic |
| `fal-ai/veo3.1/image-to-video` | I2V | Animate images |

## Speech Voices

`alloy` (default), `echo`, `fable`, `nova`, `onyx`, `shimmer`

## Transcription Models

| Model | Notes |
|-------|-------|
| `fal-ai/whisper` (default) | Best accuracy |
| `fal-ai/wizper` | Faster, Whisper v3 optimized |
| `fal-ai/speech-to-text/turbo` | Fast, lower cost |

## Critical Rules

1. **Image URLs must be HTTPS with extension** — `.jpg`, `.png`, `.gif`, `.webp`
2. **Style transfer** — provide ALL images in array, reference by position in prompt
3. **I2V requires image upload** — upload to storage first, use public URL
4. **generateObject schema must be `type: "object"`** — wrap arrays inside object
5. **System prompts must be domain-specific** — never use "You are a helpful assistant"

## AI Phone Calls

```bash
# Outbound call with custom prompt
blink ai call +15551234567 --prompt "You are a scheduling assistant. Confirm the appointment."

# With voice selection
blink ai call +15551234567 --prompt "Remind about meeting" --voice nova
```

Requires a phone number — see `blink-notifications` skill for `blink phone buy`.
Skill

blink-auth

Authentication with managed and headless modes. Social providers, email/password, magic links, RBAC.

**Important**: Enable providers via CLI or MCP before using them in your app: `blink auth-config set --provider google --enabled true`

## MCP Tools

`blink_auth_config_get` · `blink_auth_config_set`

## Getting Started

```bash
# Enable auth providers via CLI
blink auth-config set --provider google --enabled true
blink auth-config set --provider github --enabled true
blink auth-config set --provider email --enabled true
```

## Two Auth Modes

### Managed Mode (Quick Setup)

Redirects to hosted auth page. Best for websites and MVPs. **Not for mobile.**

```typescript
const blink = createClient({
  projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
  publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
  auth: { mode: 'managed' },
})

blink.auth.login()                            // Redirects to blink.new/auth
blink.auth.login('https://app.com/dashboard') // Custom redirect after login
blink.auth.logout()
```

### Headless Mode (Custom UI)

Full control. **Required for mobile (Expo/React Native).**

```typescript
const blink = createClient({
  projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
  publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
  auth: { mode: 'headless' },
})

await blink.auth.signUp({ email, password })
await blink.auth.signInWithEmail(email, password)
await blink.auth.signInWithGoogle()
await blink.auth.signInWithGitHub()
await blink.auth.signInWithApple()
await blink.auth.signInWithMicrosoft()
```

### Mode Comparison


| Feature  | Managed       | Headless     |
| -------- | ------------- | ------------ |
| Setup    | 1 line        | Custom UI    |
| Mobile   | ❌             | ✅ Required   |
| Branding | Blink-branded | Fully custom |


## Auth State Listener (Required for React)

```typescript
useEffect(() => {
  const unsubscribe = blink.auth.onAuthStateChanged((state) => {
    setUser(state.user)
    if (!state.isLoading) setLoading(false)  // Only set false, never reset to true
  })
  return unsubscribe
}, [])
```

**Loading state**: Always show a spinner or skeleton while loading — never `return null` (causes blank screen in production).

## Signup with Custom Fields

```typescript
await blink.auth.signUp({
  email: 'user@example.com',
  password: 'password123',
  displayName: 'John Doe',
  role: 'editor',
  metadata: { company: 'Acme Inc' },
})
```

## Password Reset

```typescript
await blink.auth.sendPasswordResetEmail(email)
await blink.auth.confirmPasswordReset(token, newPassword)

// Custom flow: generate token, send your own email
const { token, resetUrl } = await blink.auth.generatePasswordResetToken(email)
```

## Magic Links

```typescript
await blink.auth.sendMagicLink(email)
await blink.auth.verifyMagicLink(token)
```

## RBAC (Role-Based Access)

```typescript
const blink = createClient({
  projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
  publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
  auth: {
    mode: 'headless',
    roles: {
      admin: { permissions: ['*'] },
      editor: { permissions: ['posts.create', 'posts.update'], inherit: ['viewer'] },
      viewer: { permissions: ['posts.read'] },
    },
  },
})

blink.auth.can('posts.update')    // Check permission
blink.auth.hasRole('admin')       // Check role
```

## Core Methods

```typescript
const user = await blink.auth.me()
await blink.auth.updateMe({ displayName: 'New Name' })
const token = await blink.auth.getValidToken()
await blink.auth.signOut()
```

## How Auth Interacts with Blink APIs

When signed in, the SDK attaches `Authorization: Bearer <JWT>` automatically. Most modules require auth. Public exceptions: analytics ingest, storage upload (add-only).

## Expo/React Native Setup

```typescript
import * as WebBrowser from 'expo-web-browser'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createClient, AsyncStorageAdapter } from '@blinkdotnew/sdk'

const blink = createClient({
  projectId: process.env.EXPO_PUBLIC_BLINK_PROJECT_ID!,
  publishableKey: process.env.EXPO_PUBLIC_BLINK_PUBLISHABLE_KEY!,
  auth: { mode: 'headless', webBrowser: WebBrowser },
  storage: new AsyncStorageAdapter(AsyncStorage),
})
```
Skill

blink-backend

Blink Backend — Hono server on CF Workers for webhooks, server-side secrets, custom APIs. Pro+ only. Deploy via CLI.

## MCP Tools

`blink_backend_status` · `blink_backend_deploy` · `blink_backend_logs`

## When to Use

- **Prefer SDK** for auth, db, storage, AI (~95% of cases)
- **Use backend** for: webhooks, server-side secrets, third-party callbacks, heavy processing
- **Never** create a backend just for CRUD — use `blink.db` instead

## Getting Started

```bash
# Check backend eligibility (Pro/Max/Team)
blink backend status

# Create the entry point
mkdir -p backend
# Write backend/index.ts (see below)

# Deploy
blink backend deploy

# View logs
blink backend logs
```

## Structure

```
backend/
├── index.ts          ← REQUIRED: export default Hono app
├── routes/
│   ├── stripe.ts
│   └── webhooks.ts
└── lib/
    └── auth.ts
```

## `backend/index.ts` (Required Entry Point)

```typescript
import { Hono } from "hono"
import { cors } from "hono/cors"
import { createClient } from "@blinkdotnew/sdk"

const app = new Hono()
app.use("*", cors())

const getBlink = (env: Record<string, string>) =>
  createClient({
    projectId: env.BLINK_PROJECT_ID,
    secretKey: env.BLINK_SECRET_KEY,  // server key, not publishableKey
  })

app.get("/health", (c) => c.json({ ok: true }))

app.post("/api/example", async (c) => {
  const blink = getBlink(c.env as Record<string, string>)
  const body = await c.req.json()
  return c.json({ success: true })
})

export default app
```

## Secrets

**Auto-injected** (never hardcode): `BLINK_PROJECT_ID`, `BLINK_SECRET_KEY`, `BLINK_PUBLISHABLE_KEY`

**User secrets** (third-party keys): Add via project settings or CLI, access as `c.env.MY_SECRET`.

## JWT Verification

```typescript
app.post("/api/protected", async (c) => {
  const blink = getBlink(c.env as Record<string, string>)
  const auth = await blink.auth.verifyToken(c.req.header("Authorization"))
  if (!auth.valid) return c.json({ error: auth.error }, 401)
  return c.json({ userId: auth.userId })
})
```

## Calling from Frontend

```typescript
// Direct fetch
const res = await fetch("https://{projectId8}.backend.blink.new/api/example", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ key: "value" }),
})

// Via SDK
const data = await blink.functions.invoke("api/example", { body: { key: "value" } })
```

`projectId8` = last 8 chars of the full project ID.

## Deployment Flow

```bash
# 1. Check eligibility
blink backend status

# 2. Write backend/index.ts

# 3. Deploy (reads all backend/ files, bundles with esbuild, deploys to CF Workers)
blink backend deploy

# 4. URL is printed: https://{projectId8}.backend.blink.new
```

## Common Errors


| Error                  | Cause                  | Fix                                   |
| ---------------------- | ---------------------- | ------------------------------------- |
| `BACKEND_REQUIRES_PRO` | Free/Starter workspace | Upgrade to Pro+                       |
| `MISSING_ENTRYPOINT`   | No `backend/index.ts`  | Create with `export default app`      |
| CORS error             | Missing middleware     | Add `app.use("*", cors())`            |
| 401 on blink-apis      | Wrong key              | Use `secretKey`, not `publishableKey` |


## Checklist

- `backend/index.ts` exports `default app`
- `app.use("*", cors())` present
- Uses `secretKey` in `createClient` (not publishableKey)
- Deployed with `blink backend deploy`
- AbortController timeout on all external `fetch()` calls
Skill

blink-connectors

OAuth connector system for 38+ third-party services. Execute API calls to Google, Notion, Slack, Discord, GitHub, Stripe, Jira, HubSpot, Salesforce, LinkedIn, and more.

## Getting Started

```bash
# List available providers
blink connector providers

# Check connection status
blink connector status slack

# Execute an API call
blink connector exec slack /conversations GET
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_connector_exec` | Execute API call through a connected provider |
| `blink_connector_providers` | List all available connector providers |
| `blink_connector_status` | Check if a provider is connected |

## Prerequisites

**Initial OAuth connection must be done in the browser** at `blink.new/settings?tab=connectors`. Users authorize each provider once, then CLI/MCP/SDK can make API calls.

## SDK Usage

```typescript
// Check status
const status = await blink.connectors.status('slack')

// GET request
const channels = await blink.connectors.execute('slack', {
  method: '/conversations',
  http_method: 'GET',
  params: { types: 'public_channel,private_channel' }
})

// POST request
await blink.connectors.execute('slack', {
  method: '/chat/postMessage',
  http_method: 'POST',
  params: { channel: 'C1234', text: 'Hello from Blink!' }
})
```

## Available Providers

| Provider | ID | Common Endpoints |
|----------|-----|-----------------|
| **Slack** | `slack` | `/conversations`, `/chat/postMessage`, `/users` |
| **Discord** | `discord` | `/user`, `/guilds`, `/channels/{id}/messages` |
| **Notion** | `notion` | `/search`, `/databases`, `/pages` |
| **Google Drive** | `google_drive` | `/files` |
| **Google Calendar** | `google_calendar` | `/events` |
| **Google Sheets** | `google_sheets` | `/spreadsheets/{id}/values/{range}` |
| **Google Docs** | `google_docs` | `/documents` |
| **Google Slides** | `google_slides` | `/presentations` |
| **LinkedIn** | `linkedin` | `/userinfo`, `/ugcPosts` |
| **HubSpot** | `hubspot` | `/contacts`, `/companies`, `/deals` |
| **Salesforce** | `salesforce` | `/query`, `/sobjects/{type}` |
| **GitHub** | `github` | `/user`, `/repos`, `/issues` |
| **Stripe** | `stripe` | `/customers`, `/charges`, `/subscriptions` |
| **Jira** | `jira` | `/search`, `/issue`, `/project` |
| **Microsoft** | `microsoft` | `/me`, `/messages`, `/events` |
| **Airtable** | `airtable` | `/bases`, `/records` |

Plus 20+ more — run `blink connector providers` for the full list.

## CLI Examples

```bash
# Slack: list channels
blink connector exec slack /conversations GET

# Notion: search pages
blink connector exec notion /search POST '{"query":"meeting notes"}'

# Google Sheets: read data
blink connector exec google_sheets "/spreadsheets/SHEET_ID/values/Sheet1!A1:D10" GET

# HubSpot: list contacts
blink connector exec hubspot /contacts GET '{"limit":50}'

# Salesforce: SOQL query
blink connector exec salesforce /query GET '{"q":"SELECT Id,Name FROM Account LIMIT 10"}'
```

**CLI syntax**: `blink connector exec <provider> <endpoint> [method] [params]`

## Method Path Rules

1. **Paths start with `/`** — `/conversations` not `conversations`
2. **Dynamic IDs go in the path** — `/channels/${channelId}/messages`
3. **Provider IDs use underscores** — `google_drive`, `google_calendar`, `google_sheets`
4. **GET params** become query parameters; **POST params** become JSON body

## Error Codes

| Code | Meaning |
|------|---------|
| `CONNECTOR_NOT_CONNECTED` | User hasn't authorized this provider |
| `CONNECTOR_DISABLED` | Provider disabled for this project |
| `MISSING_SCOPE` | Reconnect with additional permissions |
| `*_API_ERROR` | Upstream API error (e.g. `SLACK_API_ERROR`) |

Token refresh is handled automatically by Blink backend.
Skill

blink-database

Database CRUD with automatic camelCase conversion. Create, list, update, delete, upsert, count, exists. Raw SQL via CLI. Boolean handling for SQLite.

## MCP Tools

`blink_db_query` · `blink_db_schema`

## Getting Started

```bash
# Create a table
blink db query "CREATE TABLE todos (id TEXT PRIMARY KEY, title TEXT NOT NULL, user_id TEXT, is_completed INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')))"

# Inspect schema
blink db query "SELECT name FROM sqlite_master WHERE type='table'"

# Run any SQL
blink db query "SELECT * FROM todos LIMIT 10"
```

## Automatic Case Conversion

SDK converts camelCase ↔ snake_case automatically. **Always use camelCase in code.**

```typescript
// ✅ Correct
await blink.db.emailDrafts.create({ userId: user.id, createdAt: new Date() })

// ❌ Wrong — never use snake_case in SDK calls
await blink.db.email_drafts.create({ user_id: user.id })
```

## CRUD Operations

```typescript
// Create (ID auto-generates as string)
const todo = await blink.db.todos.create({ title: 'Learn Blink', userId: user.id, isCompleted: false })

// Get by ID
const todo = await blink.db.todos.get('todo_12345')

// List with filtering
const todos = await blink.db.todos.list({
  where: { AND: [{ userId: user.id }, { OR: [{ status: 'open' }, { priority: 'high' }] }] },
  orderBy: { createdAt: 'desc' },
  limit: 20,
  offset: 0,
})

// Update
await blink.db.todos.update('todo_12345', { isCompleted: true })

// Delete
await blink.db.todos.delete('todo_12345')

// Upsert
await blink.db.todos.upsert({ id: 'todo_12345', title: 'Updated', userId: user.id })

// Count / Exists
const count = await blink.db.todos.count({ where: { userId: user.id } })
const exists = await blink.db.todos.exists({ where: { id: 'todo_12345' } })
```

### Batch Operations

```typescript
await blink.db.todos.createMany([{ title: 'Task 1', userId: user.id }, { title: 'Task 2', userId: user.id }])
await blink.db.todos.updateMany([{ id: 'todo_1', isCompleted: true }, { id: 'todo_2', isCompleted: true }])
await blink.db.todos.deleteMany({ where: { userId: user.id, isCompleted: "1" } })
```

## Boolean Handling (CRITICAL)

SQLite returns booleans as `"0"`/`"1"` strings, not `true`/`false`.

```typescript
// ✅ Correct
const completed = todos.filter(todo => Number(todo.isCompleted) > 0)

// Filter in queries
const done = await blink.db.todos.list({ where: { isCompleted: "1" } })
```

## TypeScript

```typescript
interface Todo {
  id: string        // MUST be string, never number
  title: string
  isCompleted: boolean  // Returned as "0"/"1" from SQLite
  userId: string
  createdAt: string
}

// Typed queries
const todos = await blink.db.table<Todo>('todos').list()
```

## Filtering Options

| Option | Type | Example |
|--------|------|---------|
| `where` | object | `{ userId: 'abc', status: 'active' }` |
| `orderBy` | object | `{ createdAt: 'desc' }` |
| `limit` | number | `20` |
| `offset` | number | `0` |
| `select` | array | `['id', 'title']` |

## Auth & RLS

REST CRUD is JWT-protected by default — initialize with auth for user-scoped data. RLS enforces per-user access server-side.

```typescript
const blink = createClient({
  projectId: process.env.NEXT_PUBLIC_BLINK_PROJECT_ID,
  publishableKey: process.env.NEXT_PUBLIC_BLINK_PUBLISHABLE_KEY,
  auth: { mode: 'managed' },
})
```

## Async Operations Warning

Never create a DB record before an async operation (AI, upload) completes — nullable fields or missing data will fail NOT NULL constraints. Create records only **after** you have all data.
Skill

blink-deploy

Build and deploy Blink apps to production. Preview vs production deploys, deploy pipeline, static site hosting.

## MCP Tools

`blink_deploy` · `blink_deployments_list` · `blink_rollback`

## Getting Started

```bash
# Build your app
npm run build

# Deploy to production
blink deploy ./dist --prod

# Preview deploy (temporary URL)
blink deploy ./dist

# List deployments
blink deployments

# Rollback to a previous deployment
blink rollback <deployment_id>
```

## Deploy Pipeline

```
1. npm run build          → generates ./dist (or .next, out/, build/)
2. blink deploy ./dist    → uploads to Blink hosting
3. URL printed            → {projectId}.sites.blink.new (or custom domain)
```

## Preview vs Production

| Flag | Behavior | URL |
|------|----------|-----|
| (none) | Preview deploy | Temporary preview URL |
| `--prod` | Production deploy | `{projectId}.sites.blink.new` + custom domains |

```bash
# Preview — test before going live
blink deploy ./dist
# → https://preview-abc123.sites.blink.new

# Production — replaces live site
blink deploy ./dist --prod
# → https://{projectId}.sites.blink.new
```

## Framework Build Outputs

| Framework | Build Command | Output Dir |
|-----------|--------------|------------|
| React (Vite) | `vite build` | `./dist` |
| Next.js (`output: 'export'`) | `next build` | `./out` |
| Vue | `vite build` | `./dist` |
| Svelte | `vite build` | `./build` |
| Astro | `astro build` | `./dist` |
| Plain HTML/CSS/JS | — | `./` |

For Next.js static export, ensure `next.config.ts` has `output: 'export'`.

## Backend Deploy

Backend (Hono on CF Workers) has its own deploy command — see `blink-backend` skill.

```bash
blink backend deploy
```

## Full Production Checklist

```bash
# 1. Ensure env vars are set
# 2. Build
npm run build

# 3. Deploy frontend
blink deploy ./dist --prod

# 4. Deploy backend (if applicable)
blink backend deploy

# 5. Set up custom domain (optional)
blink domains add myapp.com
```

## Common Issues

| Issue | Fix |
|-------|-----|
| Empty deploy | Check build output directory exists and has files |
| 404 after deploy | Verify correct output dir (`dist/`, `out/`, `build/`) |
| Env vars missing | Set secrets in project settings before build |
| Stale deploy | Ensure `--prod` flag for production updates |
Skill

blink-domains

Custom domain management. Add domains, DNS setup, SSL verification, domain search, and domain purchase via CLI.

## MCP Tools

`blink_domains_add` · `blink_domains_list` · `blink_domains_verify` · `blink_domains_remove` · `blink_domains_search` · `blink_domains_purchase`

## Getting Started

```bash
# Add a custom domain to your project
blink domains add myapp.com

# Check domain status and DNS instructions
blink domains list

# Verify DNS is configured (domain_id from `blink domains list` or `blink domains add`)
blink domains verify <domain_id>
```

## Adding a Custom Domain

```bash
blink domains add myapp.com
```

The CLI outputs DNS records you need to configure:

```
Configure these DNS records at your registrar:

  Type   Name    Value
  CNAME  @       cname.blink.new
  CNAME  www     cname.blink.new

After configuring DNS, run: blink domains verify <domain_id>
```

## DNS Configuration

| Domain Type | Record Type | Name | Value |
|-------------|-------------|------|-------|
| Apex (`myapp.com`) | CNAME or A | `@` | `cname.blink.new` |
| Subdomain (`www`) | CNAME | `www` | `cname.blink.new` |
| Wildcard | CNAME | `*` | `cname.blink.new` |

**Apex + www**: Add both records. Blink auto-configures SSL for both.

## SSL Verification

SSL certificates are provisioned automatically after DNS propagation. Verify status:

```bash
# domain_id comes from `blink domains list` or `blink domains add` response
blink domains verify <domain_id>
# → SSL: active | pending | error
```

DNS propagation can take 1–48 hours. Most providers propagate within 10 minutes.

## Domain Search & Purchase

```bash
# Search for available domains
blink domains search myapp

# Purchase a domain (if available)
blink domains purchase myapp.com
```

Purchased domains are auto-configured — no manual DNS needed.

## Managing Domains

```bash
# List all domains on current project
blink domains list

# Remove a domain (use domain_id, not domain name)
blink domains remove <domain_id>
```

## Apex → www Redirect

For apex domains that can't use CNAME records (some registrars), Blink supports automatic apex-to-www redirects:

```bash
blink domains add myapp.com --redirect-www
```

This stores a redirect rule so `myapp.com` → `www.myapp.com` via 301.

## Full Setup Flow

```bash
# 1. Deploy your app first
blink deploy ./dist --prod

# 2. Add domain
blink domains add myapp.com

# 3. Configure DNS at registrar (follow output instructions)

# 4. Verify (use domain_id from step 2)
blink domains verify <domain_id>

# 5. Done — site is live at myapp.com with SSL
```

## Common Issues

| Issue | Fix |
|-------|-----|
| SSL pending | Wait for DNS propagation (up to 48h) |
| CNAME conflict | Remove existing A/AAAA records for the same name |
| Apex CNAME not supported | Use A record or enable www redirect |
| Domain not resolving | Verify records with `dig myapp.com` |
Skill

blink-full-stack

End-to-end guide for building and shipping a Blink app. Project setup, SDK init, auth, database, backend, deploy, and custom domains. Index to all other skills.

## Overview

This is the master guide for building a full-stack Blink app from zero to production. Each section links to a deeper skill.

## Step 1 — Project Setup

```bash
# Create project via CLI
blink init my-app --template next

# Install SDK
cd my-app && bun add @blinkdotnew/sdk
```

## Step 2 — SDK Initialization

```typescript
import { createClient } from '@blinkdotnew/sdk'

export const blink = createClient({
  projectId: process.env.NEXT_PUBLIC_BLINK_PROJECT_ID || 'your-project-id',
  publishableKey: process.env.NEXT_PUBLIC_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
  auth: { mode: 'managed' },
})
```

Env var prefixes by framework: `NEXT_PUBLIC_` (Next.js), `VITE_` (Vite), `EXPO_PUBLIC_` (Expo).

**Why fallbacks?** If env vars are undefined, the SDK fails silently and the app hangs.

## Step 3 — Authentication

```typescript
blink.auth.login()  // managed mode — redirects to hosted auth
// OR headless mode for custom UI:
await blink.auth.signInWithEmail(email, password)
```

→ Full details: see `blink-auth` skill

## Step 4 — Database

```bash
# Create tables via CLI
blink db query "CREATE TABLE todos (id TEXT PRIMARY KEY, title TEXT NOT NULL, user_id TEXT, is_completed INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')))"
```

```typescript
const todos = await blink.db.todos.list({
  where: { userId: user.id },
  orderBy: { createdAt: 'desc' },
})
```

→ Full details: see `blink-database` skill

## Step 5 — Backend (Pro+)

Write `backend/index.ts` with a Hono server for webhooks, secrets, server logic.

```bash
blink backend deploy
```

→ Full details: see `blink-backend` skill

## Step 6 — Deploy

```bash
npm run build
blink deploy ./dist --prod
```

→ Full details: see `blink-deploy` skill

## Step 7 — Custom Domain

```bash
blink domains add myapp.com
# Follow DNS instructions from output
blink domains verify myapp.com
```

→ Full details: see `blink-domains` skill

## Key Rules

1. **camelCase everywhere** in SDK code — `userId`, `createdAt` (auto-converts to snake_case)
2. **Boolean values** from SQLite are `"0"`/`"1"` strings — use `Number(val) > 0`
3. **IDs are strings**, never numbers — `"todo_abc123"` format
4. **Auth required** for most SDK modules — set up auth before DB/storage calls

## Skill Index

| Skill | Use When |
|-------|----------|
| `blink-auth` | Login, signup, social providers, RBAC |
| `blink-database` | CRUD operations, filtering, raw SQL |
| `blink-storage` | File upload, download, management |
| `blink-backend` | Webhooks, server secrets, custom APIs |
| `blink-queue` | Background tasks, cron jobs |
| `blink-deploy` | Build and deploy to production |
| `blink-domains` | Custom domains, DNS, SSL |
| `blink-ai` | Text/image/video generation, TTS, transcription, AI calls |
| `blink-agents` | Managed AI agent hosting (Blink Claw) |
| `blink-connectors` | OAuth integrations (Slack, Google, Notion, 38+ services) |
| `blink-rag` | Knowledge base, vector search, AI Q&A with citations |
| `blink-realtime` | WebSocket pub/sub, presence, message history |
| `blink-notifications` | Email, SMS, phone numbers |

## Supported Frameworks

**Frontend**: React (Vite), Next.js (static export), Vue, Svelte, Astro, Expo React Native, plain HTML/CSS/JS

**Backend**: TypeScript/JavaScript only (Hono on CF Workers)
Skill

blink-notifications

Send emails (HTML/text, attachments, cc/bcc), SMS messages, and manage phone numbers for AI calling. Platform-provided — no API keys needed.

## Getting Started

```bash
# Send email
blink notify email user@example.com --subject "Welcome!" --html "<h1>Hello</h1>"

# Send SMS
blink sms send +15551234567 "Your code is 123456"

# List phone numbers
blink phone list

# Buy a phone number
blink phone buy --area-code 415
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_notify_email` | Send email with HTML/text, attachments, cc/bcc |
| `blink_sms_send` | Send SMS message |
| `blink_phone_list` | List owned phone numbers |
| `blink_phone_buy` | Purchase a phone number |

## Email

No setup required — built into Blink. From address auto-generated as `noreply@{projectId}.blink-email.com`.

```typescript
const { success, messageId } = await blink.notifications.email({
  to: 'customer@example.com',
  subject: 'Order shipped!',
  html: '<h1>Your order is on the way</h1>',
  text: 'Your order is on the way',
})
```

### Full Options

```typescript
await blink.notifications.email({
  to: ['team@example.com', 'manager@example.com'],
  replyTo: 'support@mycompany.com',
  cc: 'accounting@mycompany.com',
  bcc: 'archive@mycompany.com',
  subject: 'Invoice #12345',
  html: '<h2>Invoice Ready</h2><p>See attached.</p>',
  text: 'Invoice Ready. See attached.',
  attachments: [{
    url: 'https://storage.example.com/invoices/12345.pdf',
    filename: 'Invoice-12345.pdf',
    type: 'application/pdf'
  }]
})
```

### Email Options

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `to` | string \| string[] | Yes | Recipient(s) |
| `subject` | string | Yes | Subject line |
| `html` | string | Yes* | HTML content |
| `text` | string | Yes* | Plain text fallback |
| `replyTo` | string | No | Reply-to address |
| `cc` | string \| string[] | No | CC recipients |
| `bcc` | string \| string[] | No | BCC recipients |
| `attachments` | array | No | File attachments (url + filename) |

*Either `html` or `text` required (both recommended for deliverability)

## SMS

```bash
blink sms send +15551234567 "Your verification code is 847291"
```

```typescript
await blink.notifications.sms({
  to: '+15551234567',
  body: 'Your verification code is 847291'
})
```

## Phone Numbers

Phone numbers are required for AI calling (see `blink-ai` skill) and SMS.

```bash
# List available numbers
blink phone list

# Buy a number in a specific area code
blink phone buy --area-code 415

# Buy any available number
blink phone buy
```

## Best Practices

1. **Always include both HTML and text** for email deliverability
2. **Use `replyTo`** instead of custom `from` address
3. **Validate email format** before sending
4. **Phone numbers use E.164 format** — `+1` prefix for US numbers
Skill

blink-queue

Background task queue and cron schedules. Enqueue tasks, named FIFO queues with parallelism, auto-retry. Requires Blink Backend (Pro+).

## MCP Tools

`blink_queue_enqueue` · `blink_queue_schedule` · `blink_queue_list` · `blink_queue_stats` · `blink_queue_cancel` · `blink_queue_get` · `blink_queue_retry_dead` · `blink_queue_create_queue`

## Getting Started

```bash
# Check backend is deployed (required for queue delivery)
blink backend status

# Enqueue a task
blink queue enqueue send-welcome-email --payload '{"to":"user@example.com"}'

# Create a cron schedule
blink queue schedule create daily-digest "0 9 * * *" --payload '{"reportType":"daily"}'

# List tasks
blink queue list --status pending

# View stats
blink queue stats
```

**Min SDK: `@blinkdotnew/sdk >= 2.5.0`**

## Critical Rules

1. **Backend required** — deploy backend first; queue delivery fails without it
2. **Write handler first** — add `POST /api/queue` to `backend/index.ts` BEFORE enqueueing
3. **Schedule name = taskName** — handler receives schedule `name` as `taskName`
4. **Deploy after handler changes** — `blink backend deploy`
5. **Return 2xx** — non-2xx triggers retry, then dead-letter queue

## Step 1 — Add Handler to `backend/index.ts`

```typescript
app.post('/api/queue', async (c) => {
  const { taskName, payload } = await c.req.json()

  switch (taskName) {
    case 'send-welcome-email':
      await sendEmail(payload.to, payload.displayName)
      return c.json({ ok: true })

    case 'daily-digest':
      await generateDigest(payload.reportType)
      return c.json({ ok: true })

    default:
      return c.json({ error: `Unknown task: ${taskName}` }, 400)
  }
})
```

**Payload by source:**

| Source | Delivered body |
|--------|---------------|
| `blink.queue.enqueue(taskName, payload)` | `{ taskName, taskId, payload }` |
| `blink.queue.schedule(name, cron, payload)` | `{ taskName: name, payload }` (no taskId) |

## Step 2 — Deploy, Then Enqueue

```bash
blink backend deploy
blink queue enqueue send-welcome-email --payload '{"to":"user@example.com"}' --queue emails --delay 10s
```

## SDK Methods

```typescript
// Enqueue
await blink.queue.enqueue('send-welcome-email', { to: 'user@example.com' })
await blink.queue.enqueue('send-welcome-email', { to: 'user@example.com' }, {
  queue: 'emails', delay: '5m', retries: 3,
})

// Schedule — name IS the taskName
await blink.queue.schedule('daily-digest', '0 9 * * *', { type: 'daily' })
await blink.queue.schedule('daily-digest', '0 9 * * *', { type: 'daily' }, {
  timezone: 'America/New_York',
})

// Manage schedules
await blink.queue.pauseSchedule('daily-digest')
await blink.queue.resumeSchedule('daily-digest')
await blink.queue.deleteSchedule('daily-digest')

// Named queues (create once, idempotent)
await blink.queue.createQueue('emails', { parallelism: 10 })

// Inspect & manage
const tasks = await blink.queue.list({ status: 'pending', queue: 'emails' })
await blink.queue.cancel(taskId)
const dead = await blink.queue.listDead()
await blink.queue.retryDead(taskId)
const stats = await blink.queue.stats()
```

## Cron Patterns

| Cron | When |
|------|------|
| `0 9 * * *` | Daily at 9am |
| `0 * * * *` | Every hour |
| `*/15 * * * *` | Every 15 minutes |
| `0 9 * * 1` | Every Monday at 9am |

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| Tasks not delivering | Backend not deployed | `blink backend deploy` |
| `Unknown task: X` | Missing case in handler | Add matching case |
| Tasks retrying | Handler returning non-2xx | Return `c.json({ ok: true })` |
| `cancel` returns 409 | Task not pending | Can only cancel `'pending'` tasks |

## Billing

1 credit = 1,000 tasks (0.001 credits/task). Retries and reads are free. Pro+ only.
Skill

blink-rag

Knowledge base with vector search, document upload, collections, and AI-powered Q&A with citations. Semantic search over uploaded documents.

## Getting Started

```bash
# Search a collection
blink rag search docs "How do I configure auth?"

# Upload a document
blink rag upload docs ./guide.txt

# List collections
blink rag collections
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_rag_search` | Semantic search or AI Q&A over a collection |
| `blink_rag_collections` | List available collections |

## SDK Methods

```typescript
// Create collection
const col = await blink.rag.createCollection({ name: 'docs', description: 'Product docs' })

// Upload document
const doc = await blink.rag.upload({
  collectionName: 'docs',
  filename: 'guide.txt',
  content: 'Your content...',
})
await blink.rag.waitForReady(doc.id)

// Vector search
const results = await blink.rag.search({
  collectionName: 'docs',
  query: 'How do I configure auth?',
  maxResults: 5,
})

// AI search (RAG) — returns answer + sources
const result = await blink.rag.aiSearch({
  collectionName: 'docs',
  query: 'What are the main features?',
  model: 'google/gemini-3-flash',
})
console.log(result.answer, result.sources)
```

## Upload Methods

```typescript
// Text content
await blink.rag.upload({ collectionName: 'docs', filename: 'notes.txt', content: 'Text...' })

// From URL
await blink.rag.upload({ collectionName: 'docs', filename: 'article.html', url: 'https://example.com/article' })

// File (base64)
await blink.rag.upload({ collectionName: 'docs', filename: 'report.pdf', file: { data: base64, contentType: 'application/pdf' } })
```

## Critical: PDF Upload Pattern

**Do NOT upload PDFs directly as base64** — embeddings may store incorrectly. Extract text first:

```typescript
// 1. Upload PDF to storage
const { publicUrl } = await blink.storage.upload(pdfFile, `docs/${Date.now()}_${pdfFile.name}`)

// 2. Extract text
const text = await blink.data.extractFromUrl(publicUrl)

// 3. Upload extracted text as content
await blink.rag.upload({ collectionName: 'docs', filename: pdfFile.name, content: text })
```

## Document Lifecycle

Documents go through: `pending` → `processing` → `ready` (or `error`). Processing takes 30-40 seconds. Always wait before searching:

```typescript
const doc = await blink.rag.upload({ ... })
await blink.rag.waitForReady(doc.id, { timeoutMs: 120000 })
```

## Streaming AI Search

```typescript
const stream = await blink.rag.aiSearch({
  collectionName: 'docs',
  query: 'Explain the architecture',
  stream: true,
})
```

## Model Selection

**Always use `google/gemini-3-flash`** for RAG AI search. Deprecated models will fail.

## Common Errors

| Error | Fix |
|-------|-----|
| 409 "Collection exists" | Catch and reuse existing collection |
| Zero tokens in search | Document still processing — wait for `ready` status |
| "Identical content" on upload | Duplicate doc — catch error, use existing doc ID |
Skill

blink-realtime

WebSocket pub/sub messaging with channels, presence tracking, and message history. Real-time communication for chat, collaboration, and live updates.

**Requires auth**: initialize with `auth: { mode: 'managed' }` for realtime to work.

## Getting Started

```bash
# Publish a message to a channel
blink realtime publish chat-room --type message --data '{"text":"Hello everyone!"}'

# Publish with user context
blink realtime publish notifications --type alert --data '{"level":"info","text":"Deploy complete"}'
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `blink_realtime_publish` | Publish event to a channel |

## SDK Methods

```typescript
// Simple subscribe/publish
const unsubscribe = await blink.realtime.subscribe('chat-room', (message) => {
  console.log(message.data)
})

await blink.realtime.publish('chat-room', 'message', { text: 'Hello!' })
unsubscribe()
```

## Channel API (Advanced)

```typescript
const channel = blink.realtime.channel('game-lobby')

await channel.subscribe({
  userId: user.id,
  metadata: { displayName: user.name, status: 'online' }
})

channel.onMessage((msg) => {
  if (msg.type === 'chat') addMessage(msg.data)
})

channel.onPresence((users) => {
  setOnlineUsers(users)
})

await channel.publish('chat', { text: 'Hello!' }, { userId: user.id })

const history = await channel.getMessages({ limit: 50 })
const users = await channel.getPresence()

await channel.unsubscribe()
```

## Message Format

```typescript
{
  id: '1640995200000-0',
  type: 'chat',
  data: { text: 'Hello!' },
  timestamp: 1640995200000,
  userId: 'user123',
  metadata: { displayName: 'John' }
}
```

## Presence Format

```typescript
{
  userId: 'user123',
  metadata: { displayName: 'John', status: 'online' },
  joinedAt: 1640995200000,
  lastSeen: 1640995230000
}
```

## React Cleanup (Critical)

```typescript
useEffect(() => {
  if (!user?.id) return

  let channel: any = null

  const init = async () => {
    channel = blink.realtime.channel('room')
    await channel.subscribe({ userId: user.id })
    channel.onMessage((msg: any) => setMessages(prev => [...prev, msg]))
  }

  init().catch(console.error)

  return () => { channel?.unsubscribe() }
}, [user?.id])
```

**Never** return cleanup from inside an async function — it gets lost. Store channel reference outside and clean up synchronously.

## Use Cases

- **Chat apps** — messages + presence + history
- **Live collaboration** — cursor positions, document edits
- **Notifications** — real-time alerts pushed to connected clients
- **Gaming** — game state sync, lobby management
- **Dashboards** — live data updates
Skill

blink-storage

File upload with progress tracking and public URLs. Download, remove files. CLI for management. Extension auto-detection.

## Getting Started

```bash
# Upload a file
blink storage upload ./photo.jpg --path uploads/photo.jpg

# List files
blink storage list

# List files in a path
blink storage list uploads/
```

## Auth Requirements


| Operation    | Auth Required | Notes                          |
| ------------ | ------------- | ------------------------------ |
| `upload()`   | ❌ No          | Public add-only (no overwrite) |
| `download()` | ✅ Yes         | Signed URL                     |
| `remove()`   | ✅ Yes         | Requires JWT                   |


## Upload

```typescript
const { publicUrl } = await blink.storage.upload(
  file,
  `uploads/${Date.now()}.${file.name.split('.').pop()}`,
  { onProgress: (percent) => console.log(`${percent}%`) }
)
```

SDK auto-detects file type from content and corrects the extension. Uploads are **add-only** — duplicate paths return `409`. Use timestamps or random IDs for uniqueness.

## Download

```typescript
const { downloadUrl, filename } = await blink.storage.download('images/photo.jpg')
window.open(downloadUrl, '_blank')

// Custom filename
const { downloadUrl } = await blink.storage.download('images/photo.jpg', {
  filename: 'my-photo.jpg',
})
```

## Remove

```typescript
await blink.storage.remove('uploads/file1.jpg')
await blink.storage.remove('file1.jpg', 'file2.jpg', 'file3.png')  // multiple
```

## Common Patterns

### Avatar Upload

```typescript
const handleAvatarUpload = async (file: File) => {
  const { publicUrl } = await blink.storage.upload(
    file,
    `avatars/${user.id}-${Date.now()}.${file.name.split('.').pop()}`
  )
  await blink.auth.updateMe({ avatar: publicUrl })
  return publicUrl
}
```

### Upload with Progress

```typescript
const [progress, setProgress] = useState(0)

const handleUpload = async (file: File) => {
  const { publicUrl } = await blink.storage.upload(
    file,
    `documents/${Date.now()}.${file.name.split('.').pop()}`,
    { onProgress: (percent) => setProgress(percent) }
  )
  return publicUrl
}
```

### For AI Image Input

Upload first to get an HTTPS URL with extension — required for AI vision.

```typescript
const { publicUrl } = await blink.storage.upload(photo, `photos/${Date.now()}.jpg`)
const { text } = await blink.ai.generateText({
  messages: [{ role: "user", content: [
    { type: "text", text: "What's in this image?" },
    { type: "image", image: publicUrl },
  ]}],
})
```

## URL Format

Uploaded files return direct HTTPS URLs: `https://storage.googleapis.com/{project}/uploads/{filename}.{ext}` — publicly accessible, HTTPS, with file extension.

## Common Errors


| Error          | Cause               | Fix                                                   |
| -------------- | ------------------- | ----------------------------------------------------- |
| 409 conflict   | Path already exists | Add timestamp/random ID                               |
| Invalid path   | Special characters  | Sanitize: `path.replace(/[^a-zA-Z0-9_\-\/\.]/g, '_')` |
| File too large | Exceeds limit       | Compress or split file                                |
规则

Rules for building apps on Blink infrastructure. Apply when using @blinkdotnew/sdk, blink CLI, or Blink MCP tools.

Rules for building apps on Blink infrastructure. Apply when using @blinkdotnew/sdk, blink CLI, or Blink MCP tools.

When building on Blink infrastructure:

- Initialize the SDK with both `projectId` and `publishableKey`:
  ```typescript
  import { createClient } from '@blinkdotnew/sdk'
  const blink = createClient({
    projectId: import.meta.env.VITE_BLINK_PROJECT_ID || 'your-project-id',
    publishableKey: import.meta.env.VITE_BLINK_PUBLISHABLE_KEY || 'blnk_pk_xxx',
    auth: { mode: 'managed' },
  })
  ```

- Use `blink.db.<tableName>` for database CRUD — never raw SQL from client code. The SDK auto-converts camelCase JS to snake_case SQL.

- Use `blink.auth` for authentication. Enable providers first: `blink auth-config set --provider google --enabled true`

- Backend must be a Hono server in `backend/index.ts` that exports default. Deploy with `blink backend deploy`.

- Build before deploying frontend: `npm run build && blink deploy ./dist --prod`

- Store secrets via `blink env set KEY value` — never hardcode API keys.

- SQLite booleans are integers: use `1`/`0`, not `true`/`false`. Filter with `{ isActive: 1 }`.

- All database IDs should be strings, generated with a prefix pattern like `usr_${generateId()}`.

- Queue tasks are delivered to `POST /api/queue` on the backend. Name tasks clearly: `send-welcome-email`, `process-image`.

来源:https://github.com/blink-new/blink-plugin