npm package: @metagptx/web-sdk
Description: TypeScript SDK for interacting with FuncSea API
Author: MetaGPTX
Latest version: 0.0.68

# FuncSea WebSDK

A TypeScript SDK for interacting with FuncSea API, providing modules for authentication, entity management, API calls, integrations, and a Vite plugin for automatic 404 page setup.

## Installation

```bash
npm install @metagptx/web-sdk
# or
pnpm install @metagptx/web-sdk
# or
yarn add @metagptx/web-sdk
```

## Quick Start

### Basic Usage

```typescript
import { createClient } from '@metagptx/web-sdk';

// Create client instance (no configuration needed)
const client = createClient();

// Use the client
const user = await client.auth.me();
```

### Using the Vite Plugin

If you're using Vite with React Router, you can use the included plugin to automatically add a 404 page:

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { vitePlugin404 } from '@metagptx/web-sdk';

export default defineConfig({
  plugins: [
    react(),
    vitePlugin404(), // Automatically adds 404 page to your routes
  ],
});
```

## Modules

The SDK provides eight main modules and a Vite plugin:

- **auth**: User authentication operations
- **entities**: Dynamic entity CRUD operations
- **apiCall**: Custom API calls
- **integrations**: Integration function invocations
- **frame**: Frame communication operations for iframe/parent window messaging
- **utils**: Utility functions for URL opening and window management
- **ai**: AI-powered text, image, video, and audio generation
- **storage**: Object storage operations (buckets, files, upload/download)
- **vitePlugin404**: Vite plugin for automatically adding a 404 page to React Router applications

## API Reference

### Auth Module

Handles user authentication and session management.

#### Authentication Callback Setup

When using this SDK, your project needs to implement an `/auth/callback` route and page. This callback page is used to handle the authentication flow after a successful login.

**Required Setup:**

1. Create an `/auth/callback` route in your application
2. In the callback page, read the `token` parameter from the URL query string
3. Call `client.auth.login()` to save the token to localStorage

**Example Implementation:**

```tsx
// In your /auth/callback page component
import { useEffect } from 'react';

function AuthCallbackPage() {
  useEffect(() => {
    // This will automatically read the token from URL query string
    // and save it to localStorage
    client.auth.login().then((token) => {
      if (token) {
        // Token saved successfully, redirect to your app
        window.location.href = '/dashboard'; // or your desired route
      } else {
        // No token found, handle error
        console.error('No token found in URL');
      }
    });
  }, []);

  return <div>Processing authentication...</div>;
}
```

**How it works:**
- After successful authentication, the user will be redirected to `/auth/callback?token=xxx`
- The `auth.login()` method reads the token from the URL and stores it in localStorage
- Once the token is saved, redirect the user to your application

#### `auth.login()`

Read the token from the URL query string and save it to localStorage.

**HTTP Details:**
- **Method:** Client-side only (no HTTP request)
- **Parameters:** None (automatically reads `token` from URL query string)

**Returns:** The token value if found, or null if not found.

**Example:**
```typescript
const token = await client.auth.login();
if (token) {
  console.log('Token saved to localStorage');
}
```

#### `auth.me()`

Get current user information.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/auth/me`
- **Parameters:** None

**Example:**
```typescript
const response = await client.auth.me();
console.log(response.data);
```

#### `auth.logout()`

Logout the current user.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/auth/logout`
- **Parameters:** None

**Example:**
```typescript
await client.auth.logout();
```

#### `auth.toLogin()`

Redirect to the login page. This is a utility function that redirects the browser to the login URL with the current page as the redirect parameter.

**Example:**
```typescript
client.auth.toLogin();
```

---

### Entities Module

Provides dynamic CRUD operations for any entity type. Access entities using property syntax: `client.entities.{entityName}.{operation}()`.

**Note:** The `fields` parameter (array) will be automatically converted to a comma-separated string, and the `query` parameter (object) will be automatically converted to a JSON string before sending to the API.

#### `entities[entityName].query(params?)`

Query entities with filtering, sorting, and pagination.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/entities/{entityName}`
- **Parameters:**
  - `query` (optional): Query conditions object (will be converted to JSON string)
  - `sort` (optional): Sort field and order (e.g., `"-createdAt"`)
  - `limit` (optional): Maximum number of results
  - `skip` (optional): Number of results to skip
  - `fields` (optional): Array of fields to return (will be converted to comma-separated string)

**Example:**
```typescript
const response = await client.entities.users.query({
  query: { status: 'active' },
  sort: '-createdAt',
  limit: 10,
  skip: 0,
  fields: ['id', 'name', 'email'],
});
// Actual API call: GET /api/v1/entities/users?query={"status":"active"}&sort=-createdAt&limit=10&skip=0&fields=id,name,email
```

#### `entities[entityName].queryAll(params?)`

Query all public entities with filtering, sorting, and pagination.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/entities/{entityName}/all`
- **Parameters:**
  - `query` (optional): Query conditions object (will be converted to JSON string)
  - `sort` (optional): Sort field and order (e.g., `"-createdAt"`)
  - `limit` (optional): Maximum number of results
  - `skip` (optional): Number of results to skip
  - `fields` (optional): Array of fields to return (will be converted to comma-separated string)

**Example:**
```typescript
const response = await client.entities.users.queryAll({
  query: { status: 'active' },
  sort: '-createdAt',
  limit: 10,
  skip: 0,
  fields: ['id', 'name', 'email'],
});
// Actual API call: GET /api/v1/entities/users/all?query={"status":"active"}&sort=-createdAt&limit=10&skip=0&fields=id,name,email
```

#### `entities[entityName].get(params)`

Get a single entity by ID.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/entities/{entityName}/{id}`
- **Parameters:**
  - `id` (required): Entity ID
  - `fields` (optional): Array of fields to return (will be converted to comma-separated string)

**Example:**
```typescript
const response = await client.entities.users.get({
  id: '12345',
  fields: ['id', 'name', 'email'],
});
// Actual API call: GET /api/v1/entities/users/12345?fields=id,name,email
```

#### `entities[entityName].create(params)`

Create a new entity.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/entities/{entityName}`
- **Body:**
  - `data` (required): Entity data object

**Example:**
```typescript
const response = await client.entities.users.create({
  data: {
    name: 'John Doe',
    email: 'john@example.com',
    status: 'active',
  },
});
```

#### `entities[entityName].update(params)`

Update an existing entity.

**HTTP Details:**
- **Method:** `PUT`
- **Path:** `/api/v1/entities/{entityName}/{id}`
- **Body:**
  - `id` (required): Entity ID
  - `data` (required): Updated entity data

**Example:**
```typescript
const response = await client.entities.users.update({
  id: '12345',
  data: {
    name: 'Jane Doe',
    status: 'inactive',
  },
});
```

#### `entities[entityName].delete(params)`

Delete a single entity by ID.

**HTTP Details:**
- **Method:** `DELETE`
- **Path:** `/api/v1/entities/{entityName}/{id}`
- **Parameters:**
  - `id` (required): Entity ID

**Example:**
```typescript
await client.entities.users.delete({ id: '12345' });
```

#### `entities[entityName].createBatch(params)`

Create multiple entities in a single request.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/entities/{entityName}/batch`
- **Body:**
  - `data` (required): Array of entity data objects

**Example:**
```typescript
const response = await client.entities.users.createBatch({
  data: [
    { name: 'John Doe', email: 'john@example.com' },
    { name: 'Jane Smith', email: 'jane@example.com' },
  ],
});
```

#### `entities[entityName].updateBatch(params)`

Update multiple entities in a single request.

**HTTP Details:**
- **Method:** `PUT`
- **Path:** `/api/v1/entities/{entityName}/batch`
- **Body:**
  - `data` (required): Array of entity data objects (must include `id` for each)

**Example:**
```typescript
const response = await client.entities.users.updateBatch({
  data: [
    { id: '12345', status: 'active' },
    { id: '67890', status: 'inactive' },
  ],
});
```

#### `entities[entityName].deleteBatch(params)`

Delete multiple entities by their IDs.

**HTTP Details:**
- **Method:** `DELETE`
- **Path:** `/api/v1/entities/{entityName}/batch`
- **Body:**
  - `ids` (required): Array of entity IDs

**Example:**
```typescript
await client.entities.users.deleteBatch({
  ids: ['12345', '67890', '11111'],
});
```

---

### API Call Module

Provides a flexible way to make custom API calls with any HTTP method.

#### `apiCall.invoke(params)`

Invoke a custom API call with specified method, URL, and data.

**HTTP Details:**
- **Method:** Configurable (GET, POST, PUT, PATCH, DELETE, etc.)
- **Path:** Configurable
- **Parameters:**
  - `url` (required): API endpoint path
  - `method` (optional): HTTP method (defaults to 'GET')
  - `data` (optional): Request data (body for POST/PUT/PATCH, query params for GET/DELETE)
  - `options` (optional): Additional axios request options

**Example:**
```typescript
// GET request with query parameters
const response = await client.apiCall.invoke({
  url: '/api/v1/custom/endpoint',
  method: 'GET',
  data: { filter: 'active' },
});

// POST request with object data
const response = await client.apiCall.invoke({
  url: '/api/v1/custom/action',
  method: 'POST',
  data: { key1: 'value1', key2: 'value2' },
  options: {
    headers: { 'X-Custom-Header': 'value' },
  },
});

// POST request with array data
const response = await client.apiCall.invoke({
  url: '/api/v1/custom/action',
  method: 'POST',
  data: [
    { key1: 'value1', key2: 'value2' },
    { key1: 'value3', key2: 'value4' }
  ],
  options: {
    headers: { 'X-Custom-Header': 'value' },
  },
});
```

#### Stream Response Type (Browser Only)

When making API calls in a browser environment with `responseType: 'stream'`, the SDK automatically uses the native `fetch` API instead of axios to handle streaming responses more efficiently. This avoids potential performance issues with axios interceptors when processing streaming data.

**Behavior:**
- **Browser environment** + `responseType: 'stream'`: Uses `fetch` API
- **Non-browser environment** or other `responseType` values: Uses axios (default behavior)

**Automatic Configuration:**
When using `fetch` for streaming:
- Automatically includes `Authorization` header from `localStorage.token` (if available)
- Automatically includes `Content-Type: application/json` header
- Automatically includes `App-Host` header with current origin
- Automatically handles `baseURL` from axios instance configuration
- Merges custom headers from `options.headers`
- Automatically serializes request body for POST/PUT/PATCH methods

**Return Value:**
- When using `fetch` (stream responseType in browser): Returns a native `Response` object
- When using axios (default): Returns an `AxiosResponse` object

**Example - Streaming Response:**
```typescript
// Stream response in browser - returns Response object
const response = await client.apiCall.invoke({
  url: '/api/v1/files/download',
  method: 'GET',
  options: {
    responseType: 'stream',
  },
});

// Access the ReadableStream from response.body
const reader = response.body?.getReader();
if (reader) {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    // Process chunk: value is a Uint8Array
    console.log('Received chunk:', value);
  }
}

// Or convert to blob
const blob = await response.blob();
const url = URL.createObjectURL(blob);
```

**Example - Regular Response (uses axios):**
```typescript
// Regular response - returns AxiosResponse object
const response = await client.apiCall.invoke({
  url: '/api/v1/custom/endpoint',
  method: 'GET',
  // responseType defaults to 'json' or undefined
});

// Access data via response.data (axios format)
console.log(response.data);
```

**Note:** In non-browser environments (e.g., Node.js), even with `responseType: 'stream'`, the SDK will use axios to maintain consistency across environments.

---

### Integrations Module

Provides dynamic access to integration functions. Access integrations using property syntax: `client.integrations.{packageName}.{functionName}(params)`.

#### `integrations.core[functionName](params?)`

Invoke a core integration function.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/integrations/core/{functionName}`
- **Body:**
  - `payload` (optional): Function payload data (object or FormData)
  - `option` (optional): Additional request options

**Example:**
```typescript
const response = await client.integrations.core.sendEmail({
  payload: {
    to: 'user@example.com',
    subject: 'Hello',
    body: 'Welcome!',
  },
});
```

#### `integrations[packageName][functionName](params?)`

Invoke a provider-specific integration function.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/integrations/providers/{packageName}/{functionName}`
- **Body:**
  - `payload` (optional): Function payload data (object or FormData)
  - `option` (optional): Additional request options

**Example:**
```typescript
// Regular JSON payload
const response = await client.integrations.stripe.createPayment({
  payload: {
    amount: 1000,
    currency: 'usd',
  },
});

// FormData payload for file uploads
const formData = new FormData();
formData.append('file', fileBlob);
formData.append('metadata', JSON.stringify({ key: 'value' }));

const response = await client.integrations.storage.uploadFile({
  payload: formData,
});
```

---

### Frame Module

Provides utilities for communicating between iframe and parent window using postMessage API.

#### `frame.createPage(path?)`

Create a page by sending a `'mgx-create-page'` message to the parent window.

**Parameters:**
- `path` (optional): Page path to create. If not provided, uses `window.location.pathname`

**Example:**
```typescript
// Create page with current path
client.frame.createPage();

// Create page with specific path
client.frame.createPage('/custom/path');
```

---

### Utils Module

Provides utility functions for navigating to URLs.

#### `utils.openUrl(url?)`

Navigate to the given URL in the current window. For certain domains (like stripe.com) when running in an MGX iframe, this will send a postMessage to the parent window instead of navigating directly.

**Parameters:**
- `url` (optional): The URL to navigate to (string or URL object)

**Behavior:**
- If `url` is not provided or is undefined, the function returns without doing anything
- For URLs containing `stripe.com` when running in an MGX iframe (detected by `window.name` containing `'devIframe'`), the function sends a `'mgx-open-url'` postMessage to the parent window instead of navigating
- For other URLs or when not in an MGX iframe, the function navigates to the URL by setting `window.location.href`

**Returns:** `undefined`

**Example:**
```typescript
// Navigate to a regular URL
client.utils.openUrl('https://example.com');

// Navigate using a URL object
const url = new URL('https://example.com/page');
client.utils.openUrl(url);

// Stripe URLs in MGX iframe will trigger postMessage instead
client.utils.openUrl('https://stripe.com/checkout'); // Sends postMessage to parent when in MGX iframe

// Stripe URLs outside MGX iframe will navigate normally
client.utils.openUrl('https://stripe.com/checkout'); // Navigates to URL when not in MGX iframe
```

---

### AI Module

Provides AI-powered text, image, video, and audio generation capabilities with support for streaming responses, multimodal inputs, image editing, and text-to-speech.

#### `ai.gentxt(params)`

Generate text using AI models. Supports both streaming and non-streaming modes.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/aihub/gentxt`
- **Parameters:**
  - `messages` (required): Array of chat messages with roles (`system`, `user`, `assistant`)
  - `model` (required): Model identifier (e.g., `'deepseek-v3.2'`)
  - `stream` (required): Boolean to enable/disable streaming
  - `timeout` (optional): Request timeout in milliseconds
  - `onChunk` (optional, streaming only): Callback for each content chunk
  - `onComplete` (optional, streaming only): Callback when streaming completes
  - `onError` (optional, streaming only): Callback when an error occurs

**Example - Streaming mode (recommended):**
```typescript
const result = await client.ai.gentxt({
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Hello!' }
  ],
  model: 'deepseek-v3.2',
  stream: true,
  onChunk: (chunk) => {
    console.log('Received:', chunk.content);
  },
  onComplete: (result) => {
    console.log('Complete:', result.content);
  },
  onError: (error) => {
    console.error('Error:', error.message);
  }
});
console.log('Final result:', result.content);
```

**Example - Non-streaming mode:**
```typescript
const response = await client.ai.gentxt({
  messages: [{ role: 'user', content: 'Hello!' }],
  model: 'deepseek-v3.2',
  stream: false
});
console.log(response.data.content);
```

**Example - Multimodal (image + text):**
```typescript
const result = await client.ai.gentxt({
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'What is in this image?' },
      { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
    ]
  }],
  model: 'gpt-4-vision',
  stream: true,
  onChunk: (chunk) => console.log(chunk.content)
});
```

**Example - Base64 image input:**
```typescript
const result = await client.ai.gentxt({
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Describe this image' },
      { type: 'image_url', image_url: { url: 'data:image/png;base64,iVBORw0KGgo...' } }
    ]
  }],
  model: 'gpt-4-vision',
  stream: true
});
```

#### `ai.genimg(params, options?)`

Generate images using AI models. Supports text-to-image and image editing (img2img).

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/aihub/genimg`
- **Parameters:**
  - `prompt` (required): Text prompt describing the desired image
  - `model` (required): Model identifier for image generation
  - `size` (optional): Image size (default: `"1024x1024"`)
  - `quality` (optional): Image quality (default: `"standard"`), ignored for img2img
  - `n` (optional): Number of images to generate (default: `1`)
  - `image` (optional): Base64 Data URI image(s) for image editing
- **Options:**
  - `timeout` (optional): Request timeout in milliseconds (default: 600000ms / 10 minutes)

**Example - Text-to-image:**
```typescript
const response = await client.ai.genimg({
  prompt: 'A beautiful sunset over the ocean',
  model: 'dall-e-3',
  size: '1024x1024',
  quality: 'standard',
  n: 1
}, { timeout: 600_000 });

console.log(response.data.images[0]); // URL or base64
```

**Example - Image editing (single image):**
```typescript
const response = await client.ai.genimg({
  prompt: 'Add a rainbow to the sky',
  model: 'dall-e-2',
  image: 'data:image/png;base64,...'
}, { timeout: 600_000 });
```

**Example - Multi-image editing (e.g., style transfer):**
```typescript
const response = await client.ai.genimg({
  prompt: 'Apply the style to the content image',
  model: 'stable-diffusion',
  image: [contentImageBase64, styleImageBase64]
}, { timeout: 600_000 });
```

#### `ai.genvideo(params, options?)`

Generate videos using AI models. Supports text-to-video and image-to-video (using an image as the first frame). Video generation is async — the API polls internally until completion.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/aihub/genvideo`
- **Parameters:**
  - `prompt` (required): Text prompt describing the desired video
  - `model` (required): Model identifier (e.g., `'wan2.6-t2v'` for text-to-video, `'wan2.6-i2v'` for image-to-video)
  - `size` (optional): Video size (default: `"1280x720"`)
  - `seconds` (optional): Video duration in seconds (default: `"8"`)
  - `image` (optional): Base64 Data URI image as the first frame reference (for image-to-video)
- **Options:**
  - `timeout` (optional): Request timeout in milliseconds (default: 600000ms / 10 minutes). Video generation is slow; consider setting a longer timeout (e.g., `600_000` ms or more)

**Response:** `response.data.url` is the CDN URL of the generated video.

**Example - Text-to-Video:**
```typescript
const video = await client.ai.genvideo(
  { prompt: 'Ocean waves at sunset', model: 'wan2.6-t2v' },
  { timeout: 600_000 }
);
const videoUrl = video.data.url;
```

**Example - Image-to-Video (use image as first frame):**
```typescript
const videoFromImage = await client.ai.genvideo(
  { prompt: 'Animate the scene', model: 'wan2.6-i2v', image: 'data:image/png;base64,...' },
  { timeout: 600_000 }
);
const videoUrl = videoFromImage.data.url;
```

#### `ai.genaudio(params, options?)`

Generate audio (text-to-speech) using AI models. Voice is auto-selected based on model and gender — no manual voice selection needed.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/aihub/genaudio`
- **Parameters:**
  - `text` (required): Text content to convert to speech
  - `model` (required): Model identifier (e.g., `'qwen3-tts-flash'`, `'eleven-v3-alpha'`)
  - `gender` (optional): Voice gender — `"male"` or `"female"` (default: `"female"`)
- **Options:**
  - `timeout` (optional): Request timeout in milliseconds (default: 60000ms / 1 minute)

**Response:** `response.data.url` is the CDN URL of the generated audio (mp3).

**Example - Female voice (default):**
```typescript
const audio = await client.ai.genaudio(
  { text: 'Welcome to our website', model: 'qwen3-tts-flash', gender: 'female' },
  { timeout: 60_000 }
);
const audioUrl = audio.data.url;
```

**Example - Male voice:**
```typescript
const maleAudio = await client.ai.genaudio(
  { text: 'Product introduction', model: 'eleven-v3-alpha', gender: 'male' },
  { timeout: 60_000 }
);
const audioUrl = maleAudio.data.url;
```

---

### Storage Module

Provides object storage operations for managing buckets and files with presigned URL support for secure uploads and downloads.

#### `storage.createBucket(params)`

Create a new storage bucket.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/storage/create-bucket`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket to create
  - `visibility` (required): Bucket visibility (`'public'` or `'private'`)

**Example:**
```typescript
const response = await client.storage.createBucket({
  bucket_name: 'my-bucket',
  visibility: 'public'
});
console.log(response.data.created_at); // "2024-01-15 10:30:00"
```

#### `storage.listBuckets()`

List all buckets for the current user.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/storage/list-buckets`

**Example:**
```typescript
const response = await client.storage.listBuckets();
response.data.buckets.forEach(bucket => {
  console.log(bucket.bucket_name, bucket.visibility);
});
```

#### `storage.listObjects(params)`

List objects in a bucket.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/storage/list-objects`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket

**Example:**
```typescript
const response = await client.storage.listObjects({
  bucket_name: 'my-bucket'
});
response.data.objects.forEach(obj => {
  console.log(obj.object_key, obj.size, obj.last_modified);
});
```

#### `storage.getObjectInfo(params)`

Get metadata for a specific object.

**HTTP Details:**
- **Method:** `GET`
- **Path:** `/api/v1/storage/get-object-info`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket
  - `object_key` (required): The object key (path)

**Example:**
```typescript
const response = await client.storage.getObjectInfo({
  bucket_name: 'my-bucket',
  object_key: 'images/photo.jpg'
});
console.log(response.data.size, response.data.etag);
```

#### `storage.renameObject(params)`

Rename an object within a bucket.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/storage/rename-object`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket
  - `source_key` (required): The current object key
  - `target_key` (required): The new object key
  - `overwrite_key` (required): Whether to overwrite if target exists

**Example:**
```typescript
const response = await client.storage.renameObject({
  bucket_name: 'my-bucket',
  source_key: 'old-name.jpg',
  target_key: 'new-name.jpg',
  overwrite_key: false
});
console.log(response.data.success); // true
```

#### `storage.getUploadUrl(params)`

Get a presigned URL for uploading a file.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/storage/upload-url`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket
  - `object_key` (required): The object key (path) to upload as

**Example:**
```typescript
const response = await client.storage.getUploadUrl({
  bucket_name: 'my-bucket',
  object_key: 'uploads/file.pdf'
});
console.log(response.data.upload_url, response.data.expires_at);
```

#### `storage.getDownloadUrl(params)`

Get a presigned URL for downloading a file.

**HTTP Details:**
- **Method:** `POST`
- **Path:** `/api/v1/storage/download-url`
- **Parameters:**
  - `bucket_name` (required): The name of the bucket
  - `object_key` (required): The object key (path) to download

**Example:**
```typescript
const response = await client.storage.getDownloadUrl({
  bucket_name: 'my-bucket',
  object_key: 'downloads/file.pdf'
});
console.log(response.data.download_url, response.data.expires_at);
```

#### `storage.upload(params)`

High-level method to upload a file. Automatically handles presigned URL generation and file upload. If no file is provided, opens a browser file picker dialog.

**Parameters:**
- `bucket_name` (required): The name of the bucket to upload to
- `object_key` (optional): The object key (path) to upload as. Defaults to the file name
- `file` (optional): The File object to upload. If not provided, opens a file picker
- `accept` (optional): File input accept attribute for file picker (e.g., `'image/*'`, `'.pdf,.doc'`)

**Returns:** `UploadResult` with `bucket_name`, `object_key`, `size`, and `file_name`

**Example - With file picker (no file provided):**
```typescript
// Opens browser file picker dialog
const result = await client.storage.upload({
  bucket_name: 'my-bucket',
  accept: 'image/*'  // Only allow images
});
console.log('Uploaded:', result.object_key, result.size);
```

**Example - With specific file:**
```typescript
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const result = await client.storage.upload({
  bucket_name: 'my-bucket',
  object_key: 'custom/path/image.jpg',  // Optional custom path
  file
});
console.log('Uploaded:', result.object_key, result.file_name);
```

**Example - With File from drag-and-drop:**
```typescript
const handleDrop = async (event) => {
  event.preventDefault();
  const file = event.dataTransfer.files[0];
  
  const result = await client.storage.upload({
    bucket_name: 'my-bucket',
    file
  });
  console.log('Uploaded:', result.object_key);
};
```

#### `storage.download(params)`

High-level method to download a file. Gets a presigned download URL and triggers a browser download.

**Parameters:**
- `bucket_name` (required): The name of the bucket
- `object_key` (required): The object key (path) to download

**Returns:** The download URL (string)

**Example:**
```typescript
// Triggers browser download dialog
const downloadUrl = await client.storage.download({
  bucket_name: 'my-bucket',
  object_key: 'files/document.pdf'
});
```

---

## Configuration

### Client Configuration Options

In most cases, you don't need to pass any configuration:

```typescript
// Default usage (recommended)
const client = createClient();
```

However, you can optionally customize the client behavior for advanced use cases:

```typescript
const client = createClient({
  baseURL: '/custom-api',              // Custom base URL (defaults to '/')
  timeout: 30000,                      // Request timeout in milliseconds (defaults to 60000)
  headers: {                           // Custom headers
    'X-Custom-Header': 'value',
  },
  // Any other axios configuration options
});
```

**Note:** The `baseURL` and `timeout` options are reserved for future use or special deployment scenarios. Most users should rely on the default values.

### Automatic 401 Handling

The SDK automatically handles 401 (Unauthorized) responses by redirecting to the login page. This behavior is built-in and requires no additional configuration.

---

## Vite Plugin

The SDK provides a Vite plugin for automatically adding a 404 page to your React Router application.

### `vitePlugin404()`

A Vite plugin that automatically creates a virtual 404 React component and adds it to your routing configuration.

**Features:**
- Automatically creates a beautiful 404 page component
- Automatically adds a wildcard route (`path="*"`) to your `App.tsx`
- Detects if already configured (won't duplicate routes)
- Works seamlessly with React Router
- Provides "Create page" functionality when running in MGX iframe

**Setup:**

1. Import the plugin in your `vite.config.ts`:

```typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { vitePlugin404 } from '@metagptx/web-sdk';

export default defineConfig({
  plugins: [
    react(),
    vitePlugin404(), // Add the plugin
  ],
});
```

2. The plugin will automatically:
   - Create a virtual `NotFoundPage` component
   - Add the import statement to your `App.tsx`
   - Add a `<Route path="*" element={<NotFoundPage />} />` route

**Requirements:**

- Your project must have an `App.tsx` file (or any file containing `App.tsx` in the path)
- Your project must use React Router with `<Routes>` component
- Your project should have `@/components/ui/button` component (or adjust the import in the plugin code)

**How it works:**

The plugin uses multiple strategies to intelligently add the 404 route:

1. **Import placement strategies:**
   - After `AuthError` import (if exists)
   - After `MODULE_IMPORTS_END` comment (if exists)
   - Before `MODULE_IMPORTS_START` comment (if exists)
   - After the last import statement (fallback)

2. **Route placement strategies:**
   - Before `MODULE_ROUTES_END` comment (if exists)
   - Before closing `</Routes>` tag
   - After the last `<Route>` tag (fallback)

**Example - Before plugin:**

```tsx
// App.tsx
import { Route, Routes } from 'react-router-dom';

export default function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
}
```

**Example - After plugin (automatically transformed):**

```tsx
// App.tsx
import { Route, Routes } from 'react-router-dom';
import NotFoundPage from 'virtual:404-page';

export default function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="*" element={<NotFoundPage />} />
    </Routes>
  );
}
```

**404 Page Features:**

The generated 404 page includes:
- Modern, responsive design with gradient background
- "Return Home" button
- "Create page" button (when running in MGX iframe) - uses `client.frame.createPage()`
- "Go to MGX" button (when not in iframe)

**Customization:**

If you need to customize the 404 page, you can:
1. Create your own 404 component
2. Import it manually in `App.tsx`
3. The plugin will detect existing 404 routes and skip automatic addition

**Note:** The plugin only processes files that contain `App.tsx` in their path and end with `.tsx`. It will skip files that already have a 404 route configured.

---

## TypeScript Support

The SDK is written in TypeScript and provides full type definitions. You can use generic types for typed responses:

```typescript
interface User {
  id: string;
  name: string;
  email: string;
}

const response = await client.entities.users.get<User>({ id: '12345' });
const user: User = response.data;
```

---

## Error Handling

All API calls return promises that may reject with an error. Always handle errors appropriately:

```typescript
try {
  const response = await client.entities.users.get({ id: '12345' });
  console.log(response.data);
} catch (error) {
  if (error.response) {
    // Server responded with error status
    console.error('Error:', error.response.status, error.response.data);
  } else if (error.request) {
    // Request made but no response received
    console.error('No response received:', error.request);
  } else {
    // Error setting up the request
    console.error('Error:', error.message);
  }
}
```

---

## Exports

The SDK exports the following:

### Main Exports

```typescript
import { createClient, vitePlugin404 } from '@metagptx/web-sdk';
```

- **`createClient(config?)`**: Creates a new SDK client instance
- **`vitePlugin404()`**: Vite plugin for automatic 404 page setup

### Type Exports

```typescript
import type {
  // AI types
  AiOptions,
  // Client types
  AnyType,
  ApiCallParams,
  // Storage types
  BucketInfo,
  BucketVisibility,
  ChatMessage,
  ClientConfig,
  CreateBucketParams,
  CreateBucketResponse,
  DownloadParams,
  GenAudioParams,
  GenAudioResponse,
  GenImgParams,
  GenImgResponse,
  GenTxtNonStreamParams,
  GenTxtParams,
  GenTxtResponse,
  GenTxtStreamParams,
  GenVideoParams,
  GenVideoResponse,
  GetDownloadUrlParams,
  GetDownloadUrlResponse,
  GetObjectInfoParams,
  GetObjectInfoResponse,
  GetUploadUrlParams,
  GetUploadUrlResponse,
  ImageContent,
  IntegrationFunction,
  IntegrationParams,
  ListBucketsResponse,
  ListObjectsParams,
  ListObjectsResponse,
  MessageContent,
  ObjectInfo,
  RenameObjectParams,
  RenameObjectResponse,
  RequestConfig,
  StreamChunk,
  StreamResult,
  TextContent,
  UploadParams,
  UploadResult,
} from '@metagptx/web-sdk';
```

- **`ClientConfig`**: Configuration options for `createClient()`
- **`ApiCallParams`**: Parameters for `apiCall.invoke()`
- **`IntegrationFunction`**: Type for integration functions
- **`IntegrationParams`**: Parameters for integration function calls
- **`RequestConfig`**: Axios request configuration options
- **`AnyType`**: Generic type utility

**AI Types:**
- **`ChatMessage`**: Message format with role and content
- **`GenTxtParams`**: Union type for text generation parameters
- **`GenTxtStreamParams`**: Streaming text generation parameters
- **`GenTxtNonStreamParams`**: Non-streaming text generation parameters
- **`GenTxtResponse`**: Text generation response
- **`GenImgParams`**: Image generation parameters
- **`GenImgResponse`**: Image generation response
- **`GenVideoParams`**: Video generation parameters
- **`GenVideoResponse`**: Video generation response (CDN URL)
- **`GenAudioParams`**: Audio generation (TTS) parameters
- **`GenAudioResponse`**: Audio generation response (CDN URL)
- **`StreamChunk`**: Chunk received during streaming
- **`StreamResult`**: Complete streaming result
- **`ImageContent`**: Image content for multimodal messages
- **`TextContent`**: Text content for multimodal messages
- **`MessageContent`**: Union type for message content
- **`AiOptions`**: Options for AI methods

**Storage Types:**
- **`BucketInfo`**: Bucket information
- **`BucketVisibility`**: `'public'` or `'private'`
- **`CreateBucketParams`**: Parameters for creating a bucket
- **`CreateBucketResponse`**: Response from bucket creation
- **`ListBucketsResponse`**: Response from listing buckets
- **`ListObjectsParams`**: Parameters for listing objects
- **`ListObjectsResponse`**: Response from listing objects
- **`ObjectInfo`**: Object metadata
- **`GetObjectInfoParams`**: Parameters for getting object info
- **`GetObjectInfoResponse`**: Response with object metadata
- **`RenameObjectParams`**: Parameters for renaming an object
- **`RenameObjectResponse`**: Response from rename operation
- **`GetUploadUrlParams`**: Parameters for getting upload URL
- **`GetUploadUrlResponse`**: Response with presigned upload URL
- **`GetDownloadUrlParams`**: Parameters for getting download URL
- **`GetDownloadUrlResponse`**: Response with presigned download URL
- **`UploadParams`**: Parameters for high-level upload
- **`UploadResult`**: Result from upload operation
- **`DownloadParams`**: Parameters for high-level download