mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-11 16:08:04 -05:00
Compare commits
3 Commits
improvemen
...
v0.3.21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85cdca28f1 | ||
|
|
9f2ff7e9cd | ||
|
|
aeef2b7e2b |
@@ -77,7 +77,7 @@ services:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=simstudio
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
|
||||
9
.github/CONTRIBUTING.md
vendored
9
.github/CONTRIBUTING.md
vendored
@@ -416,8 +416,8 @@ In addition, you will need to update the registries:
|
||||
Your tool should export a constant with a naming convention of `{toolName}Tool`. The tool ID should follow the format `{provider}_{tool_name}`. For example:
|
||||
|
||||
```typescript:/apps/sim/tools/pinecone/fetch.ts
|
||||
import { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import { PineconeParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
import { ToolConfig, ToolResponse } from '../types'
|
||||
import { PineconeParams, PineconeResponse } from './types'
|
||||
|
||||
export const fetchTool: ToolConfig<PineconeParams, PineconeResponse> = {
|
||||
id: 'pinecone_fetch', // Follow the {provider}_{tool_name} format
|
||||
@@ -448,6 +448,9 @@ In addition, you will need to update the registries:
|
||||
transformResponse: async (response: Response) => {
|
||||
// Transform response
|
||||
},
|
||||
transformError: (error) => {
|
||||
// Handle errors
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -455,7 +458,7 @@ In addition, you will need to update the registries:
|
||||
Update the tools registry in `/apps/sim/tools/index.ts` to include your new tool:
|
||||
|
||||
```typescript:/apps/sim/tools/index.ts
|
||||
import { fetchTool, generateEmbeddingsTool, searchTextTool } from '/@tools/pinecone'
|
||||
import { fetchTool, generateEmbeddingsTool, searchTextTool } from './pinecone'
|
||||
// ... other imports
|
||||
|
||||
export const tools: Record<string, ToolConfig> = {
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -85,8 +85,8 @@ jobs:
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha,scope=build-v3
|
||||
cache-to: type=gha,mode=max,scope=build-v3
|
||||
cache-from: type=gha,scope=build-v2
|
||||
cache-to: type=gha,mode=max,scope=build-v2
|
||||
provenance: false
|
||||
sbom: false
|
||||
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
node-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
run: bun install
|
||||
|
||||
- name: Run tests with coverage
|
||||
env:
|
||||
|
||||
44
.github/workflows/trigger-deploy.yml
vendored
44
.github/workflows/trigger-deploy.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: Trigger.dev Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- staging
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Trigger.dev Deploy
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: trigger-deploy-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
env:
|
||||
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Deploy to Staging
|
||||
if: github.ref == 'refs/heads/staging'
|
||||
working-directory: ./apps/sim
|
||||
run: npx --yes trigger.dev@4.0.1 deploy -e staging
|
||||
|
||||
- name: Deploy to Production
|
||||
if: github.ref == 'refs/heads/main'
|
||||
working-directory: ./apps/sim
|
||||
run: npx --yes trigger.dev@4.0.1 deploy
|
||||
|
||||
57
README.md
57
README.md
@@ -1,46 +1,50 @@
|
||||
<p align="center">
|
||||
<a href="https://sim.ai" target="_blank" rel="noopener noreferrer">
|
||||
<img src="apps/sim/public/logo/reverse/text/large.png" alt="Sim Logo" width="500"/>
|
||||
</a>
|
||||
<img src="apps/sim/public/static/sim.png" alt="Sim Logo" width="500"/>
|
||||
</p>
|
||||
|
||||
<p align="center">Build and deploy AI agent workflows in minutes.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License: Apache-2.0"></a>
|
||||
<a href="https://discord.gg/Hr4UWYEcTT"><img src="https://img.shields.io/badge/Discord-Join%20Server-7289DA?logo=discord&logoColor=white" alt="Discord"></a>
|
||||
<a href="https://x.com/simdotai"><img src="https://img.shields.io/twitter/follow/simstudioai?style=social" alt="Twitter"></a>
|
||||
<a href="https://github.com/simstudioai/sim/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome"></a>
|
||||
<a href="https://docs.sim.ai"><img src="https://img.shields.io/badge/Docs-visit%20documentation-blue.svg" alt="Documentation"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/sim.ai-6F3DFA" alt="Sim.ai"></a>
|
||||
<a href="https://discord.gg/Hr4UWYEcTT" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
|
||||
<a href="https://x.com/simdotai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/twitter/follow/simstudioai?style=social" alt="Twitter"></a>
|
||||
<a href="https://docs.sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Docs-6F3DFA.svg" alt="Documentation"></a>
|
||||
<strong>Sim</strong> is a lightweight, user-friendly platform for building AI agent workflows.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="apps/sim/public/static/demo.gif" alt="Sim Demo" width="800"/>
|
||||
</p>
|
||||
|
||||
## Quickstart
|
||||
## Getting Started
|
||||
|
||||
### Cloud-hosted: [sim.ai](https://sim.ai)
|
||||
1. Use our [cloud-hosted version](https://sim.ai)
|
||||
2. Self-host using one of the methods below
|
||||
|
||||
<a href="https://sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/sim.ai-6F3DFA?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iNjE2IiBoZWlnaHQ9IjYxNiIgdmlld0JveD0iMCAwIDYxNiA2MTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTU5XzMxMykiPgo8cGF0aCBkPSJNNjE2IDBIMFY2MTZINjE2VjBaIiBmaWxsPSIjNkYzREZBIi8+CjxwYXRoIGQ9Ik04MyAzNjUuNTY3SDExM0MxMTMgMzczLjgwNSAxMTYgMzgwLjM3MyAxMjIgMzg1LjI3MkMxMjggMzg5Ljk0OCAxMzYuMTExIDM5Mi4yODUgMTQ2LjMzMyAzOTIuMjg1QzE1Ny40NDQgMzkyLjI4NSAxNjYgMzkwLjE3MSAxNzIgMzg1LjkzOUMxNzcuOTk5IDM4MS40ODcgMTgxIDM3NS41ODYgMTgxIDM2OC4yMzlDMTgxIDM2Mi44OTUgMTc5LjMzMyAzNTguNDQyIDE3NiAzNTQuODhDMTcyLjg4OSAzNTEuMzE4IDE2Ny4xMTEgMzQ4LjQyMiAxNTguNjY3IDM0Ni4xOTZMMTMwIDMzOS41MTdDMTE1LjU1NSAzMzUuOTU1IDEwNC43NzggMzMwLjQ5OSA5Ny42NjY1IDMyMy4xNTFDOTAuNzc3NSAzMTUuODA0IDg3LjMzMzQgMzA2LjExOSA4Ny4zMzM0IDI5NC4wOTZDODcuMzMzNCAyODQuMDc2IDg5Ljg4OSAyNzUuMzkyIDk0Ljk5OTYgMjY4LjA0NUMxMDAuMzMzIDI2MC42OTcgMTA3LjU1NSAyNTUuMDIgMTE2LjY2NiAyNTEuMDEyQzEyNiAyNDcuMDA0IDEzNi42NjcgMjQ1IDE0OC42NjYgMjQ1QzE2MC42NjcgMjQ1IDE3MSAyNDcuMTE2IDE3OS42NjcgMjUxLjM0NkMxODguNTU1IDI1NS41NzYgMTk1LjQ0NCAyNjEuNDc3IDIwMC4zMzMgMjY5LjA0N0MyMDUuNDQ0IDI3Ni42MTcgMjA4LjExMSAyODUuNjM0IDIwOC4zMzMgMjk2LjA5OUgxNzguMzMzQzE3OC4xMTEgMjg3LjYzOCAxNzUuMzMzIDI4MS4wNyAxNjkuOTk5IDI3Ni4zOTRDMTY0LjY2NiAyNzEuNzE5IDE1Ny4yMjIgMjY5LjM4MSAxNDcuNjY3IDI2OS4zODFDMTM3Ljg4OSAyNjkuMzgxIDEzMC4zMzMgMjcxLjQ5NiAxMjUgMjc1LjcyNkMxMTkuNjY2IDI3OS45NTcgMTE3IDI4NS43NDYgMTE3IDI5My4wOTNDMTE3IDMwNC4wMDMgMTI1IDMxMS40NjIgMTQxIDMxNS40N0wxNjkuNjY3IDMyMi40ODNDMTgzLjQ0NSAzMjUuNiAxOTMuNzc4IDMzMC43MjIgMjAwLjY2NyAzMzcuODQ3QzIwNy41NTUgMzQ0Ljc0OSAyMTEgMzU0LjIxMiAyMTEgMzY2LjIzNUMyMTEgMzc2LjQ3NyAyMDguMjIyIDM4NS40OTQgMjAyLjY2NiAzOTMuMjg3QzE5Ny4xMTEgNDAwLjg1NyAxODkuNDQ0IDQwNi43NTggMTc5LjY2NyA0MTAuOTg5QzE3MC4xMTEgNDE0Ljk5NiAxNTguNzc4IDQxNyAxNDUuNjY3IDQxN0MxMjYuNTU1IDQxNyAxMTEuMzMzIDQxMi4zMjUgOTkuOTk5NyA0MDIuOTczQzg4LjY2NjggMzkzLjYyMSA4MyAzODEuMTUzIDgzIDM2NS41NjdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjMyLjI5MSA0MTNWMjUwLjA4MkMyNDQuNjg0IDI1NC42MTQgMjUwLjE0OCAyNTQuNjE0IDI2My4zNzEgMjUwLjA4MlY0MTNIMjMyLjI5MVpNMjQ3LjUgMjM5LjMxM0MyNDEuOTkgMjM5LjMxMyAyMzcuMTQgMjM3LjMxMyAyMzIuOTUyIDIzMy4zMTZDMjI4Ljk4NCAyMjkuMDk1IDIyNyAyMjQuMjA5IDIyNyAyMTguNjU2QzIyNyAyMTIuODgyIDIyOC45ODQgMjA3Ljk5NSAyMzIuOTUyIDIwMy45OTdDMjM3LjE0IDE5OS45OTkgMjQxLjk5IDE5OCAyNDcuNSAxOThDMjUzLjIzMSAxOTggMjU4LjA4IDE5OS45OTkgMjYyLjA0OSAyMDMuOTk3QzI2Ni4wMTYgMjA3Ljk5NSAyNjggMjEyLjg4MiAyNjggMjE4LjY1NkMyNjggMjI0LjIwOSAyNjYuMDE2IDIyOS4wOTUgMjYyLjA0OSAyMzMuMzE2QzI1OC4wOCAyMzcuMzEzIDI1My4yMzEgMjM5LjMxMyAyNDcuNSAyMzkuMzEzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTMxOS4zMzMgNDEzSDI4OFYyNDkuNjc2SDMxNlYyNzcuMjMzQzMxOS4zMzMgMjY4LjEwNCAzMjUuNzc4IDI2MC4zNjQgMzM0LjY2NyAyNTQuMzUyQzM0My43NzggMjQ4LjExNyAzNTQuNzc4IDI0NSAzNjcuNjY3IDI0NUMzODIuMTExIDI0NSAzOTQuMTEyIDI0OC44OTcgNDAzLjY2NyAyNTYuNjlDNDEzLjIyMiAyNjQuNDg0IDQxOS40NDQgMjc0LjgzNyA0MjIuMzM0IDI4Ny43NTJINDE2LjY2N0M0MTguODg5IDI3NC44MzcgNDI1IDI2NC40ODQgNDM1IDI1Ni42OUM0NDUgMjQ4Ljg5NyA0NTcuMzM0IDI0NSA0NzIgMjQ1QzQ5MC42NjYgMjQ1IDUwNS4zMzQgMjUwLjQ1NSA1MTYgMjYxLjM2NkM1MjYuNjY3IDI3Mi4yNzYgNTMyIDI4Ny4xOTUgNTMyIDMwNi4xMjFWNDEzSDUwMS4zMzNWMzEzLjgwNEM1MDEuMzMzIDMwMC44ODkgNDk4IDI5MC45ODEgNDkxLjMzMyAyODQuMDc4QzQ4NC44ODkgMjc2Ljk1MiA0NzYuMTExIDI3My4zOSA0NjUgMjczLjM5QzQ1Ny4yMjIgMjczLjM5IDQ1MC4zMzMgMjc1LjE3MSA0NDQuMzM0IDI3OC43MzRDNDM4LjU1NiAyODIuMDc0IDQzNCAyODYuOTcyIDQzMC42NjcgMjkzLjQzQzQyNy4zMzMgMjk5Ljg4NyA0MjUuNjY3IDMwNy40NTcgNDI1LjY2NyAzMTYuMTQxVjQxM0gzOTQuNjY3VjMxMy40NjlDMzk0LjY2NyAzMDAuNTU1IDM5MS40NDUgMjkwLjc1OCAzODUgMjg0LjA3OEMzNzguNTU2IDI3Ny4xNzUgMzY5Ljc3OCAyNzMuNzI0IDM1OC42NjcgMjczLjcyNEMzNTAuODg5IDI3My43MjQgMzQ0IDI3NS41MDUgMzM4IDI3OS4wNjhDMzMyLjIyMiAyODIuNDA4IDMyNy42NjcgMjg3LjMwNyAzMjQuMzMzIDI5My43NjNDMzIxIDI5OS45OTggMzE5LjMzMyAzMDcuNDU3IDMxOS4zMzMgMzE2LjE0MVY0MTNaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzExNTlfMzEzIj4KPHJlY3Qgd2lkdGg9IjYxNiIgaGVpZ2h0PSI2MTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==&logoColor=white" alt="Sim.ai"></a>
|
||||
## Self-Hosting Options
|
||||
|
||||
### Self-hosted: NPM Package
|
||||
### Option 1: NPM Package (Simplest)
|
||||
|
||||
The easiest way to run Sim locally is using our [NPM package](https://www.npmjs.com/package/simstudio?activeTab=readme):
|
||||
|
||||
```bash
|
||||
npx simstudio
|
||||
```
|
||||
→ http://localhost:3000
|
||||
|
||||
#### Note
|
||||
Docker must be installed and running on your machine.
|
||||
After running these commands, open [http://localhost:3000/](http://localhost:3000/) in your browser.
|
||||
|
||||
#### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-p, --port <port>` | Port to run Sim on (default `3000`) |
|
||||
| `--no-pull` | Skip pulling latest Docker images |
|
||||
- `-p, --port <port>`: Specify the port to run Sim on (default: 3000)
|
||||
- `--no-pull`: Skip pulling the latest Docker images
|
||||
|
||||
### Self-hosted: Docker Compose
|
||||
#### Requirements
|
||||
|
||||
- Docker must be installed and running on your machine
|
||||
|
||||
### Option 2: Docker Compose
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
@@ -72,14 +76,14 @@ Wait for the model to download, then visit [http://localhost:3000](http://localh
|
||||
docker compose -f docker-compose.ollama.yml exec ollama ollama pull llama3.1:8b
|
||||
```
|
||||
|
||||
### Self-hosted: Dev Containers
|
||||
### Option 3: Dev Containers
|
||||
|
||||
1. Open VS Code with the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
2. Open the project and click "Reopen in Container" when prompted
|
||||
3. Run `bun run dev:full` in the terminal or use the `sim-start` alias
|
||||
- This starts both the main application and the realtime socket server
|
||||
|
||||
### Self-hosted: Manual Setup
|
||||
### Option 4: Manual Setup
|
||||
|
||||
**Requirements:**
|
||||
- [Bun](https://bun.sh/) runtime
|
||||
@@ -154,13 +158,6 @@ cd apps/sim
|
||||
bun run dev:sockets
|
||||
```
|
||||
|
||||
## Copilot API Keys
|
||||
|
||||
Copilot is a Sim-managed service. To use Copilot on a self-hosted instance:
|
||||
|
||||
- Go to https://sim.ai → Settings → Copilot and generate a Copilot API key
|
||||
- Set `COPILOT_API_KEY` in your self-hosted environment to that value
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Framework**: [Next.js](https://nextjs.org/) (App Router)
|
||||
@@ -183,4 +180,4 @@ We welcome contributions! Please see our [Contributing Guide](.github/CONTRIBUTI
|
||||
|
||||
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
<p align="center">Made with ❤️ by the Sim Team</p>
|
||||
<p align="center">Made with ❤️ by the Sim Team</p>
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
title: Copilot
|
||||
description: Build and edit workflows with Sim Copilot
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Card, Cards } from 'fumadocs-ui/components/card'
|
||||
import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react'
|
||||
|
||||
Copilot is your in-editor assistant that helps you build, understand, and improve workflows. It can:
|
||||
|
||||
- **Explain**: Answer questions about Sim and your current workflow
|
||||
- **Guide**: Suggest edits and best practices
|
||||
- **Edit**: Make changes to blocks, connections, and settings when you approve
|
||||
|
||||
<Callout type="info">
|
||||
Copilot is a Sim-managed service. For self-hosted deployments, generate a Copilot API key in the hosted app (sim.ai → Settings → Copilot)
|
||||
1. Go to [sim.ai](https://sim.ai) → Settings → Copilot and generate a Copilot API key
|
||||
2. Set `COPILOT_API_KEY` in your self-hosted environment to that value
|
||||
</Callout>
|
||||
|
||||
## Modes
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<MessageCircle className="h-4 w-4 text-muted-foreground" />
|
||||
Ask
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">
|
||||
Q&A mode for explanations, guidance, and suggestions without making changes to your workflow.
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Package className="h-4 w-4 text-muted-foreground" />
|
||||
Agent
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">
|
||||
Build-and-edit mode. Copilot proposes specific edits (add blocks, wire variables, tweak settings) and applies them when you approve.
|
||||
</div>
|
||||
</Card>
|
||||
</Cards>
|
||||
|
||||
## Depth Levels
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Zap className="h-4 w-4 text-muted-foreground" />
|
||||
Fast
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">Quickest and cheapest. Best for small edits, simple workflows, and minor tweaks.</div>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<InfinityIcon className="h-4 w-4 text-muted-foreground" />
|
||||
Auto
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">Balanced speed and reasoning. Recommended default for most tasks.</div>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Brain className="h-4 w-4 text-muted-foreground" />
|
||||
Advanced
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">More reasoning for larger workflows and complex edits while staying performant.</div>
|
||||
</Card>
|
||||
<Card
|
||||
title={
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<BrainCircuit className="h-4 w-4 text-muted-foreground" />
|
||||
Behemoth
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<div className="m-0 text-sm">Maximum reasoning for deep planning, debugging, and complex architectural changes.</div>
|
||||
</Card>
|
||||
</Cards>
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"title": "Copilot",
|
||||
"pages": ["index"]
|
||||
}
|
||||
@@ -12,8 +12,6 @@
|
||||
"connections",
|
||||
"---Execution---",
|
||||
"execution",
|
||||
"---Copilot---",
|
||||
"copilot",
|
||||
"---Advanced---",
|
||||
"./variables/index",
|
||||
"yaml",
|
||||
|
||||
@@ -71,16 +71,19 @@ Read records from an Airtable table
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token |
|
||||
| `baseId` | string | Yes | ID of the Airtable base |
|
||||
| `tableId` | string | Yes | ID of the table |
|
||||
| `maxRecords` | number | No | Maximum number of records to return |
|
||||
| `filterFormula` | string | No | Formula to filter records \(e.g., "\(\{Field Name\} = \'Value\'\)"\) |
|
||||
| `filterFormula` | string | No | Formula to filter records \(e.g., |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of retrieved Airtable records |
|
||||
| `records` | json | Retrieved record data |
|
||||
| `record` | json | Single record data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `airtable_get_record`
|
||||
|
||||
@@ -90,6 +93,7 @@ Retrieve a single record from an Airtable table by its ID
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token |
|
||||
| `baseId` | string | Yes | ID of the Airtable base |
|
||||
| `tableId` | string | Yes | ID or name of the table |
|
||||
| `recordId` | string | Yes | ID of the record to retrieve |
|
||||
@@ -98,8 +102,9 @@ Retrieve a single record from an Airtable table by its ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Retrieved Airtable record with id, createdTime, and fields |
|
||||
| `metadata` | json | Operation metadata including record count |
|
||||
| `records` | json | Retrieved record data |
|
||||
| `record` | json | Single record data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `airtable_create_records`
|
||||
|
||||
@@ -109,16 +114,17 @@ Write new records to an Airtable table
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token |
|
||||
| `baseId` | string | Yes | ID of the Airtable base |
|
||||
| `tableId` | string | Yes | ID or name of the table |
|
||||
| `records` | json | Yes | Array of records to create, each with a `fields` object |
|
||||
| `fields` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of created Airtable records |
|
||||
| `records` | json | Retrieved record data |
|
||||
| `record` | json | Single record data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `airtable_update_record`
|
||||
|
||||
@@ -128,6 +134,7 @@ Update an existing record in an Airtable table by ID
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token |
|
||||
| `baseId` | string | Yes | ID of the Airtable base |
|
||||
| `tableId` | string | Yes | ID or name of the table |
|
||||
| `recordId` | string | Yes | ID of the record to update |
|
||||
@@ -137,8 +144,9 @@ Update an existing record in an Airtable table by ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Updated Airtable record with id, createdTime, and fields |
|
||||
| `metadata` | json | Operation metadata including record count and updated field names |
|
||||
| `records` | json | Retrieved record data |
|
||||
| `record` | json | Single record data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `airtable_update_multiple_records`
|
||||
|
||||
@@ -148,15 +156,17 @@ Update multiple existing records in an Airtable table
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token |
|
||||
| `baseId` | string | Yes | ID of the Airtable base |
|
||||
| `tableId` | string | Yes | ID or name of the table |
|
||||
| `records` | json | Yes | Array of records to update, each with an `id` and a `fields` object |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of updated Airtable records |
|
||||
| `records` | json | Retrieved record data |
|
||||
| `record` | json | Single record data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,10 @@ Search for academic papers on ArXiv by keywords, authors, titles, or other field
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `papers` | json | Array of papers matching the search query |
|
||||
| `papers` | json | Found papers data |
|
||||
| `totalResults` | number | Total results count |
|
||||
| `paper` | json | Paper details |
|
||||
| `authorPapers` | json | Author papers list |
|
||||
|
||||
### `arxiv_get_paper`
|
||||
|
||||
@@ -81,13 +84,16 @@ Get detailed information about a specific ArXiv paper by its ID.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `paperId` | string | Yes | ArXiv paper ID \(e.g., "1706.03762"\) |
|
||||
| `paperId` | string | Yes | ArXiv paper ID \(e.g., |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `paper` | json | Detailed information about the requested ArXiv paper |
|
||||
| `papers` | json | Found papers data |
|
||||
| `totalResults` | number | Total results count |
|
||||
| `paper` | json | Paper details |
|
||||
| `authorPapers` | json | Author papers list |
|
||||
|
||||
### `arxiv_get_author_papers`
|
||||
|
||||
@@ -104,7 +110,10 @@ Search for papers by a specific author on ArXiv.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `authorPapers` | json | Array of papers authored by the specified author |
|
||||
| `papers` | json | Found papers data |
|
||||
| `totalResults` | number | Total results count |
|
||||
| `paper` | json | Paper details |
|
||||
| `authorPapers` | json | Author papers list |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ Runs a browser automation task using BrowserUse
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `task` | string | Yes | What should the browser agent do |
|
||||
| `variables` | json | No | Optional variables to use as secrets \(format: \{key: value\}\) |
|
||||
| `format` | string | No | No description |
|
||||
| `save_browser_data` | boolean | No | Whether to save browser data |
|
||||
| `model` | string | No | LLM model to use \(default: gpt-4o\) |
|
||||
| `apiKey` | string | Yes | API key for BrowserUse API |
|
||||
@@ -84,7 +83,7 @@ Runs a browser automation task using BrowserUse
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Task execution identifier |
|
||||
| `success` | boolean | Task completion status |
|
||||
| `output` | json | Task output data |
|
||||
| `output` | any | Task output data |
|
||||
| `steps` | json | Execution steps taken |
|
||||
|
||||
|
||||
|
||||
@@ -220,8 +220,7 @@ Populate Clay with data from a JSON file. Enables direct communication and notif
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | json | Clay populate operation results including response data from Clay webhook |
|
||||
| `data` | any | Response data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ Retrieve content from Confluence pages using the Confluence API.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Confluence |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | Yes | Confluence page ID to retrieve |
|
||||
| `cloudId` | string | No | Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
@@ -65,10 +66,11 @@ Retrieve content from Confluence pages using the Confluence API.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of retrieval |
|
||||
| `pageId` | string | Confluence page ID |
|
||||
| `content` | string | Page content with HTML tags stripped |
|
||||
| `ts` | string | Timestamp |
|
||||
| `pageId` | string | Page identifier |
|
||||
| `content` | string | Page content |
|
||||
| `title` | string | Page title |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `confluence_update`
|
||||
|
||||
@@ -78,6 +80,7 @@ Update a Confluence page using the Confluence API.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Confluence |
|
||||
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `pageId` | string | Yes | Confluence page ID to update |
|
||||
| `title` | string | No | New title for the page |
|
||||
@@ -89,10 +92,11 @@ Update a Confluence page using the Confluence API.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of update |
|
||||
| `pageId` | string | Confluence page ID |
|
||||
| `title` | string | Updated page title |
|
||||
| `success` | boolean | Update operation success status |
|
||||
| `ts` | string | Timestamp |
|
||||
| `pageId` | string | Page identifier |
|
||||
| `content` | string | Page content |
|
||||
| `title` | string | Page title |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ Send a message to a Discord channel
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or error message |
|
||||
| `data` | object | Discord message data |
|
||||
| `message` | string | Message content |
|
||||
| `data` | any | Response data |
|
||||
|
||||
### `discord_get_messages`
|
||||
|
||||
@@ -99,8 +99,8 @@ Retrieve messages from a Discord channel
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or error message |
|
||||
| `messages` | array | Array of Discord messages with full metadata |
|
||||
| `message` | string | Message content |
|
||||
| `data` | any | Response data |
|
||||
|
||||
### `discord_get_server`
|
||||
|
||||
@@ -117,8 +117,8 @@ Retrieve information about a Discord server (guild)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or error message |
|
||||
| `data` | object | Discord server \(guild\) information |
|
||||
| `message` | string | Message content |
|
||||
| `data` | any | Response data |
|
||||
|
||||
### `discord_get_user`
|
||||
|
||||
@@ -135,8 +135,8 @@ Retrieve information about a Discord user
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or error message |
|
||||
| `data` | object | Discord user information |
|
||||
| `message` | string | Message content |
|
||||
| `data` | any | Response data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Convert TTS using ElevenLabs voices
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `audioUrl` | string | The URL of the generated audio |
|
||||
| `audioUrl` | string | Generated audio URL |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,11 @@ Search the web using Exa AI. Returns relevant search results with titles, URLs,
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Search results with titles, URLs, and text snippets |
|
||||
| `results` | json | Search results |
|
||||
| `similarLinks` | json | Similar links found |
|
||||
| `answer` | string | Generated answer |
|
||||
| `citations` | json | Answer citations |
|
||||
| `research` | json | Research findings |
|
||||
|
||||
### `exa_get_contents`
|
||||
|
||||
@@ -87,7 +91,11 @@ Retrieve the contents of webpages using Exa AI. Returns the title, text content,
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Retrieved content from URLs with title, text, and summaries |
|
||||
| `results` | json | Search results |
|
||||
| `similarLinks` | json | Similar links found |
|
||||
| `answer` | string | Generated answer |
|
||||
| `citations` | json | Answer citations |
|
||||
| `research` | json | Research findings |
|
||||
|
||||
### `exa_find_similar_links`
|
||||
|
||||
@@ -106,7 +114,11 @@ Find webpages similar to a given URL using Exa AI. Returns a list of similar lin
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `similarLinks` | array | Similar links found with titles, URLs, and text snippets |
|
||||
| `results` | json | Search results |
|
||||
| `similarLinks` | json | Similar links found |
|
||||
| `answer` | string | Generated answer |
|
||||
| `citations` | json | Answer citations |
|
||||
| `research` | json | Research findings |
|
||||
|
||||
### `exa_answer`
|
||||
|
||||
@@ -124,8 +136,11 @@ Get an AI-generated answer to a question with citations from the web using Exa A
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `answer` | string | AI-generated answer to the question |
|
||||
| `citations` | array | Sources and citations for the answer |
|
||||
| `results` | json | Search results |
|
||||
| `similarLinks` | json | Similar links found |
|
||||
| `answer` | string | Generated answer |
|
||||
| `citations` | json | Answer citations |
|
||||
| `research` | json | Research findings |
|
||||
|
||||
### `exa_research`
|
||||
|
||||
@@ -143,7 +158,11 @@ Perform comprehensive research using AI to generate detailed reports with citati
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `research` | array | Comprehensive research findings with citations and summaries |
|
||||
| `results` | json | Search results |
|
||||
| `similarLinks` | json | Similar links found |
|
||||
| `answer` | string | Generated answer |
|
||||
| `citations` | json | Answer citations |
|
||||
| `research` | json | Research findings |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ Parse one or more uploaded files or files from URLs (text, PDF, CSV, images, etc
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `files` | array | Array of parsed files |
|
||||
| `combinedContent` | string | Combined content of all parsed files |
|
||||
| `files` | json | Parsed file data |
|
||||
| `combinedContent` | string | Combined file content |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -81,9 +81,14 @@ Extract structured content from web pages with comprehensive metadata support. C
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `markdown` | string | Page content in markdown format |
|
||||
| `html` | string | Raw HTML content of the page |
|
||||
| `metadata` | object | Page metadata including SEO and Open Graph information |
|
||||
| `markdown` | string | Page content markdown |
|
||||
| `html` | any | Raw HTML content |
|
||||
| `metadata` | json | Page metadata |
|
||||
| `data` | json | Search results data |
|
||||
| `warning` | any | Warning messages |
|
||||
| `pages` | json | Crawled pages data |
|
||||
| `total` | number | Total pages found |
|
||||
| `creditsUsed` | number | Credits consumed |
|
||||
|
||||
### `firecrawl_search`
|
||||
|
||||
@@ -100,7 +105,14 @@ Search for information on the web using Firecrawl
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Search results data |
|
||||
| `markdown` | string | Page content markdown |
|
||||
| `html` | any | Raw HTML content |
|
||||
| `metadata` | json | Page metadata |
|
||||
| `data` | json | Search results data |
|
||||
| `warning` | any | Warning messages |
|
||||
| `pages` | json | Crawled pages data |
|
||||
| `total` | number | Total pages found |
|
||||
| `creditsUsed` | number | Credits consumed |
|
||||
|
||||
### `firecrawl_crawl`
|
||||
|
||||
@@ -119,7 +131,14 @@ Crawl entire websites and extract structured content from all accessible pages
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pages` | array | Array of crawled pages with their content and metadata |
|
||||
| `markdown` | string | Page content markdown |
|
||||
| `html` | any | Raw HTML content |
|
||||
| `metadata` | json | Page metadata |
|
||||
| `data` | json | Search results data |
|
||||
| `warning` | any | Warning messages |
|
||||
| `pages` | json | Crawled pages data |
|
||||
| `total` | number | Total pages found |
|
||||
| `creditsUsed` | number | Credits consumed |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
title: Webhook
|
||||
description: Receive webhooks from any service
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="generic_webhook"
|
||||
color="#10B981"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
fill='currentColor'
|
||||
|
||||
|
||||
viewBox='0 0 24 24'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path d='M17.974 7A4.967 4.967 0 0 0 18 6.5a5.5 5.5 0 1 0-8.672 4.491L7.18 15.114A2.428 2.428 0 0 0 6.496 15 2.5 2.5 0 1 0 9 17.496a2.36 2.36 0 0 0-.93-1.925l2.576-4.943-.41-.241A4.5 4.5 0 1 1 17 6.5a4.8 4.8 0 0 1-.022.452zM6.503 18.999a1.5 1.5 0 1 1 1.496-1.503A1.518 1.518 0 0 1 6.503 19zM18.5 12a5.735 5.735 0 0 0-1.453.157l-2.744-3.941A2.414 2.414 0 0 0 15 6.5a2.544 2.544 0 1 0-1.518 2.284l3.17 4.557.36-.13A4.267 4.267 0 0 1 18.5 13a4.5 4.5 0 1 1-.008 9h-.006a4.684 4.684 0 0 1-3.12-1.355l-.703.71A5.653 5.653 0 0 0 18.49 23h.011a5.5 5.5 0 0 0 0-11zM11 6.5A1.5 1.5 0 1 1 12.5 8 1.509 1.509 0 0 1 11 6.5zM18.5 20a2.5 2.5 0 1 0-2.447-3h-5.05l-.003.497A4.546 4.546 0 0 1 6.5 22 4.526 4.526 0 0 1 2 17.5a4.596 4.596 0 0 1 3.148-4.37l-.296-.954A5.606 5.606 0 0 0 1 17.5 5.532 5.532 0 0 0 6.5 23a5.573 5.573 0 0 0 5.478-5h4.08a2.487 2.487 0 0 0 2.442 2zm0-4a1.5 1.5 0 1 1-1.5 1.5 1.509 1.509 0 0 1 1.5-1.5z' />
|
||||
<path fill='none' d='M0 0h24v24H0z' />
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `triggers`
|
||||
- Type: `generic_webhook`
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: GitHub
|
||||
description: Interact with GitHub or trigger workflows from GitHub events
|
||||
description: Interact with GitHub
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -35,7 +35,7 @@ In Sim, the GitHub integration enables your agents to interact directly with Git
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Access GitHub repositories, pull requests, and comments through the GitHub API. Automate code reviews, PR management, and repository interactions within your workflow. Trigger workflows from GitHub events like push, pull requests, and issues.
|
||||
Access GitHub repositories, pull requests, and comments through the GitHub API. Automate code reviews, PR management, and repository interactions within your workflow.
|
||||
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ Fetch PR details including diff and files changed
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Human-readable PR summary |
|
||||
| `metadata` | object | Detailed PR metadata including file changes |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Response metadata |
|
||||
|
||||
### `github_comment`
|
||||
|
||||
@@ -85,8 +85,8 @@ Create comments on GitHub PRs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Human-readable comment confirmation |
|
||||
| `metadata` | object | Comment metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Response metadata |
|
||||
|
||||
### `github_repo_info`
|
||||
|
||||
@@ -104,8 +104,8 @@ Retrieve comprehensive GitHub repository metadata including stars, forks, issues
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Human-readable repository summary |
|
||||
| `metadata` | object | Repository metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Response metadata |
|
||||
|
||||
### `github_latest_commit`
|
||||
|
||||
@@ -117,15 +117,15 @@ Retrieve the latest commit from a GitHub repository
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `owner` | string | Yes | Repository owner \(user or organization\) |
|
||||
| `repo` | string | Yes | Repository name |
|
||||
| `branch` | string | No | Branch name \(defaults to the repository's default branch\) |
|
||||
| `branch` | string | No | Branch name \(defaults to the repository |
|
||||
| `apiKey` | string | Yes | GitHub API token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Human-readable commit summary |
|
||||
| `metadata` | object | Commit metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Response metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Gmail
|
||||
description: Send Gmail or trigger workflows from Gmail events
|
||||
description: Send Gmail
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -51,7 +51,7 @@ In Sim, the Gmail integration enables your agents to send, read, and search emai
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Comprehensive Gmail integration with OAuth authentication. Send email messages, read email content, and trigger workflows from Gmail events like new emails and label changes.
|
||||
Integrate Gmail functionality to send email messages within your workflow. Automate email communications and process email content using OAuth authentication.
|
||||
|
||||
|
||||
|
||||
@@ -65,18 +65,17 @@ Send emails using Gmail
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Gmail API |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
| `bcc` | string | No | BCC recipients \(comma-separated\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Success message |
|
||||
| `metadata` | object | Email metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Email metadata |
|
||||
|
||||
### `gmail_draft`
|
||||
|
||||
@@ -86,18 +85,17 @@ Draft emails using Gmail
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Gmail API |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
| `bcc` | string | No | BCC recipients \(comma-separated\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Success message |
|
||||
| `metadata` | object | Draft metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Email metadata |
|
||||
|
||||
### `gmail_read`
|
||||
|
||||
@@ -107,19 +105,18 @@ Read emails from Gmail
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Gmail API |
|
||||
| `messageId` | string | No | ID of the message to read |
|
||||
| `folder` | string | No | Folder/label to read emails from |
|
||||
| `unreadOnly` | boolean | No | Only retrieve unread messages |
|
||||
| `maxResults` | number | No | Maximum number of messages to retrieve \(default: 1, max: 10\) |
|
||||
| `includeAttachments` | boolean | No | Download and include email attachments |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Text content of the email |
|
||||
| `metadata` | json | Metadata of the email |
|
||||
| `attachments` | file[] | Attachments of the email |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Email metadata |
|
||||
|
||||
### `gmail_search`
|
||||
|
||||
@@ -129,6 +126,7 @@ Search emails in Gmail
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Gmail API |
|
||||
| `query` | string | Yes | Search query for emails |
|
||||
| `maxResults` | number | No | Maximum number of results to return |
|
||||
|
||||
@@ -136,8 +134,8 @@ Search emails in Gmail
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Search results summary |
|
||||
| `metadata` | object | Search metadata |
|
||||
| `content` | string | Response content |
|
||||
| `metadata` | json | Email metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ Create a new event in Google Calendar
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Google Calendar API |
|
||||
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
|
||||
| `summary` | string | Yes | Event title/summary |
|
||||
| `description` | string | No | Event description |
|
||||
@@ -118,8 +119,8 @@ Create a new event in Google Calendar
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Event creation confirmation message |
|
||||
| `metadata` | json | Created event metadata including ID, status, and details |
|
||||
| `content` | string | Operation response content |
|
||||
| `metadata` | json | Event metadata |
|
||||
|
||||
### `google_calendar_list`
|
||||
|
||||
@@ -129,6 +130,7 @@ List events from Google Calendar
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Google Calendar API |
|
||||
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
|
||||
| `timeMin` | string | No | Lower bound for events \(RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z\) |
|
||||
| `timeMax` | string | No | Upper bound for events \(RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z\) |
|
||||
@@ -139,8 +141,8 @@ List events from Google Calendar
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Summary of found events count |
|
||||
| `metadata` | json | List of events with pagination tokens and event details |
|
||||
| `content` | string | Operation response content |
|
||||
| `metadata` | json | Event metadata |
|
||||
|
||||
### `google_calendar_get`
|
||||
|
||||
@@ -150,6 +152,7 @@ Get a specific event from Google Calendar
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Google Calendar API |
|
||||
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
|
||||
| `eventId` | string | Yes | Event ID to retrieve |
|
||||
|
||||
@@ -157,8 +160,8 @@ Get a specific event from Google Calendar
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Event retrieval confirmation message |
|
||||
| `metadata` | json | Event details including ID, status, times, and attendees |
|
||||
| `content` | string | Operation response content |
|
||||
| `metadata` | json | Event metadata |
|
||||
|
||||
### `google_calendar_quick_add`
|
||||
|
||||
@@ -168,8 +171,9 @@ Create events from natural language text
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Google Calendar API |
|
||||
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
|
||||
| `text` | string | Yes | Natural language text describing the event \(e.g., "Meeting with John tomorrow at 3pm"\) |
|
||||
| `text` | string | Yes | Natural language text describing the event \(e.g., |
|
||||
| `attendees` | array | No | Array of attendee email addresses \(comma-separated string also accepted\) |
|
||||
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
|
||||
|
||||
@@ -177,8 +181,8 @@ Create events from natural language text
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Event creation confirmation message from natural language |
|
||||
| `metadata` | json | Created event metadata including parsed details |
|
||||
| `content` | string | Operation response content |
|
||||
| `metadata` | json | Event metadata |
|
||||
|
||||
### `google_calendar_invite`
|
||||
|
||||
@@ -188,6 +192,7 @@ Invite attendees to an existing Google Calendar event
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Google Calendar API |
|
||||
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
|
||||
| `eventId` | string | Yes | Event ID to invite attendees to |
|
||||
| `attendees` | array | Yes | Array of attendee email addresses to invite |
|
||||
@@ -198,8 +203,8 @@ Invite attendees to an existing Google Calendar event
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Attendee invitation confirmation message with email delivery status |
|
||||
| `metadata` | json | Updated event metadata including attendee list and details |
|
||||
| `content` | string | Operation response content |
|
||||
| `metadata` | json | Event metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -95,14 +95,16 @@ Read content from a Google Docs document
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Docs API |
|
||||
| `documentId` | string | Yes | The ID of the document to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Extracted document text content |
|
||||
| `metadata` | json | Document metadata including ID, title, and URL |
|
||||
| `content` | string | Document content |
|
||||
| `metadata` | json | Document metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
### `google_docs_write`
|
||||
|
||||
@@ -112,6 +114,7 @@ Write or update content in a Google Docs document
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Docs API |
|
||||
| `documentId` | string | Yes | The ID of the document to write to |
|
||||
| `content` | string | Yes | The content to write to the document |
|
||||
|
||||
@@ -119,8 +122,9 @@ Write or update content in a Google Docs document
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `updatedContent` | boolean | Indicates if document content was updated successfully |
|
||||
| `metadata` | json | Updated document metadata including ID, title, and URL |
|
||||
| `content` | string | Document content |
|
||||
| `metadata` | json | Document metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
### `google_docs_create`
|
||||
|
||||
@@ -130,6 +134,7 @@ Create a new Google Docs document
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Docs API |
|
||||
| `title` | string | Yes | The title of the document to create |
|
||||
| `content` | string | No | The content of the document to create |
|
||||
| `folderSelector` | string | No | Select the folder to create the document in |
|
||||
@@ -139,7 +144,9 @@ Create a new Google Docs document
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metadata` | json | Created document metadata including ID, title, and URL |
|
||||
| `content` | string | Document content |
|
||||
| `metadata` | json | Document metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ Upload a file to Google Drive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Drive API |
|
||||
| `fileName` | string | Yes | The name of the file to upload |
|
||||
| `content` | string | Yes | The content of the file to upload |
|
||||
| `mimeType` | string | No | The MIME type of the file to upload |
|
||||
@@ -97,7 +98,8 @@ Upload a file to Google Drive
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `file` | json | Uploaded file metadata including ID, name, and links |
|
||||
| `file` | json | File data |
|
||||
| `files` | json | Files list |
|
||||
|
||||
### `google_drive_create_folder`
|
||||
|
||||
@@ -107,6 +109,7 @@ Create a new folder in Google Drive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Drive API |
|
||||
| `fileName` | string | Yes | Name of the folder to create |
|
||||
| `folderSelector` | string | No | Select the parent folder to create the folder in |
|
||||
| `folderId` | string | No | ID of the parent folder \(internal use\) |
|
||||
@@ -115,7 +118,8 @@ Create a new folder in Google Drive
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `file` | json | Created folder metadata including ID, name, and parent information |
|
||||
| `file` | json | File data |
|
||||
| `files` | json | Files list |
|
||||
|
||||
### `google_drive_list`
|
||||
|
||||
@@ -125,6 +129,7 @@ List files and folders in Google Drive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Drive API |
|
||||
| `folderSelector` | string | No | Select the folder to list files from |
|
||||
| `folderId` | string | No | The ID of the folder to list files from \(internal use\) |
|
||||
| `query` | string | No | A query to filter the files |
|
||||
@@ -135,7 +140,8 @@ List files and folders in Google Drive
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `files` | json | Array of file metadata objects from the specified folder |
|
||||
| `file` | json | File data |
|
||||
| `files` | json | Files list |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ Search the web with the Custom Search API
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of search results from Google |
|
||||
| `items` | json | Search result items |
|
||||
| `searchInformation` | json | Search metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ Read data from a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Sheets API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to read from |
|
||||
| `range` | string | No | The range of cells to read from |
|
||||
|
||||
@@ -117,8 +118,13 @@ Read data from a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | json | Sheet data including range and cell values |
|
||||
| `metadata` | json | Spreadsheet metadata including ID and URL |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `tableRange` | string | Table range |
|
||||
|
||||
### `google_sheets_write`
|
||||
|
||||
@@ -128,6 +134,7 @@ Write data to a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Sheets API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to write to |
|
||||
| `range` | string | No | The range of cells to write to |
|
||||
| `values` | array | Yes | The data to write to the spreadsheet |
|
||||
@@ -138,11 +145,13 @@ Write data to a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `updatedRange` | string | Range of cells that were updated |
|
||||
| `updatedRows` | number | Number of rows updated |
|
||||
| `updatedColumns` | number | Number of columns updated |
|
||||
| `updatedCells` | number | Number of cells updated |
|
||||
| `metadata` | json | Spreadsheet metadata including ID and URL |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `tableRange` | string | Table range |
|
||||
|
||||
### `google_sheets_update`
|
||||
|
||||
@@ -152,6 +161,7 @@ Update data in a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Sheets API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to update |
|
||||
| `range` | string | No | The range of cells to update |
|
||||
| `values` | array | Yes | The data to update in the spreadsheet |
|
||||
@@ -162,11 +172,13 @@ Update data in a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `updatedRange` | string | Range of cells that were updated |
|
||||
| `updatedRows` | number | Number of rows updated |
|
||||
| `updatedColumns` | number | Number of columns updated |
|
||||
| `updatedCells` | number | Number of cells updated |
|
||||
| `metadata` | json | Spreadsheet metadata including ID and URL |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `tableRange` | string | Table range |
|
||||
|
||||
### `google_sheets_append`
|
||||
|
||||
@@ -176,6 +188,7 @@ Append data to the end of a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Google Sheets API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to append to |
|
||||
| `range` | string | No | The range of cells to append after |
|
||||
| `values` | array | Yes | The data to append to the spreadsheet |
|
||||
@@ -187,12 +200,13 @@ Append data to the end of a Google Sheets spreadsheet
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tableRange` | string | Range of the table where data was appended |
|
||||
| `updatedRange` | string | Range of cells that were updated |
|
||||
| `updatedRows` | number | Number of rows updated |
|
||||
| `updatedColumns` | number | Number of columns updated |
|
||||
| `updatedCells` | number | Number of cells updated |
|
||||
| `metadata` | json | Spreadsheet metadata including ID and URL |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `tableRange` | string | Table range |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -92,8 +92,9 @@ Generate completions using Hugging Face Inference API
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Chat completion results |
|
||||
| `content` | string | Generated response |
|
||||
| `model` | string | Model used |
|
||||
| `usage` | json | Token usage stats |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Returns companies matching a set of criteria using Hunter.io AI-powered search.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | No | Natural language search query for companies |
|
||||
| `domain` | string | No | Company domain names to filter by |
|
||||
| `headcount` | string | No | Company size filter \(e.g., "1-10", "11-50"\) |
|
||||
| `headcount` | string | No | Company size filter \(e.g., |
|
||||
| `company_type` | string | No | Type of organization |
|
||||
| `technology` | string | No | Technology used by companies |
|
||||
| `apiKey` | string | Yes | Hunter.io API Key |
|
||||
@@ -66,7 +66,15 @@ Returns companies matching a set of criteria using Hunter.io AI-powered search.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Array of companies matching the search criteria, each containing domain, name, headcount, technologies, and email_count |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
### `hunter_domain_search`
|
||||
|
||||
@@ -88,26 +96,15 @@ Returns all the email addresses found using one given domain name, with sources.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `domain` | string | The searched domain name |
|
||||
| `disposable` | boolean | Whether the domain accepts disposable email addresses |
|
||||
| `webmail` | boolean | Whether the domain is a webmail provider |
|
||||
| `accept_all` | boolean | Whether the domain accepts all email addresses |
|
||||
| `pattern` | string | The email pattern used by the organization |
|
||||
| `organization` | string | The organization name |
|
||||
| `description` | string | Description of the organization |
|
||||
| `industry` | string | Industry of the organization |
|
||||
| `twitter` | string | Twitter profile of the organization |
|
||||
| `facebook` | string | Facebook profile of the organization |
|
||||
| `linkedin` | string | LinkedIn profile of the organization |
|
||||
| `instagram` | string | Instagram profile of the organization |
|
||||
| `youtube` | string | YouTube channel of the organization |
|
||||
| `technologies` | array | Array of technologies used by the organization |
|
||||
| `country` | string | Country where the organization is located |
|
||||
| `state` | string | State where the organization is located |
|
||||
| `city` | string | City where the organization is located |
|
||||
| `postal_code` | string | Postal code of the organization |
|
||||
| `street` | string | Street address of the organization |
|
||||
| `emails` | array | Array of email addresses found for the domain, each containing value, type, confidence, sources, and person details |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
### `hunter_email_finder`
|
||||
|
||||
@@ -118,8 +115,8 @@ Finds the most likely email address for a person given their name and company do
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Company domain name |
|
||||
| `first_name` | string | Yes | Person's first name |
|
||||
| `last_name` | string | Yes | Person's last name |
|
||||
| `first_name` | string | Yes | Person |
|
||||
| `last_name` | string | Yes | Person |
|
||||
| `company` | string | No | Company name |
|
||||
| `apiKey` | string | Yes | Hunter.io API Key |
|
||||
|
||||
@@ -127,10 +124,15 @@ Finds the most likely email address for a person given their name and company do
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `email` | string | The found email address |
|
||||
| `score` | number | Confidence score for the found email address |
|
||||
| `sources` | array | Array of sources where the email was found, each containing domain, uri, extracted_on, last_seen_on, and still_on_page |
|
||||
| `verification` | object | Verification information containing date and status |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
### `hunter_email_verifier`
|
||||
|
||||
@@ -147,20 +149,15 @@ Verifies the deliverability of an email address and provides detailed verificati
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `result` | string | Deliverability result: deliverable, undeliverable, or risky |
|
||||
| `score` | number | Confidence score for the verification result |
|
||||
| `email` | string | The verified email address |
|
||||
| `regexp` | boolean | Whether the email follows a valid regex pattern |
|
||||
| `gibberish` | boolean | Whether the email appears to be gibberish |
|
||||
| `disposable` | boolean | Whether the email is from a disposable email provider |
|
||||
| `webmail` | boolean | Whether the email is from a webmail provider |
|
||||
| `mx_records` | boolean | Whether MX records exist for the domain |
|
||||
| `smtp_server` | boolean | Whether the SMTP server is reachable |
|
||||
| `smtp_check` | boolean | Whether the SMTP check was successful |
|
||||
| `accept_all` | boolean | Whether the domain accepts all email addresses |
|
||||
| `block` | boolean | Whether the email is blocked |
|
||||
| `status` | string | Verification status: valid, invalid, accept_all, webmail, disposable, or unknown |
|
||||
| `sources` | array | Array of sources where the email was found |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
### `hunter_companies_find`
|
||||
|
||||
@@ -177,8 +174,15 @@ Enriches company data using domain name.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `person` | object | Person information \(undefined for companies_find tool\) |
|
||||
| `company` | object | Company information including name, domain, industry, size, country, linkedin, and twitter |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
### `hunter_email_count`
|
||||
|
||||
@@ -197,11 +201,15 @@ Returns the total number of email addresses found for a domain or company.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `total` | number | Total number of email addresses found |
|
||||
| `personal_emails` | number | Number of personal email addresses found |
|
||||
| `generic_emails` | number | Number of generic email addresses found |
|
||||
| `department` | object | Breakdown of email addresses by department \(executive, it, finance, management, sales, legal, support, hr, marketing, communication\) |
|
||||
| `seniority` | object | Breakdown of email addresses by seniority level \(junior, senior, executive\) |
|
||||
| `results` | json | Search results |
|
||||
| `emails` | json | Email addresses found |
|
||||
| `email` | string | Found email address |
|
||||
| `score` | number | Confidence score |
|
||||
| `result` | string | Verification result |
|
||||
| `status` | string | Status message |
|
||||
| `total` | number | Total results count |
|
||||
| `personal_emails` | number | Personal emails count |
|
||||
| `generic_emails` | number | Generic emails count |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ Generate images using OpenAI
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Generated image data |
|
||||
| `content` | string | Generation response |
|
||||
| `image` | string | Generated image URL |
|
||||
| `metadata` | json | Generation metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ Extract and process web content into clean, LLM-friendly text using Jina AI Read
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | The extracted content from the URL, processed into clean, LLM-friendly text |
|
||||
| `content` | string | Extracted content |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ Retrieve detailed information about a specific Jira issue
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Jira |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | No | Jira project ID to retrieve issues from. If not provided, all issues will be retrieved. |
|
||||
| `issueKey` | string | Yes | Jira issue key to retrieve \(e.g., PROJ-123\) |
|
||||
@@ -66,8 +67,14 @@ Retrieve detailed information about a specific Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Jira issue details with issue key, summary, description, created and updated timestamps |
|
||||
| `ts` | string | Timestamp |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | string | Issue description |
|
||||
| `created` | string | Creation date |
|
||||
| `updated` | string | Update date |
|
||||
| `success` | boolean | Operation success |
|
||||
| `url` | string | Issue URL |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -77,6 +84,7 @@ Update a Jira issue
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Jira |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | No | Jira project ID to update issues in. If not provided, all issues will be retrieved. |
|
||||
| `issueKey` | string | Yes | Jira issue key to update |
|
||||
@@ -91,8 +99,14 @@ Update a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated Jira issue details with timestamp, issue key, summary, and success status |
|
||||
| `ts` | string | Timestamp |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | string | Issue description |
|
||||
| `created` | string | Creation date |
|
||||
| `updated` | string | Update date |
|
||||
| `success` | boolean | Operation success |
|
||||
| `url` | string | Issue URL |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -102,6 +116,7 @@ Write a Jira issue
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Jira |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | Yes | Project ID for the issue |
|
||||
| `summary` | string | Yes | Summary for the issue |
|
||||
@@ -115,8 +130,14 @@ Write a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created Jira issue details with timestamp, issue key, summary, success status, and URL |
|
||||
| `ts` | string | Timestamp |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | string | Issue description |
|
||||
| `created` | string | Creation date |
|
||||
| `updated` | string | Update date |
|
||||
| `success` | boolean | Operation success |
|
||||
| `url` | string | Issue URL |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -126,6 +147,7 @@ Retrieve multiple Jira issues in bulk
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Jira |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | Yes | Jira project ID |
|
||||
| `cloudId` | string | No | Jira cloud ID |
|
||||
@@ -134,8 +156,14 @@ Retrieve multiple Jira issues in bulk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | array | Array of Jira issues with summary, description, created and updated timestamps |
|
||||
| `ts` | string | Timestamp |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | string | Issue description |
|
||||
| `created` | string | Creation date |
|
||||
| `updated` | string | Update date |
|
||||
| `success` | boolean | Operation success |
|
||||
| `url` | string | Issue URL |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,9 @@ Search for similar content in a knowledge base using vector similarity
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Array of search results from the knowledge base |
|
||||
| `results` | json | Search results |
|
||||
| `query` | string | Query used |
|
||||
| `totalResults` | number | Total results count |
|
||||
|
||||
### `knowledge_upload_chunk`
|
||||
|
||||
@@ -90,7 +92,9 @@ Upload a new chunk to a document in a knowledge base
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | object | Information about the uploaded chunk |
|
||||
| `results` | json | Search results |
|
||||
| `query` | string | Query used |
|
||||
| `totalResults` | number | Total results count |
|
||||
|
||||
### `knowledge_create_document`
|
||||
|
||||
@@ -116,7 +120,9 @@ Create a new document in a knowledge base
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | object | Information about the created document |
|
||||
| `results` | json | Search results |
|
||||
| `query` | string | Query used |
|
||||
| `totalResults` | number | Total results count |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ Fetch and filter issues from Linear
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issues` | array | Array of issues from the specified Linear team and project, each containing id, title, description, state, teamId, and projectId |
|
||||
| `issues` | json | Issues list |
|
||||
| `issue` | json | Single issue data |
|
||||
|
||||
### `linear_create_issue`
|
||||
|
||||
@@ -82,7 +83,8 @@ Create a new issue in Linear
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issue` | object | The created issue containing id, title, description, state, teamId, and projectId |
|
||||
| `issues` | json | Issues list |
|
||||
| `issue` | json | Single issue data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,16 +58,16 @@ Search the web for information using Linkup
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `q` | string | Yes | The search query |
|
||||
| `depth` | string | Yes | Search depth \(has to either be "standard" or "deep"\) |
|
||||
| `outputType` | string | Yes | Type of output to return \(has to either be "sourcedAnswer" or "searchResults"\) |
|
||||
| `depth` | string | Yes | Search depth \(has to either be |
|
||||
| `outputType` | string | Yes | Type of output to return \(has to either be |
|
||||
| `apiKey` | string | Yes | Enter your Linkup API key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `answer` | string | The sourced answer to the search query |
|
||||
| `sources` | array | Array of sources used to compile the answer, each containing name, url, and snippet |
|
||||
| `answer` | string | Generated answer |
|
||||
| `sources` | json | Source references |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,8 +66,9 @@ Add memories to Mem0 for persistent storage and retrieval
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ids` | array | Array of memory IDs that were created |
|
||||
| `memories` | array | Array of memory objects that were created |
|
||||
| `ids` | any | Memory identifiers |
|
||||
| `memories` | any | Memory data |
|
||||
| `searchResults` | any | Search results |
|
||||
|
||||
### `mem0_search_memories`
|
||||
|
||||
@@ -86,8 +87,9 @@ Search for memories in Mem0 using semantic search
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `searchResults` | array | Array of search results with memory data, each containing id, data, and score |
|
||||
| `ids` | array | Array of memory IDs found in the search results |
|
||||
| `ids` | any | Memory identifiers |
|
||||
| `memories` | any | Memory data |
|
||||
| `searchResults` | any | Search results |
|
||||
|
||||
### `mem0_get_memories`
|
||||
|
||||
@@ -108,8 +110,9 @@ Retrieve memories from Mem0 by ID or filter criteria
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `memories` | array | Array of retrieved memory objects |
|
||||
| `ids` | array | Array of memory IDs that were retrieved |
|
||||
| `ids` | any | Memory identifiers |
|
||||
| `memories` | any | Memory data |
|
||||
| `searchResults` | any | Search results |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,9 +57,8 @@ Add a new memory to the database or append to existing memory with the same ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the memory was added successfully |
|
||||
| `memories` | array | Array of memory objects including the new or updated memory |
|
||||
| `error` | string | Error message if operation failed |
|
||||
| `memories` | any | Memory data |
|
||||
| `id` | string | Memory identifier |
|
||||
|
||||
### `memory_get`
|
||||
|
||||
@@ -75,10 +74,8 @@ Retrieve a specific memory by its ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the memory was retrieved successfully |
|
||||
| `memories` | array | Array of memory data for the requested ID |
|
||||
| `message` | string | Success or error message |
|
||||
| `error` | string | Error message if operation failed |
|
||||
| `memories` | any | Memory data |
|
||||
| `id` | string | Memory identifier |
|
||||
|
||||
### `memory_get_all`
|
||||
|
||||
@@ -93,10 +90,8 @@ Retrieve all memories from the database
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether all memories were retrieved successfully |
|
||||
| `memories` | array | Array of all memory objects with keys, types, and data |
|
||||
| `message` | string | Success or error message |
|
||||
| `error` | string | Error message if operation failed |
|
||||
| `memories` | any | Memory data |
|
||||
| `id` | string | Memory identifier |
|
||||
|
||||
### `memory_delete`
|
||||
|
||||
@@ -112,9 +107,8 @@ Delete a specific memory by its ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the memory was deleted successfully |
|
||||
| `message` | string | Success or error message |
|
||||
| `error` | string | Error message if operation failed |
|
||||
| `memories` | any | Memory data |
|
||||
| `id` | string | Memory identifier |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"exa",
|
||||
"file",
|
||||
"firecrawl",
|
||||
"generic_webhook",
|
||||
"github",
|
||||
"gmail",
|
||||
"google_calendar",
|
||||
@@ -33,15 +32,12 @@
|
||||
"microsoft_planner",
|
||||
"microsoft_teams",
|
||||
"mistral_parse",
|
||||
"mysql",
|
||||
"notion",
|
||||
"onedrive",
|
||||
"openai",
|
||||
"outlook",
|
||||
"parallel_ai",
|
||||
"perplexity",
|
||||
"pinecone",
|
||||
"postgresql",
|
||||
"qdrant",
|
||||
"reddit",
|
||||
"s3",
|
||||
|
||||
@@ -108,14 +108,22 @@ Read data from a Microsoft Excel spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to read from |
|
||||
| `range` | string | No | The range of cells to read from. Accepts "SheetName!A1:B2" for explicit ranges or just "SheetName" to read the used range of that sheet. If omitted, reads the used range of the first sheet. |
|
||||
| `range` | string | No | The range of cells to read from |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | object | Range data from the spreadsheet |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `index` | number | Row index |
|
||||
| `values` | json | Table values |
|
||||
|
||||
### `microsoft_excel_write`
|
||||
|
||||
@@ -125,6 +133,7 @@ Write data to a Microsoft Excel spreadsheet
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to write to |
|
||||
| `range` | string | No | The range of cells to write to |
|
||||
| `values` | array | Yes | The data to write to the spreadsheet |
|
||||
@@ -135,11 +144,14 @@ Write data to a Microsoft Excel spreadsheet
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `updatedRange` | string | The range that was updated |
|
||||
| `updatedRows` | number | Number of rows that were updated |
|
||||
| `updatedColumns` | number | Number of columns that were updated |
|
||||
| `updatedCells` | number | Number of cells that were updated |
|
||||
| `metadata` | object | Spreadsheet metadata |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `index` | number | Row index |
|
||||
| `values` | json | Table values |
|
||||
|
||||
### `microsoft_excel_table_add`
|
||||
|
||||
@@ -149,6 +161,7 @@ Add new rows to a Microsoft Excel table
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
|
||||
| `spreadsheetId` | string | Yes | The ID of the spreadsheet containing the table |
|
||||
| `tableName` | string | Yes | The name of the table to add rows to |
|
||||
| `values` | array | Yes | The data to add to the table \(array of arrays or array of objects\) |
|
||||
@@ -157,9 +170,14 @@ Add new rows to a Microsoft Excel table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `index` | number | Index of the first row that was added |
|
||||
| `values` | array | Array of rows that were added to the table |
|
||||
| `metadata` | object | Spreadsheet metadata |
|
||||
| `data` | json | Sheet data |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `updatedRange` | string | Updated range |
|
||||
| `updatedRows` | number | Updated rows count |
|
||||
| `updatedColumns` | number | Updated columns count |
|
||||
| `updatedCells` | number | Updated cells count |
|
||||
| `index` | number | Row index |
|
||||
| `values` | json | Table values |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ Read tasks from Microsoft Planner - get all user tasks or all tasks from a speci
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Planner API |
|
||||
| `planId` | string | No | The ID of the plan to get tasks from \(if not provided, gets all user tasks\) |
|
||||
| `taskId` | string | No | The ID of the task to get |
|
||||
|
||||
@@ -143,9 +144,8 @@ Read tasks from Microsoft Planner - get all user tasks or all tasks from a speci
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether tasks were retrieved successfully |
|
||||
| `tasks` | array | Array of task objects with filtered properties |
|
||||
| `metadata` | object | Metadata including planId, userId, and planUrl |
|
||||
| `task` | json | The Microsoft Planner task object, including details such as id, title, description, status, due date, and assignees. |
|
||||
| `metadata` | json | Additional metadata about the operation, such as timestamps, request status, or other relevant information. |
|
||||
|
||||
### `microsoft_planner_create_task`
|
||||
|
||||
@@ -155,6 +155,7 @@ Create a new task in Microsoft Planner
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Planner API |
|
||||
| `planId` | string | Yes | The ID of the plan where the task will be created |
|
||||
| `title` | string | Yes | The title of the task |
|
||||
| `description` | string | No | The description of the task |
|
||||
@@ -166,9 +167,8 @@ Create a new task in Microsoft Planner
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the task was created successfully |
|
||||
| `task` | object | The created task object with all properties |
|
||||
| `metadata` | object | Metadata including planId, taskId, and taskUrl |
|
||||
| `task` | json | The Microsoft Planner task object, including details such as id, title, description, status, due date, and assignees. |
|
||||
| `metadata` | json | Additional metadata about the operation, such as timestamps, request status, or other relevant information. |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -112,19 +112,16 @@ Read content from a Microsoft Teams chat
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Teams API |
|
||||
| `chatId` | string | Yes | The ID of the chat to read from |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Teams chat read operation success status |
|
||||
| `messageCount` | number | Number of messages retrieved from chat |
|
||||
| `chatId` | string | ID of the chat that was read from |
|
||||
| `messages` | array | Array of chat message objects |
|
||||
| `attachmentCount` | number | Total number of attachments found |
|
||||
| `attachmentTypes` | array | Types of attachments found |
|
||||
| `content` | string | Formatted content of chat messages |
|
||||
| `content` | string | Message content |
|
||||
| `metadata` | json | Message metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
### `microsoft_teams_write_chat`
|
||||
|
||||
@@ -134,6 +131,7 @@ Write or update content in a Microsoft Teams chat
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Teams API |
|
||||
| `chatId` | string | Yes | The ID of the chat to write to |
|
||||
| `content` | string | Yes | The content to write to the message |
|
||||
|
||||
@@ -141,12 +139,9 @@ Write or update content in a Microsoft Teams chat
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Teams chat message send success status |
|
||||
| `messageId` | string | Unique identifier for the sent message |
|
||||
| `chatId` | string | ID of the chat where message was sent |
|
||||
| `createdTime` | string | Timestamp when message was created |
|
||||
| `url` | string | Web URL to the message |
|
||||
| `updatedContent` | boolean | Whether content was successfully updated |
|
||||
| `content` | string | Message content |
|
||||
| `metadata` | json | Message metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
### `microsoft_teams_read_channel`
|
||||
|
||||
@@ -156,6 +151,7 @@ Read content from a Microsoft Teams channel
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Teams API |
|
||||
| `teamId` | string | Yes | The ID of the team to read from |
|
||||
| `channelId` | string | Yes | The ID of the channel to read from |
|
||||
|
||||
@@ -163,14 +159,9 @@ Read content from a Microsoft Teams channel
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Teams channel read operation success status |
|
||||
| `messageCount` | number | Number of messages retrieved from channel |
|
||||
| `teamId` | string | ID of the team that was read from |
|
||||
| `channelId` | string | ID of the channel that was read from |
|
||||
| `messages` | array | Array of channel message objects |
|
||||
| `attachmentCount` | number | Total number of attachments found |
|
||||
| `attachmentTypes` | array | Types of attachments found |
|
||||
| `content` | string | Formatted content of channel messages |
|
||||
| `content` | string | Message content |
|
||||
| `metadata` | json | Message metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
### `microsoft_teams_write_channel`
|
||||
|
||||
@@ -180,6 +171,7 @@ Write or send a message to a Microsoft Teams channel
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Microsoft Teams API |
|
||||
| `teamId` | string | Yes | The ID of the team to write to |
|
||||
| `channelId` | string | Yes | The ID of the channel to write to |
|
||||
| `content` | string | Yes | The content to write to the channel |
|
||||
@@ -188,13 +180,9 @@ Write or send a message to a Microsoft Teams channel
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Teams channel message send success status |
|
||||
| `messageId` | string | Unique identifier for the sent message |
|
||||
| `teamId` | string | ID of the team where message was sent |
|
||||
| `channelId` | string | ID of the channel where message was sent |
|
||||
| `createdTime` | string | Timestamp when message was created |
|
||||
| `url` | string | Web URL to the message |
|
||||
| `updatedContent` | boolean | Whether content was successfully updated |
|
||||
| `content` | string | Message content |
|
||||
| `metadata` | json | Message metadata |
|
||||
| `updatedContent` | boolean | Content update status |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -106,9 +106,8 @@ Parse PDF documents using Mistral OCR API
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the PDF was parsed successfully |
|
||||
| `content` | string | Extracted content in the requested format \(markdown, text, or JSON\) |
|
||||
| `metadata` | object | Processing metadata including jobId, fileType, pageCount, and usage info |
|
||||
| `content` | string | Extracted content |
|
||||
| `metadata` | json | Processing metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
---
|
||||
title: MySQL
|
||||
description: Connect to MySQL database
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="mysql"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
|
||||
|
||||
viewBox='0 0 25.6 25.6'
|
||||
>
|
||||
<path
|
||||
d='M179.076 94.886c-3.568-.1-6.336.268-8.656 1.25-.668.27-1.74.27-1.828 1.116.357.355.4.936.713 1.428.535.893 1.473 2.096 2.32 2.72l2.855 2.053c1.74 1.07 3.703 1.695 5.398 2.766.982.625 1.963 1.428 2.945 2.098.5.357.803.938 1.428 1.16v-.135c-.312-.4-.402-.98-.713-1.428l-1.34-1.293c-1.293-1.74-2.9-3.258-4.64-4.506-1.428-.982-4.55-2.32-5.13-3.97l-.088-.1c.98-.1 2.14-.447 3.078-.715 1.518-.4 2.9-.312 4.46-.713l2.143-.625v-.4c-.803-.803-1.383-1.874-2.23-2.632-2.275-1.963-4.775-3.882-7.363-5.488-1.383-.892-3.168-1.473-4.64-2.23-.537-.268-1.428-.402-1.74-.848-.805-.98-1.25-2.275-1.83-3.436l-3.658-7.763c-.803-1.74-1.295-3.48-2.275-5.086-4.596-7.585-9.594-12.18-17.268-16.687-1.65-.937-3.613-1.34-5.7-1.83l-3.346-.18c-.715-.312-1.428-1.16-2.053-1.562-2.543-1.606-9.102-5.086-10.977-.5-1.205 2.9 1.785 5.755 2.8 7.228.76 1.026 1.74 2.186 2.277 3.346.3.758.4 1.562.713 2.365.713 1.963 1.383 4.15 2.32 5.98.5.937 1.025 1.92 1.65 2.767.357.5.982.714 1.115 1.517-.625.893-.668 2.23-1.025 3.347-1.607 5.042-.982 11.288 1.293 15 .715 1.115 2.4 3.57 4.686 2.632 2.008-.803 1.56-3.346 2.14-5.577.135-.535.045-.892.312-1.25v.1l1.83 3.703c1.383 2.186 3.793 4.462 5.8 5.98 1.07.803 1.918 2.187 3.256 2.677v-.135h-.088c-.268-.4-.67-.58-1.027-.892-.803-.803-1.695-1.785-2.32-2.677-1.873-2.498-3.523-5.265-4.996-8.12-.715-1.383-1.34-2.9-1.918-4.283-.27-.536-.27-1.34-.715-1.606-.67.98-1.65 1.83-2.143 3.034-.848 1.918-.936 4.283-1.248 6.737-.18.045-.1 0-.18.1-1.426-.356-1.918-1.83-2.453-3.078-1.338-3.168-1.562-8.254-.402-11.913.312-.937 1.652-3.882 1.117-4.774-.27-.848-1.16-1.338-1.652-2.008-.58-.848-1.203-1.918-1.605-2.855-1.07-2.5-1.605-5.265-2.766-7.764-.537-1.16-1.473-2.365-2.232-3.435-.848-1.205-1.783-2.053-2.453-3.48-.223-.5-.535-1.294-.178-1.83.088-.357.268-.5.623-.58.58-.5 2.232.134 2.812.4 1.65.67 3.033 1.294 4.416 2.23.625.446 1.295 1.294 2.098 1.518h.938c1.428.312 3.033.1 4.37.5 2.365.76 4.506 1.874 6.426 3.08 5.844 3.703 10.664 8.968 13.92 15.26.535 1.026.758 1.963 1.25 3.034.938 2.187 2.098 4.417 3.033 6.56.938 2.097 1.83 4.24 3.168 5.98.67.937 3.346 1.427 4.55 1.918.893.4 2.275.76 3.08 1.25 1.516.937 3.033 2.008 4.46 3.034.713.534 2.945 1.65 3.078 2.54zm-45.5-38.772a7.09 7.09 0 0 0-1.828.223v.1h.088c.357.714.982 1.205 1.428 1.83l1.027 2.142.088-.1c.625-.446.938-1.16.938-2.23-.268-.312-.312-.625-.535-.937-.268-.446-.848-.67-1.206-1.026z'
|
||||
transform='matrix(.390229 0 0 .38781 -46.300037 -16.856717)'
|
||||
fillRule='evenodd'
|
||||
fill='#00678c'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
The [MySQL](https://www.mysql.com/) tool enables you to connect to any MySQL database and perform a wide range of database operations directly within your agentic workflows. With secure connection handling and flexible configuration, you can easily manage and interact with your data.
|
||||
|
||||
With the MySQL tool, you can:
|
||||
|
||||
- **Query data**: Execute SELECT queries to retrieve data from your MySQL tables using the `mysql_query` operation.
|
||||
- **Insert records**: Add new rows to your tables with the `mysql_insert` operation by specifying the table and data to insert.
|
||||
- **Update records**: Modify existing data in your tables using the `mysql_update` operation, providing the table, new data, and WHERE conditions.
|
||||
- **Delete records**: Remove rows from your tables with the `mysql_delete` operation, specifying the table and WHERE conditions.
|
||||
- **Execute raw SQL**: Run any custom SQL command using the `mysql_execute` operation for advanced use cases.
|
||||
|
||||
The MySQL tool is ideal for scenarios where your agents need to interact with structured data—such as automating reporting, syncing data between systems, or powering data-driven workflows. It streamlines database access, making it easy to read, write, and manage your MySQL data programmatically.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Connect to any MySQL database to execute queries, manage data, and perform database operations. Supports SELECT, INSERT, UPDATE, DELETE operations with secure connection handling.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `mysql_query`
|
||||
|
||||
Execute SELECT query on MySQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | MySQL server hostname or IP address |
|
||||
| `port` | number | Yes | MySQL server port \(default: 3306\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `query` | string | Yes | SQL SELECT query to execute |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of rows returned from the query |
|
||||
| `rowCount` | number | Number of rows returned |
|
||||
|
||||
### `mysql_insert`
|
||||
|
||||
Insert new record into MySQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | MySQL server hostname or IP address |
|
||||
| `port` | number | Yes | MySQL server port \(default: 3306\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to insert into |
|
||||
| `data` | object | Yes | Data to insert as key-value pairs |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of inserted rows |
|
||||
| `rowCount` | number | Number of rows inserted |
|
||||
|
||||
### `mysql_update`
|
||||
|
||||
Update existing records in MySQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | MySQL server hostname or IP address |
|
||||
| `port` | number | Yes | MySQL server port \(default: 3306\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to update |
|
||||
| `data` | object | Yes | Data to update as key-value pairs |
|
||||
| `where` | string | Yes | WHERE clause condition \(without WHERE keyword\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of updated rows |
|
||||
| `rowCount` | number | Number of rows updated |
|
||||
|
||||
### `mysql_delete`
|
||||
|
||||
Delete records from MySQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | MySQL server hostname or IP address |
|
||||
| `port` | number | Yes | MySQL server port \(default: 3306\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to delete from |
|
||||
| `where` | string | Yes | WHERE clause condition \(without WHERE keyword\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of deleted rows |
|
||||
| `rowCount` | number | Number of rows deleted |
|
||||
|
||||
### `mysql_execute`
|
||||
|
||||
Execute raw SQL query on MySQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | MySQL server hostname or IP address |
|
||||
| `port` | number | Yes | MySQL server port \(default: 3306\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `query` | string | Yes | Raw SQL query to execute |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of rows returned from the query |
|
||||
| `rowCount` | number | Number of rows affected |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `mysql`
|
||||
@@ -59,14 +59,15 @@ Read content from a Notion page
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `pageId` | string | Yes | The ID of the Notion page to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Page content in markdown format with headers, paragraphs, lists, and todos |
|
||||
| `metadata` | object | Page metadata including title, URL, and timestamps |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_read_database`
|
||||
|
||||
@@ -76,14 +77,15 @@ Read database information and structure from Notion
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `databaseId` | string | Yes | The ID of the Notion database to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Database information including title, properties schema, and metadata |
|
||||
| `metadata` | object | Database metadata including title, ID, URL, timestamps, and properties schema |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_write`
|
||||
|
||||
@@ -93,6 +95,7 @@ Append content to a Notion page
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `pageId` | string | Yes | The ID of the Notion page to append content to |
|
||||
| `content` | string | Yes | The content to append to the page |
|
||||
|
||||
@@ -100,7 +103,8 @@ Append content to a Notion page
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Success message confirming content was appended to page |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_create_page`
|
||||
|
||||
@@ -110,6 +114,7 @@ Create a new page in Notion
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `parentId` | string | Yes | ID of the parent page |
|
||||
| `title` | string | No | Title of the new page |
|
||||
| `content` | string | No | Optional content to add to the page upon creation |
|
||||
@@ -118,8 +123,8 @@ Create a new page in Notion
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Success message confirming page creation |
|
||||
| `metadata` | object | Page metadata including title, page ID, URL, and timestamps |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_query_database`
|
||||
|
||||
@@ -129,6 +134,7 @@ Query and filter Notion database entries with advanced filtering
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `databaseId` | string | Yes | The ID of the database to query |
|
||||
| `filter` | string | No | Filter conditions as JSON \(optional\) |
|
||||
| `sorts` | string | No | Sort criteria as JSON array \(optional\) |
|
||||
@@ -138,8 +144,8 @@ Query and filter Notion database entries with advanced filtering
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Formatted list of database entries with their properties |
|
||||
| `metadata` | object | Query metadata including total results count, pagination info, and raw results array |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_search`
|
||||
|
||||
@@ -149,6 +155,7 @@ Search across all pages and databases in Notion workspace
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `query` | string | No | Search terms \(leave empty to get all pages\) |
|
||||
| `filterType` | string | No | Filter by object type: page, database, or leave empty for all |
|
||||
| `pageSize` | number | No | Number of results to return \(default: 100, max: 100\) |
|
||||
@@ -157,8 +164,8 @@ Search across all pages and databases in Notion workspace
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Formatted list of search results including pages and databases |
|
||||
| `metadata` | object | Search metadata including total results count, pagination info, and raw results array |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
### `notion_create_database`
|
||||
|
||||
@@ -168,16 +175,17 @@ Create a new database in Notion with custom properties
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Notion OAuth access token |
|
||||
| `parentId` | string | Yes | ID of the parent page where the database will be created |
|
||||
| `title` | string | Yes | Title for the new database |
|
||||
| `properties` | string | No | Database properties as JSON object \(optional, will create a default "Name" property if empty\) |
|
||||
| `properties` | string | No | Database properties as JSON object \(optional, will create a default |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Success message with database details and properties list |
|
||||
| `metadata` | object | Database metadata including ID, title, URL, creation time, and properties schema |
|
||||
| `content` | string | Page content |
|
||||
| `metadata` | any | Page metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -65,17 +65,18 @@ Upload a file to OneDrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the OneDrive API |
|
||||
| `fileName` | string | Yes | The name of the file to upload |
|
||||
| `content` | string | Yes | The content of the file to upload |
|
||||
| `folderSelector` | string | No | Select the folder to upload the file to |
|
||||
| `manualFolderId` | string | No | Manually entered folder ID \(advanced mode\) |
|
||||
| `folderId` | string | No | The ID of the folder to upload the file to \(internal use\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the file was uploaded successfully |
|
||||
| `file` | object | The uploaded file object with metadata including id, name, webViewLink, webContentLink, and timestamps |
|
||||
| `file` | json | The OneDrive file object, including details such as id, name, size, and more. |
|
||||
| `files` | json | An array of OneDrive file objects, each containing details such as id, name, size, and more. |
|
||||
|
||||
### `onedrive_create_folder`
|
||||
|
||||
@@ -85,16 +86,17 @@ Create a new folder in OneDrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the OneDrive API |
|
||||
| `folderName` | string | Yes | Name of the folder to create |
|
||||
| `folderSelector` | string | No | Select the parent folder to create the folder in |
|
||||
| `manualFolderId` | string | No | Manually entered parent folder ID \(advanced mode\) |
|
||||
| `folderId` | string | No | ID of the parent folder \(internal use\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the folder was created successfully |
|
||||
| `file` | object | The created folder object with metadata including id, name, webViewLink, and timestamps |
|
||||
| `file` | json | The OneDrive file object, including details such as id, name, size, and more. |
|
||||
| `files` | json | An array of OneDrive file objects, each containing details such as id, name, size, and more. |
|
||||
|
||||
### `onedrive_list`
|
||||
|
||||
@@ -104,8 +106,9 @@ List files and folders in OneDrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the OneDrive API |
|
||||
| `folderSelector` | string | No | Select the folder to list files from |
|
||||
| `manualFolderId` | string | No | The manually entered folder ID \(advanced mode\) |
|
||||
| `folderId` | string | No | The ID of the folder to list files from \(internal use\) |
|
||||
| `query` | string | No | A query to filter the files |
|
||||
| `pageSize` | number | No | The number of files to return |
|
||||
|
||||
@@ -113,9 +116,8 @@ List files and folders in OneDrive
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether files were listed successfully |
|
||||
| `files` | array | Array of file and folder objects with metadata |
|
||||
| `nextPageToken` | string | Token for retrieving the next page of results \(optional\) |
|
||||
| `file` | json | The OneDrive file object, including details such as id, name, size, and more. |
|
||||
| `files` | json | An array of OneDrive file objects, each containing details such as id, name, size, and more. |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,8 +66,9 @@ Generate embeddings from text using OpenAI
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Embeddings generation results |
|
||||
| `embeddings` | json | Generated embeddings |
|
||||
| `model` | string | Model used |
|
||||
| `usage` | json | Token usage |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@ Send emails using Outlook
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Outlook API |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
@@ -166,10 +167,8 @@ Send emails using Outlook
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Email send success status |
|
||||
| `status` | string | Delivery status of the email |
|
||||
| `timestamp` | string | Timestamp when email was sent |
|
||||
| `message` | string | Success or error message |
|
||||
| `message` | string | Response message |
|
||||
| `results` | json | Email results |
|
||||
|
||||
### `outlook_draft`
|
||||
|
||||
@@ -179,22 +178,17 @@ Draft emails using Outlook
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Outlook API |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
| `bcc` | string | No | BCC recipients \(comma-separated\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Email draft creation success status |
|
||||
| `messageId` | string | Unique identifier for the drafted email |
|
||||
| `status` | string | Draft status of the email |
|
||||
| `subject` | string | Subject of the drafted email |
|
||||
| `timestamp` | string | Timestamp when draft was created |
|
||||
| `message` | string | Success or error message |
|
||||
| `message` | string | Response message |
|
||||
| `results` | json | Email results |
|
||||
|
||||
### `outlook_read`
|
||||
|
||||
@@ -204,6 +198,7 @@ Read emails from Outlook
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | OAuth access token for Outlook |
|
||||
| `folder` | string | No | Folder ID to read emails from \(default: Inbox\) |
|
||||
| `maxResults` | number | No | Maximum number of emails to retrieve \(default: 1, max: 10\) |
|
||||
|
||||
@@ -211,27 +206,8 @@ Read emails from Outlook
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or status message |
|
||||
| `results` | array | Array of email message objects |
|
||||
|
||||
### `outlook_forward`
|
||||
|
||||
Forward an existing Outlook message to specified recipients
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `messageId` | string | Yes | The ID of the message to forward |
|
||||
| `to` | string | Yes | Recipient email address\(es\), comma-separated |
|
||||
| `comment` | string | No | Optional comment to include with the forwarded message |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success or error message |
|
||||
| `results` | object | Delivery result details |
|
||||
| `message` | string | Response message |
|
||||
| `results` | json | Email results |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
title: Parallel AI
|
||||
description: Search with Parallel AI
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="parallel_ai"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
fill='currentColor'
|
||||
|
||||
|
||||
viewBox='0 0 271 270'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
d='M267.804 105.65H193.828C194.026 106.814 194.187 107.996 194.349 109.178H76.6703C76.4546 110.736 76.2388 112.312 76.0591 113.87H1.63342C1.27387 116.198 0.950289 118.543 0.698608 120.925H75.3759C75.2501 122.483 75.1602 124.059 75.0703 125.617H195.949C196.003 126.781 196.057 127.962 196.093 129.144H270.68V125.384C270.195 118.651 269.242 112.061 267.804 105.65Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M195.949 144.401H75.0703C75.1422 145.977 75.2501 147.535 75.3759 149.093H0.698608C0.950289 151.457 1.2559 153.802 1.63342 156.148H76.0591C76.2388 157.724 76.4366 159.282 76.6703 160.84H194.349C194.187 162.022 194.008 163.186 193.828 164.367H267.804C269.242 157.957 270.195 151.367 270.68 144.634V140.874H196.093C196.057 142.055 196.003 143.219 195.949 144.401Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M190.628 179.642H80.3559C80.7514 181.218 81.1828 182.776 81.6143 184.334H9.30994C10.2448 186.715 11.2515 189.061 12.3121 191.389H83.7536C84.2749 192.965 84.7962 194.523 85.3535 196.08H185.594C185.163 197.262 184.732 198.426 184.282 199.608H254.519C258.6 192.177 261.98 184.316 264.604 176.114H191.455C191.185 177.296 190.898 178.46 190.61 179.642H190.628Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M177.666 214.883H93.3352C94.1082 216.458 94.9172 218.034 95.7441 219.574H29.8756C31.8351 221.992 33.8666 224.337 35.9699 226.63H99.6632C100.598 228.205 101.551 229.781 102.522 231.321H168.498C167.761 232.503 167.006 233.685 166.233 234.849H226.762C234.474 227.847 241.36 219.95 247.292 211.355H179.356C178.799 212.537 178.26 213.719 177.684 214.883H177.666Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M154.943 250.106H116.058C117.371 251.699 118.701 253.257 120.067 254.797H73.021C91.6094 264.431 112.715 269.946 135.096 270C135.24 270 135.366 270 135.492 270C135.618 270 135.761 270 135.887 270C164.04 269.911 190.178 261.28 211.805 246.56H157.748C156.813 247.742 155.878 248.924 154.925 250.088L154.943 250.106Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M116.059 19.9124H154.943C155.896 21.0764 156.831 22.2582 157.766 23.4401H211.823C190.179 8.72065 164.058 0.0895344 135.906 0C135.762 0 135.636 0 135.51 0C135.384 0 135.24 0 135.115 0C112.715 0.0716275 91.6277 5.56904 73.0393 15.2029H120.086C118.719 16.7429 117.389 18.3187 116.077 19.8945L116.059 19.9124Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M93.3356 55.1532H177.667C178.242 56.3171 178.799 57.499 179.339 58.6808H247.274C241.342 50.0855 234.457 42.1886 226.744 35.187H166.215C166.988 36.351 167.743 37.5328 168.48 38.7147H102.504C101.533 40.2726 100.58 41.8305 99.6456 43.4063H35.9523C33.831 45.6804 31.7996 48.0262 29.858 50.4616H95.7265C94.8996 52.0195 94.1086 53.5774 93.3176 55.1532H93.3356Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
<path
|
||||
d='M80.3736 90.3758H190.646C190.933 91.5398 191.221 92.7216 191.491 93.9035H264.64C262.015 85.7021 258.636 77.841 254.555 70.4097H184.318C184.767 71.5736 185.199 72.7555 185.63 73.9373H85.3893C84.832 75.4952 84.2927 77.0531 83.7893 78.6289H12.3479C11.2872 80.9389 10.2805 83.2847 9.3457 85.6842H81.65C81.2186 87.2421 80.7871 88.8 80.3916 90.3758H80.3736Z'
|
||||
fill='#1D1C1A'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Parallel AI](https://parallel.ai/) is an advanced web search and content extraction platform designed to deliver comprehensive, high-quality results for any query. By leveraging intelligent processing and large-scale data extraction, Parallel AI enables users and agents to access, analyze, and synthesize information from across the web with speed and accuracy.
|
||||
|
||||
With Parallel AI, you can:
|
||||
|
||||
- **Search the web intelligently**: Retrieve relevant, up-to-date information from a wide range of sources
|
||||
- **Extract and summarize content**: Get concise, meaningful excerpts from web pages and documents
|
||||
- **Customize search objectives**: Tailor queries to specific needs or questions for targeted results
|
||||
- **Process results at scale**: Handle large volumes of search results with advanced processing options
|
||||
- **Integrate with workflows**: Use Parallel AI within Sim to automate research, content gathering, and knowledge extraction
|
||||
- **Control output granularity**: Specify the number of results and the amount of content per result
|
||||
- **Secure API access**: Protect your searches and data with API key authentication
|
||||
|
||||
In Sim, the Parallel AI integration empowers your agents to perform web searches and extract content programmatically. This enables powerful automation scenarios such as real-time research, competitive analysis, content monitoring, and knowledge base creation. By connecting Sim with Parallel AI, you unlock the ability for agents to gather, process, and utilize web data as part of your automated workflows.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Search the web using Parallel AI's advanced search capabilities. Get comprehensive results with intelligent processing and content extraction.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `parallel_search`
|
||||
|
||||
Search the web using Parallel AI. Provides comprehensive search results with intelligent processing and content extraction.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `objective` | string | Yes | The search objective or question to answer |
|
||||
| `search_queries` | string | No | Optional comma-separated list of search queries to execute |
|
||||
| `processor` | string | No | Processing method: base or pro \(default: base\) |
|
||||
| `max_results` | number | No | Maximum number of results to return \(default: 5\) |
|
||||
| `max_chars_per_result` | number | No | Maximum characters per result \(default: 1500\) |
|
||||
| `apiKey` | string | Yes | Parallel AI API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Search results with excerpts from relevant pages |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `parallel_ai`
|
||||
@@ -62,8 +62,9 @@ Generate completions using Perplexity AI chat models
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Chat completion results |
|
||||
| `content` | string | Generated response |
|
||||
| `model` | string | Model used |
|
||||
| `usage` | json | Token usage |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -67,10 +67,12 @@ Generate embeddings from text using Pinecone
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Generated embeddings data with values and vector type |
|
||||
| `model` | string | Model used for generating embeddings |
|
||||
| `vector_type` | string | Type of vector generated \(dense/sparse\) |
|
||||
| `usage` | object | Usage statistics for embeddings generation |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `model` | any | Model information |
|
||||
| `vector_type` | any | Vector type |
|
||||
| `usage` | any | Usage statistics |
|
||||
|
||||
### `pinecone_upsert_text`
|
||||
|
||||
@@ -89,8 +91,12 @@ Insert or update text records in a Pinecone index
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `statusText` | string | Status of the upsert operation |
|
||||
| `upsertedCount` | number | Number of records successfully upserted |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `model` | any | Model information |
|
||||
| `vector_type` | any | Vector type |
|
||||
| `usage` | any | Usage statistics |
|
||||
|
||||
### `pinecone_search_text`
|
||||
|
||||
@@ -113,7 +119,12 @@ Search for similar text in a Pinecone index
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `matches` | array | Search results with ID, score, and metadata |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `model` | any | Model information |
|
||||
| `vector_type` | any | Vector type |
|
||||
| `usage` | any | Usage statistics |
|
||||
|
||||
### `pinecone_search_vector`
|
||||
|
||||
@@ -136,8 +147,12 @@ Search for similar vectors in a Pinecone index
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `matches` | array | Vector search results with ID, score, values, and metadata |
|
||||
| `namespace` | string | Namespace where the search was performed |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `model` | any | Model information |
|
||||
| `vector_type` | any | Vector type |
|
||||
| `usage` | any | Usage statistics |
|
||||
|
||||
### `pinecone_fetch`
|
||||
|
||||
@@ -156,7 +171,12 @@ Fetch vectors by ID from a Pinecone index
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `matches` | array | Fetched vectors with ID, values, metadata, and score |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `model` | any | Model information |
|
||||
| `vector_type` | any | Vector type |
|
||||
| `usage` | any | Usage statistics |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
title: PostgreSQL
|
||||
description: Connect to PostgreSQL database
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="postgresql"
|
||||
color="#336791"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='-4 0 264 264'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
preserveAspectRatio='xMinYMin meet'
|
||||
>
|
||||
<path d='M255.008 158.086c-1.535-4.649-5.556-7.887-10.756-8.664-2.452-.366-5.26-.21-8.583.475-5.792 1.195-10.089 1.65-13.225 1.738 11.837-19.985 21.462-42.775 27.003-64.228 8.96-34.689 4.172-50.492-1.423-57.64C233.217 10.847 211.614.683 185.552.372c-13.903-.17-26.108 2.575-32.475 4.549-5.928-1.046-12.302-1.63-18.99-1.738-12.537-.2-23.614 2.533-33.079 8.15-5.24-1.772-13.65-4.27-23.362-5.864-22.842-3.75-41.252-.828-54.718 8.685C6.622 25.672-.937 45.684.461 73.634c.444 8.874 5.408 35.874 13.224 61.48 4.492 14.718 9.282 26.94 14.237 36.33 7.027 13.315 14.546 21.156 22.987 23.972 4.731 1.576 13.327 2.68 22.368-4.85 1.146 1.388 2.675 2.767 4.704 4.048 2.577 1.625 5.728 2.953 8.875 3.74 11.341 2.835 21.964 2.126 31.027-1.848.056 1.612.099 3.152.135 4.482.06 2.157.12 4.272.199 6.25.537 13.374 1.447 23.773 4.143 31.049.148.4.347 1.01.557 1.657 1.345 4.118 3.594 11.012 9.316 16.411 5.925 5.593 13.092 7.308 19.656 7.308 3.292 0 6.433-.432 9.188-1.022 9.82-2.105 20.973-5.311 29.041-16.799 7.628-10.86 11.336-27.217 12.007-52.99.087-.729.167-1.425.244-2.088l.16-1.362 1.797.158.463.031c10.002.456 22.232-1.665 29.743-5.154 5.935-2.754 24.954-12.795 20.476-26.351' />
|
||||
<path
|
||||
d='M237.906 160.722c-29.74 6.135-31.785-3.934-31.785-3.934 31.4-46.593 44.527-105.736 33.2-120.211-30.904-39.485-84.399-20.811-85.292-20.327l-.287.052c-5.876-1.22-12.451-1.946-19.842-2.067-13.456-.22-23.664 3.528-31.41 9.402 0 0-95.43-39.314-90.991 49.444.944 18.882 27.064 142.873 58.218 105.422 11.387-13.695 22.39-25.274 22.39-25.274 5.464 3.63 12.006 5.482 18.864 4.817l.533-.452c-.166 1.7-.09 3.363.213 5.332-8.026 8.967-5.667 10.541-21.711 13.844-16.235 3.346-6.698 9.302-.471 10.86 7.549 1.887 25.013 4.561 36.813-11.958l-.47 1.885c3.144 2.519 5.352 16.383 4.982 28.952-.37 12.568-.617 21.197 1.86 27.937 2.479 6.74 4.948 21.905 26.04 17.386 17.623-3.777 26.756-13.564 28.027-29.89.901-11.606 2.942-9.89 3.07-20.267l1.637-4.912c1.887-15.733.3-20.809 11.157-18.448l2.64.232c7.99.363 18.45-1.286 24.589-4.139 13.218-6.134 21.058-16.377 8.024-13.686h.002'
|
||||
fill='#336791'
|
||||
/>
|
||||
<path
|
||||
d='M108.076 81.525c-2.68-.373-5.107-.028-6.335.902-.69.523-.904 1.129-.962 1.546-.154 1.105.62 2.327 1.096 2.957 1.346 1.784 3.312 3.01 5.258 3.28.282.04.563.058.842.058 3.245 0 6.196-2.527 6.456-4.392.325-2.336-3.066-3.893-6.355-4.35M196.86 81.599c-.256-1.831-3.514-2.353-6.606-1.923-3.088.43-6.082 1.824-5.832 3.659.2 1.427 2.777 3.863 5.827 3.863.258 0 .518-.017.78-.054 2.036-.282 3.53-1.575 4.24-2.32 1.08-1.136 1.706-2.402 1.591-3.225'
|
||||
fill='#FFF'
|
||||
/>
|
||||
<path
|
||||
d='M247.802 160.025c-1.134-3.429-4.784-4.532-10.848-3.28-18.005 3.716-24.453 1.142-26.57-.417 13.995-21.32 25.508-47.092 31.719-71.137 2.942-11.39 4.567-21.968 4.7-30.59.147-9.463-1.465-16.417-4.789-20.665-13.402-17.125-33.072-26.311-56.882-26.563-16.369-.184-30.199 4.005-32.88 5.183-5.646-1.404-11.801-2.266-18.502-2.376-12.288-.199-22.91 2.743-31.704 8.74-3.82-1.422-13.692-4.811-25.765-6.756-20.872-3.36-37.458-.814-49.294 7.571-14.123 10.006-20.643 27.892-19.38 53.16.425 8.501 5.269 34.653 12.913 59.698 10.062 32.964 21 51.625 32.508 55.464 1.347.449 2.9.763 4.613.763 4.198 0 9.345-1.892 14.7-8.33a529.832 529.832 0 0 1 20.261-22.926c4.524 2.428 9.494 3.784 14.577 3.92.01.133.023.266.035.398a117.66 117.66 0 0 0-2.57 3.175c-3.522 4.471-4.255 5.402-15.592 7.736-3.225.666-11.79 2.431-11.916 8.435-.136 6.56 10.125 9.315 11.294 9.607 4.074 1.02 7.999 1.523 11.742 1.523 9.103 0 17.114-2.992 23.516-8.781-.197 23.386.778 46.43 3.586 53.451 2.3 5.748 7.918 19.795 25.664 19.794 2.604 0 5.47-.303 8.623-.979 18.521-3.97 26.564-12.156 29.675-30.203 1.665-9.645 4.522-32.676 5.866-45.03 2.836.885 6.487 1.29 10.434 1.289 8.232 0 17.731-1.749 23.688-4.514 6.692-3.108 18.768-10.734 16.578-17.36zm-44.106-83.48c-.061 3.647-.563 6.958-1.095 10.414-.573 3.717-1.165 7.56-1.314 12.225-.147 4.54.42 9.26.968 13.825 1.108 9.22 2.245 18.712-2.156 28.078a36.508 36.508 0 0 1-1.95-4.009c-.547-1.326-1.735-3.456-3.38-6.404-6.399-11.476-21.384-38.35-13.713-49.316 2.285-3.264 8.084-6.62 22.64-4.813zm-17.644-61.787c21.334.471 38.21 8.452 50.158 23.72 9.164 11.711-.927 64.998-30.14 110.969a171.33 171.33 0 0 0-.886-1.117l-.37-.462c7.549-12.467 6.073-24.802 4.759-35.738-.54-4.488-1.05-8.727-.92-12.709.134-4.22.692-7.84 1.232-11.34.663-4.313 1.338-8.776 1.152-14.037.139-.552.195-1.204.122-1.978-.475-5.045-6.235-20.144-17.975-33.81-6.422-7.475-15.787-15.84-28.574-21.482 5.5-1.14 13.021-2.203 21.442-2.016zM66.674 175.778c-5.9 7.094-9.974 5.734-11.314 5.288-8.73-2.912-18.86-21.364-27.791-50.624-7.728-25.318-12.244-50.777-12.602-57.916-1.128-22.578 4.345-38.313 16.268-46.769 19.404-13.76 51.306-5.524 64.125-1.347-.184.182-.376.352-.558.537-21.036 21.244-20.537 57.54-20.485 59.759-.002.856.07 2.068.168 3.735.362 6.105 1.036 17.467-.764 30.334-1.672 11.957 2.014 23.66 10.111 32.109a36.275 36.275 0 0 0 2.617 2.468c-3.604 3.86-11.437 12.396-19.775 22.426zm22.479-29.993c-6.526-6.81-9.49-16.282-8.133-25.99 1.9-13.592 1.199-25.43.822-31.79-.053-.89-.1-1.67-.127-2.285 3.073-2.725 17.314-10.355 27.47-8.028 4.634 1.061 7.458 4.217 8.632 9.645 6.076 28.103.804 39.816-3.432 49.229-.873 1.939-1.698 3.772-2.402 5.668l-.546 1.466c-1.382 3.706-2.668 7.152-3.465 10.424-6.938-.02-13.687-2.984-18.819-8.34zm1.065 37.9c-2.026-.506-3.848-1.385-4.917-2.114.893-.42 2.482-.992 5.238-1.56 13.337-2.745 15.397-4.683 19.895-10.394 1.031-1.31 2.2-2.794 3.819-4.602l.002-.002c2.411-2.7 3.514-2.242 5.514-1.412 1.621.67 3.2 2.702 3.84 4.938.303 1.056.643 3.06-.47 4.62-9.396 13.156-23.088 12.987-32.921 10.526zm69.799 64.952c-16.316 3.496-22.093-4.829-25.9-14.346-2.457-6.144-3.665-33.85-2.808-64.447.011-.407-.047-.8-.159-1.17a15.444 15.444 0 0 0-.456-2.162c-1.274-4.452-4.379-8.176-8.104-9.72-1.48-.613-4.196-1.738-7.46-.903.696-2.868 1.903-6.107 3.212-9.614l.549-1.475c.618-1.663 1.394-3.386 2.214-5.21 4.433-9.848 10.504-23.337 3.915-53.81-2.468-11.414-10.71-16.988-23.204-15.693-7.49.775-14.343 3.797-17.761 5.53-.735.372-1.407.732-2.035 1.082.954-11.5 4.558-32.992 18.04-46.59 8.489-8.56 19.794-12.788 33.568-12.56 27.14.444 44.544 14.372 54.366 25.979 8.464 10.001 13.047 20.076 14.876 25.51-13.755-1.399-23.11 1.316-27.852 8.096-10.317 14.748 5.644 43.372 13.315 57.129 1.407 2.521 2.621 4.7 3.003 5.626 2.498 6.054 5.732 10.096 8.093 13.046.724.904 1.426 1.781 1.96 2.547-4.166 1.201-11.649 3.976-10.967 17.847-.55 6.96-4.461 39.546-6.448 51.059-2.623 15.21-8.22 20.875-23.957 24.25zm68.104-77.936c-4.26 1.977-11.389 3.46-18.161 3.779-7.48.35-11.288-.838-12.184-1.569-.42-8.644 2.797-9.547 6.202-10.503.535-.15 1.057-.297 1.561-.473.313.255.656.508 1.032.756 6.012 3.968 16.735 4.396 31.874 1.271l.166-.033c-2.042 1.909-5.536 4.471-10.49 6.772z'
|
||||
fill='#FFF'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
The [PostgreSQL](https://www.postgresql.org/) tool enables you to connect to any PostgreSQL database and perform a wide range of database operations directly within your agentic workflows. With secure connection handling and flexible configuration, you can easily manage and interact with your data.
|
||||
|
||||
With the PostgreSQL tool, you can:
|
||||
|
||||
- **Query data**: Execute SELECT queries to retrieve data from your PostgreSQL tables using the `postgresql_query` operation.
|
||||
- **Insert records**: Add new rows to your tables with the `postgresql_insert` operation by specifying the table and data to insert.
|
||||
- **Update records**: Modify existing data in your tables using the `postgresql_update` operation, providing the table, new data, and WHERE conditions.
|
||||
- **Delete records**: Remove rows from your tables with the `postgresql_delete` operation, specifying the table and WHERE conditions.
|
||||
- **Execute raw SQL**: Run any custom SQL command using the `postgresql_execute` operation for advanced use cases.
|
||||
|
||||
The PostgreSQL tool is ideal for scenarios where your agents need to interact with structured data—such as automating reporting, syncing data between systems, or powering data-driven workflows. It streamlines database access, making it easy to read, write, and manage your PostgreSQL data programmatically.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Connect to any PostgreSQL database to execute queries, manage data, and perform database operations. Supports SELECT, INSERT, UPDATE, DELETE operations with secure connection handling.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `postgresql_query`
|
||||
|
||||
Execute a SELECT query on PostgreSQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | PostgreSQL server hostname or IP address |
|
||||
| `port` | number | Yes | PostgreSQL server port \(default: 5432\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `query` | string | Yes | SQL SELECT query to execute |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of rows returned from the query |
|
||||
| `rowCount` | number | Number of rows returned |
|
||||
|
||||
### `postgresql_insert`
|
||||
|
||||
Insert data into PostgreSQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | PostgreSQL server hostname or IP address |
|
||||
| `port` | number | Yes | PostgreSQL server port \(default: 5432\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to insert data into |
|
||||
| `data` | object | Yes | Data object to insert \(key-value pairs\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Inserted data \(if RETURNING clause used\) |
|
||||
| `rowCount` | number | Number of rows inserted |
|
||||
|
||||
### `postgresql_update`
|
||||
|
||||
Update data in PostgreSQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | PostgreSQL server hostname or IP address |
|
||||
| `port` | number | Yes | PostgreSQL server port \(default: 5432\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to update data in |
|
||||
| `data` | object | Yes | Data object with fields to update \(key-value pairs\) |
|
||||
| `where` | string | Yes | WHERE clause condition \(without WHERE keyword\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Updated data \(if RETURNING clause used\) |
|
||||
| `rowCount` | number | Number of rows updated |
|
||||
|
||||
### `postgresql_delete`
|
||||
|
||||
Delete data from PostgreSQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | PostgreSQL server hostname or IP address |
|
||||
| `port` | number | Yes | PostgreSQL server port \(default: 5432\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `table` | string | Yes | Table name to delete data from |
|
||||
| `where` | string | Yes | WHERE clause condition \(without WHERE keyword\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Deleted data \(if RETURNING clause used\) |
|
||||
| `rowCount` | number | Number of rows deleted |
|
||||
|
||||
### `postgresql_execute`
|
||||
|
||||
Execute raw SQL query on PostgreSQL database
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | PostgreSQL server hostname or IP address |
|
||||
| `port` | number | Yes | PostgreSQL server port \(default: 5432\) |
|
||||
| `database` | string | Yes | Database name to connect to |
|
||||
| `username` | string | Yes | Database username |
|
||||
| `password` | string | Yes | Database password |
|
||||
| `ssl` | string | No | SSL connection mode \(disabled, required, preferred\) |
|
||||
| `query` | string | Yes | Raw SQL query to execute |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `rows` | array | Array of rows returned from the query |
|
||||
| `rowCount` | number | Number of rows affected |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `postgresql`
|
||||
@@ -126,8 +126,10 @@ Insert or update points in a Qdrant collection
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `status` | string | Status of the upsert operation |
|
||||
| `data` | object | Result data from the upsert operation |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `status` | any | Operation status |
|
||||
|
||||
### `qdrant_search_vector`
|
||||
|
||||
@@ -150,8 +152,10 @@ Search for similar vectors in a Qdrant collection
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Vector search results with ID, score, payload, and optional vector data |
|
||||
| `status` | string | Status of the search operation |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `status` | any | Operation status |
|
||||
|
||||
### `qdrant_fetch_points`
|
||||
|
||||
@@ -172,8 +176,10 @@ Fetch points by ID from a Qdrant collection
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | array | Fetched points with ID, payload, and optional vector data |
|
||||
| `status` | string | Status of the fetch operation |
|
||||
| `matches` | any | Search matches |
|
||||
| `upsertedCount` | any | Upserted count |
|
||||
| `data` | any | Response data |
|
||||
| `status` | any | Operation status |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,17 +53,20 @@ Fetch posts from a subreddit with different sorting options
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Reddit API |
|
||||
| `subreddit` | string | Yes | The name of the subreddit to fetch posts from \(without the r/ prefix\) |
|
||||
| `sort` | string | No | Sort method for posts: "hot", "new", "top", or "rising" \(default: "hot"\) |
|
||||
| `sort` | string | No | Sort method for posts: |
|
||||
| `limit` | number | No | Maximum number of posts to return \(default: 10, max: 100\) |
|
||||
| `time` | string | No | Time filter for "top" sorted posts: "day", "week", "month", "year", or "all" \(default: "day"\) |
|
||||
| `time` | string | No | Time filter for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `subreddit` | string | Name of the subreddit where posts were fetched from |
|
||||
| `posts` | array | Array of posts with title, author, URL, score, comments count, and metadata |
|
||||
| `subreddit` | string | Subreddit name |
|
||||
| `posts` | json | Posts data |
|
||||
| `post` | json | Single post data |
|
||||
| `comments` | json | Comments data |
|
||||
|
||||
### `reddit_get_comments`
|
||||
|
||||
@@ -73,16 +76,20 @@ Fetch comments from a specific Reddit post
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | Access token for Reddit API |
|
||||
| `postId` | string | Yes | The ID of the Reddit post to fetch comments from |
|
||||
| `subreddit` | string | Yes | The subreddit where the post is located \(without the r/ prefix\) |
|
||||
| `sort` | string | No | Sort method for comments: "confidence", "top", "new", "controversial", "old", "random", "qa" \(default: "confidence"\) |
|
||||
| `sort` | string | No | Sort method for comments: |
|
||||
| `limit` | number | No | Maximum number of comments to return \(default: 50, max: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `post` | object | Post information including ID, title, author, content, and metadata |
|
||||
| `subreddit` | string | Subreddit name |
|
||||
| `posts` | json | Posts data |
|
||||
| `post` | json | Single post data |
|
||||
| `comments` | json | Comments data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -84,8 +84,8 @@ Retrieve an object from an AWS S3 bucket
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `url` | string | Pre-signed URL for downloading the S3 object |
|
||||
| `metadata` | object | File metadata including type, size, name, and last modified date |
|
||||
| `url` | string | Presigned URL |
|
||||
| `metadata` | json | Object metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ A powerful web search tool that provides access to Google search results through
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `searchResults` | array | Search results with titles, links, snippets, and type-specific metadata \(date for news, rating for places, imageUrl for images\) |
|
||||
| `searchResults` | json | Search results data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ Create a new page in a SharePoint site
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the SharePoint API |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `pageName` | string | Yes | The name of the page to create |
|
||||
@@ -85,7 +86,7 @@ Create a new page in a SharePoint site
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `page` | object | Created SharePoint page information |
|
||||
| `sites` | json | An array of SharePoint site objects, each containing details such as id, name, and more. |
|
||||
|
||||
### `sharepoint_read_page`
|
||||
|
||||
@@ -95,6 +96,7 @@ Read a specific page from a SharePoint site
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the SharePoint API |
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `pageId` | string | No | The ID of the page to read |
|
||||
@@ -105,7 +107,7 @@ Read a specific page from a SharePoint site
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `page` | object | Information about the SharePoint page |
|
||||
| `sites` | json | An array of SharePoint site objects, each containing details such as id, name, and more. |
|
||||
|
||||
### `sharepoint_list_sites`
|
||||
|
||||
@@ -115,6 +117,7 @@ List details of all SharePoint sites
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the SharePoint API |
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `groupId` | string | No | The group ID for accessing a group team site |
|
||||
|
||||
@@ -122,7 +125,7 @@ List details of all SharePoint sites
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `site` | object | Information about the current SharePoint site |
|
||||
| `sites` | json | An array of SharePoint site objects, each containing details such as id, name, and more. |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Slack
|
||||
description: Send messages to Slack or trigger workflows from Slack events
|
||||
description: Send messages to Slack
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -64,7 +64,7 @@ This allows for powerful automation scenarios such as sending notifications, ale
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Comprehensive Slack integration with OAuth authentication. Send formatted messages using Slack's mrkdwn syntax or trigger workflows from Slack events like mentions and messages.
|
||||
Comprehensive Slack integration with OAuth authentication. Send formatted messages using Slack's mrkdwn syntax.
|
||||
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Authentication method: oauth or bot_token |
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `accessToken` | string | No | OAuth access token or bot token for Slack API |
|
||||
| `channel` | string | Yes | Target Slack channel \(e.g., #general\) |
|
||||
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
|
||||
|
||||
@@ -88,7 +89,10 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Message timestamp |
|
||||
| `channel` | string | Channel ID where message was sent |
|
||||
| `channel` | string | Channel identifier |
|
||||
| `canvas_id` | string | Canvas identifier |
|
||||
| `title` | string | Canvas title |
|
||||
| `messages` | json | Message data |
|
||||
|
||||
### `slack_canvas`
|
||||
|
||||
@@ -100,6 +104,7 @@ Create and share Slack canvases in channels. Canvases are collaborative document
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Authentication method: oauth or bot_token |
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `accessToken` | string | No | OAuth access token or bot token for Slack API |
|
||||
| `channel` | string | Yes | Target Slack channel \(e.g., #general\) |
|
||||
| `title` | string | Yes | Title of the canvas |
|
||||
| `content` | string | Yes | Canvas content in markdown format |
|
||||
@@ -109,9 +114,11 @@ Create and share Slack canvases in channels. Canvases are collaborative document
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `canvas_id` | string | ID of the created canvas |
|
||||
| `channel` | string | Channel where canvas was created |
|
||||
| `title` | string | Title of the canvas |
|
||||
| `ts` | string | Message timestamp |
|
||||
| `channel` | string | Channel identifier |
|
||||
| `canvas_id` | string | Canvas identifier |
|
||||
| `title` | string | Canvas title |
|
||||
| `messages` | json | Message data |
|
||||
|
||||
### `slack_message_reader`
|
||||
|
||||
@@ -123,6 +130,7 @@ Read the latest messages from Slack channels. Retrieve conversation history with
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Authentication method: oauth or bot_token |
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `accessToken` | string | No | OAuth access token or bot token for Slack API |
|
||||
| `channel` | string | Yes | Slack channel to read messages from \(e.g., #general\) |
|
||||
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 100\) |
|
||||
| `oldest` | string | No | Start of time range \(timestamp\) |
|
||||
@@ -132,7 +140,11 @@ Read the latest messages from Slack channels. Retrieve conversation history with
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `messages` | array | Array of message objects from the channel |
|
||||
| `ts` | string | Message timestamp |
|
||||
| `channel` | string | Channel identifier |
|
||||
| `canvas_id` | string | Canvas identifier |
|
||||
| `title` | string | Canvas title |
|
||||
| `messages` | json | Message data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ Extract structured data from a webpage using Stagehand
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `data` | object | Extracted structured data matching the provided schema |
|
||||
| `data` | json | Extracted data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -212,7 +212,6 @@ Run an autonomous web agent to complete tasks and extract structured data
|
||||
| `startUrl` | string | Yes | URL of the webpage to start the agent on |
|
||||
| `task` | string | Yes | The task to complete or goal to achieve on the website |
|
||||
| `variables` | json | No | Optional variables to substitute in the task \(format: \{key: value\}\). Reference in task using %key% |
|
||||
| `format` | string | No | No description |
|
||||
| `apiKey` | string | Yes | OpenAI API key for agent execution \(required by Stagehand\) |
|
||||
| `outputSchema` | json | No | Optional JSON schema defining the structure of data the agent should return |
|
||||
|
||||
@@ -220,7 +219,8 @@ Run an autonomous web agent to complete tasks and extract structured data
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `agentResult` | object | Result from the Stagehand agent execution |
|
||||
| `agentResult` | json | Agent execution result |
|
||||
| `structuredOutput` | any | Structured output data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Query data from a Supabase table
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to query |
|
||||
| `filter` | string | No | PostgREST filter \(e.g., "id=eq.123"\) |
|
||||
| `filter` | string | No | PostgREST filter \(e.g., |
|
||||
| `orderBy` | string | No | Column to order by \(add DESC for descending\) |
|
||||
| `limit` | number | No | Maximum number of rows to return |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
@@ -101,8 +101,8 @@ Query data from a Supabase table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of records returned from the query |
|
||||
| `message` | string | Operation message |
|
||||
| `results` | json | Query results |
|
||||
|
||||
### `supabase_insert`
|
||||
|
||||
@@ -121,8 +121,8 @@ Insert data into a Supabase table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of inserted records |
|
||||
| `message` | string | Operation message |
|
||||
| `results` | json | Query results |
|
||||
|
||||
### `supabase_get_row`
|
||||
|
||||
@@ -134,15 +134,15 @@ Get a single row from a Supabase table based on filter criteria
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to query |
|
||||
| `filter` | string | Yes | PostgREST filter to find the specific row \(e.g., "id=eq.123"\) |
|
||||
| `filter` | string | Yes | PostgREST filter to find the specific row \(e.g., |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array containing the row data if found, empty array if not found |
|
||||
| `message` | string | Operation message |
|
||||
| `results` | json | Query results |
|
||||
|
||||
### `supabase_update`
|
||||
|
||||
@@ -154,7 +154,7 @@ Update rows in a Supabase table based on filter criteria
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to update |
|
||||
| `filter` | string | Yes | PostgREST filter to identify rows to update \(e.g., "id=eq.123"\) |
|
||||
| `filter` | string | Yes | PostgREST filter to identify rows to update \(e.g., |
|
||||
| `data` | object | Yes | Data to update in the matching rows |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
@@ -162,8 +162,8 @@ Update rows in a Supabase table based on filter criteria
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of updated records |
|
||||
| `message` | string | Operation message |
|
||||
| `results` | json | Query results |
|
||||
|
||||
### `supabase_delete`
|
||||
|
||||
@@ -175,35 +175,15 @@ Delete rows from a Supabase table based on filter criteria
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to delete from |
|
||||
| `filter` | string | Yes | PostgREST filter to identify rows to delete \(e.g., "id=eq.123"\) |
|
||||
| `filter` | string | Yes | PostgREST filter to identify rows to delete \(e.g., |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of deleted records |
|
||||
|
||||
### `supabase_upsert`
|
||||
|
||||
Insert or update data in a Supabase table (upsert operation)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to upsert data into |
|
||||
| `data` | any | Yes | The data to upsert \(insert or update\) |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of upserted records |
|
||||
| `message` | string | Operation message |
|
||||
| `results` | json | Query results |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -80,8 +80,12 @@ Perform AI-powered web searches using Tavily
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `query` | string | The search query that was executed |
|
||||
| `results` | array | results output from the tool |
|
||||
| `results` | json | Search results data |
|
||||
| `answer` | any | Search answer |
|
||||
| `query` | string | Query used |
|
||||
| `content` | string | Extracted content |
|
||||
| `title` | string | Page title |
|
||||
| `url` | string | Source URL |
|
||||
|
||||
### `tavily_extract`
|
||||
|
||||
@@ -99,7 +103,12 @@ Extract raw content from multiple web pages simultaneously using Tavily
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | The URL that was extracted |
|
||||
| `results` | json | Search results data |
|
||||
| `answer` | any | Search answer |
|
||||
| `query` | string | Query used |
|
||||
| `content` | string | Extracted content |
|
||||
| `title` | string | Page title |
|
||||
| `url` | string | Source URL |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Telegram
|
||||
description: Send messages through Telegram or trigger workflows from Telegram events
|
||||
description: Send a message through Telegram
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -67,7 +67,7 @@ In Sim, the Telegram integration enables your agents to leverage these powerful
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Send messages to any Telegram channel using your Bot API key or trigger workflows from Telegram bot messages. Integrate automated notifications and alerts into your workflow to keep your team informed.
|
||||
Send messages to any Telegram channel using your Bot API key. Integrate automated notifications and alerts into your workflow to keep your team informed.
|
||||
|
||||
|
||||
|
||||
@@ -89,12 +89,8 @@ Send messages to Telegram channels or users through the Telegram Bot API. Enable
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Telegram message send success status |
|
||||
| `messageId` | number | Unique Telegram message identifier |
|
||||
| `chatId` | string | Target chat ID where message was sent |
|
||||
| `text` | string | Text content of the sent message |
|
||||
| `timestamp` | number | Unix timestamp when message was sent |
|
||||
| `from` | object | Information about the bot that sent the message |
|
||||
| `ok` | boolean | Success status |
|
||||
| `result` | json | Message result |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ Processes a provided thought/instruction, making it available for subsequent ste
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `acknowledgedThought` | string | The thought that was processed and acknowledged |
|
||||
| `acknowledgedThought` | string | Acknowledged thought process |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Convert text between languages while preserving meaning, nuance, and formatting.
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
| `tokens` | any | Token usage |
|
||||
|
||||
### `anthropic_chat`
|
||||
|
||||
@@ -85,7 +85,7 @@ Convert text between languages while preserving meaning, nuance, and formatting.
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | Translated text |
|
||||
| `model` | string | Model used |
|
||||
| `tokens` | json | Token usage |
|
||||
| `tokens` | any | Token usage |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,11 +58,10 @@ Send text messages to single or multiple recipients using the Twilio API.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | SMS send success status |
|
||||
| `messageId` | string | Unique Twilio message identifier \(SID\) |
|
||||
| `status` | string | Message delivery status from Twilio |
|
||||
| `fromNumber` | string | Phone number message was sent from |
|
||||
| `toNumber` | string | Phone number message was sent to |
|
||||
| `success` | boolean | Send success status |
|
||||
| `messageId` | any | Message identifier |
|
||||
| `status` | any | Delivery status |
|
||||
| `error` | any | Error information |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ Download files uploaded in Typeform responses
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `fileUrl` | string | Direct download URL for the uploaded file |
|
||||
| `contentType` | string | MIME type of the uploaded file |
|
||||
| `filename` | string | Original filename of the uploaded file |
|
||||
| `total_items` | number | Total response count |
|
||||
| `page_count` | number | Total page count |
|
||||
| `items` | json | Response items |
|
||||
|
||||
### `typeform_insights`
|
||||
|
||||
@@ -114,7 +114,9 @@ Retrieve insights and analytics for Typeform forms
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `fields` | array | Number of users who dropped off at this field |
|
||||
| `total_items` | number | Total response count |
|
||||
| `page_count` | number | Total page count |
|
||||
| `items` | json | Response items |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -70,10 +70,9 @@ Process and analyze images using advanced vision models. Capable of understandin
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | The analyzed content and description of the image |
|
||||
| `model` | string | The vision model that was used for analysis |
|
||||
| `tokens` | number | Total tokens used for the analysis |
|
||||
| `usage` | object | Detailed token usage breakdown |
|
||||
| `content` | string | Analysis result |
|
||||
| `model` | any | Model used |
|
||||
| `tokens` | any | Token usage |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,14 +56,21 @@ Read content from a Wealthbox note
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `noteId` | string | No | The ID of the note to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Note data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
### `wealthbox_write_note`
|
||||
|
||||
@@ -73,6 +80,7 @@ Create or update a Wealthbox note
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `content` | string | Yes | The main body of the note |
|
||||
| `contactId` | string | No | ID of contact to link to this note |
|
||||
|
||||
@@ -80,8 +88,14 @@ Create or update a Wealthbox note
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created or updated note data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
### `wealthbox_read_contact`
|
||||
|
||||
@@ -91,14 +105,21 @@ Read content from a Wealthbox contact
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `contactId` | string | No | The ID of the contact to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Contact data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
### `wealthbox_write_contact`
|
||||
|
||||
@@ -108,6 +129,7 @@ Create a new Wealthbox contact
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `firstName` | string | Yes | The first name of the contact |
|
||||
| `lastName` | string | Yes | The last name of the contact |
|
||||
| `emailAddress` | string | No | The email address of the contact |
|
||||
@@ -117,8 +139,14 @@ Create a new Wealthbox contact
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created or updated contact data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
### `wealthbox_read_task`
|
||||
|
||||
@@ -128,14 +156,21 @@ Read content from a Wealthbox task
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `taskId` | string | No | The ID of the task to read |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Task data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
### `wealthbox_write_task`
|
||||
|
||||
@@ -145,8 +180,9 @@ Create or update a Wealthbox task
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | The access token for the Wealthbox API |
|
||||
| `title` | string | Yes | The name/title of the task |
|
||||
| `dueDate` | string | Yes | The due date and time of the task \(format: "YYYY-MM-DD HH:MM AM/PM -HHMM", e.g., "2015-05-24 11:00 AM -0400"\) |
|
||||
| `dueDate` | string | Yes | The due date and time of the task \(format: |
|
||||
| `contactId` | string | No | ID of contact to link to this task |
|
||||
| `description` | string | No | Description or notes about the task |
|
||||
|
||||
@@ -154,8 +190,14 @@ Create or update a Wealthbox task
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created or updated task data and metadata |
|
||||
| `note` | any | Note data |
|
||||
| `notes` | any | Notes list |
|
||||
| `contact` | any | Contact data |
|
||||
| `contacts` | any | Contacts list |
|
||||
| `task` | any | Task data |
|
||||
| `tasks` | any | Tasks list |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| `success` | any | Success status |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -54,16 +54,15 @@ Send WhatsApp messages
|
||||
| `phoneNumber` | string | Yes | Recipient phone number with country code |
|
||||
| `message` | string | Yes | Message content to send |
|
||||
| `phoneNumberId` | string | Yes | WhatsApp Business Phone Number ID |
|
||||
| `accessToken` | string | Yes | WhatsApp Business API Access Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | WhatsApp message send success status |
|
||||
| `messageId` | string | Unique WhatsApp message identifier |
|
||||
| `phoneNumber` | string | Recipient phone number |
|
||||
| `status` | string | Message delivery status |
|
||||
| `timestamp` | string | Message send timestamp |
|
||||
| `success` | boolean | Send success status |
|
||||
| `messageId` | any | Message identifier |
|
||||
| `error` | any | Error information |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -74,7 +74,11 @@ Get a summary and metadata for a specific Wikipedia page.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `summary` | object | Wikipedia page summary and metadata |
|
||||
| `summary` | json | Page summary data |
|
||||
| `searchResults` | json | Search results data |
|
||||
| `totalHits` | number | Total search hits |
|
||||
| `content` | json | Page content data |
|
||||
| `randomPage` | json | Random page data |
|
||||
|
||||
### `wikipedia_search`
|
||||
|
||||
@@ -91,7 +95,11 @@ Search for Wikipedia pages by title or content.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `searchResults` | array | Array of matching Wikipedia pages |
|
||||
| `summary` | json | Page summary data |
|
||||
| `searchResults` | json | Search results data |
|
||||
| `totalHits` | number | Total search hits |
|
||||
| `content` | json | Page content data |
|
||||
| `randomPage` | json | Random page data |
|
||||
|
||||
### `wikipedia_content`
|
||||
|
||||
@@ -107,7 +115,11 @@ Get the full HTML content of a Wikipedia page.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | object | Full HTML content and metadata of the Wikipedia page |
|
||||
| `summary` | json | Page summary data |
|
||||
| `searchResults` | json | Search results data |
|
||||
| `totalHits` | number | Total search hits |
|
||||
| `content` | json | Page content data |
|
||||
| `randomPage` | json | Random page data |
|
||||
|
||||
### `wikipedia_random`
|
||||
|
||||
@@ -122,7 +134,11 @@ Get a random Wikipedia page.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `randomPage` | object | Random Wikipedia page data |
|
||||
| `summary` | json | Page summary data |
|
||||
| `searchResults` | json | Search results data |
|
||||
| `totalHits` | number | Total search hits |
|
||||
| `content` | json | Page content data |
|
||||
| `randomPage` | json | Random page data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ Post new tweets, reply to tweets, or create polls on X (Twitter)
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | X OAuth access token |
|
||||
| `text` | string | Yes | The text content of your tweet |
|
||||
| `replyTo` | string | No | ID of the tweet to reply to |
|
||||
| `mediaIds` | array | No | Array of media IDs to attach to the tweet |
|
||||
@@ -59,7 +60,14 @@ Post new tweets, reply to tweets, or create polls on X (Twitter)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tweet` | object | The newly created tweet data |
|
||||
| `tweet` | json | Tweet data |
|
||||
| `replies` | any | Tweet replies |
|
||||
| `context` | any | Tweet context |
|
||||
| `tweets` | json | Tweets data |
|
||||
| `includes` | any | Additional data |
|
||||
| `meta` | json | Response metadata |
|
||||
| `user` | json | User profile data |
|
||||
| `recentTweets` | any | Recent tweets data |
|
||||
|
||||
### `x_read`
|
||||
|
||||
@@ -69,6 +77,7 @@ Read tweet details, including replies and conversation context
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | X OAuth access token |
|
||||
| `tweetId` | string | Yes | ID of the tweet to read |
|
||||
| `includeReplies` | boolean | No | Whether to include replies to the tweet |
|
||||
|
||||
@@ -76,7 +85,14 @@ Read tweet details, including replies and conversation context
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tweet` | object | The main tweet data |
|
||||
| `tweet` | json | Tweet data |
|
||||
| `replies` | any | Tweet replies |
|
||||
| `context` | any | Tweet context |
|
||||
| `tweets` | json | Tweets data |
|
||||
| `includes` | any | Additional data |
|
||||
| `meta` | json | Response metadata |
|
||||
| `user` | json | User profile data |
|
||||
| `recentTweets` | any | Recent tweets data |
|
||||
|
||||
### `x_search`
|
||||
|
||||
@@ -86,6 +102,7 @@ Search for tweets using keywords, hashtags, or advanced queries
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | X OAuth access token |
|
||||
| `query` | string | Yes | Search query \(supports X search operators\) |
|
||||
| `maxResults` | number | No | Maximum number of results to return \(default: 10, max: 100\) |
|
||||
| `startTime` | string | No | Start time for search \(ISO 8601 format\) |
|
||||
@@ -96,7 +113,14 @@ Search for tweets using keywords, hashtags, or advanced queries
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tweets` | array | Array of tweets matching the search query |
|
||||
| `tweet` | json | Tweet data |
|
||||
| `replies` | any | Tweet replies |
|
||||
| `context` | any | Tweet context |
|
||||
| `tweets` | json | Tweets data |
|
||||
| `includes` | any | Additional data |
|
||||
| `meta` | json | Response metadata |
|
||||
| `user` | json | User profile data |
|
||||
| `recentTweets` | any | Recent tweets data |
|
||||
|
||||
### `x_user`
|
||||
|
||||
@@ -106,13 +130,21 @@ Get user profile information
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `accessToken` | string | Yes | X OAuth access token |
|
||||
| `username` | string | Yes | Username to look up \(without @ symbol\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `user` | object | X user profile information |
|
||||
| `tweet` | json | Tweet data |
|
||||
| `replies` | any | Tweet replies |
|
||||
| `context` | any | Tweet context |
|
||||
| `tweets` | json | Tweets data |
|
||||
| `includes` | any | Additional data |
|
||||
| `meta` | json | Response metadata |
|
||||
| `user` | json | User profile data |
|
||||
| `recentTweets` | any | Recent tweets data |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,8 @@ Search for videos on YouTube using the YouTube Data API.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of YouTube videos matching the search query |
|
||||
| `items` | json | The items returned by the YouTube search |
|
||||
| `totalResults` | number | The total number of results returned by the YouTube search |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Choose your input method from the dropdown:
|
||||
<video autoPlay loop muted playsInline className="w-full -mb-2 rounded-lg" src="/chat-input.mp4"></video>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600">Chat with your workflow and access input text, conversation ID, and uploaded files for context-aware responses.</p>
|
||||
<p className="text-sm text-gray-600">Chat with your workflow and access both input text and conversation ID for context-aware responses.</p>
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -60,15 +60,13 @@ Choose your input method from the dropdown:
|
||||
In Chat mode, access user input and conversation context through special variables:
|
||||
|
||||
```yaml
|
||||
# Reference the chat input, conversation ID, and files in your workflow
|
||||
# Reference the chat input and conversation ID in your workflow
|
||||
user_message: "<start.input>"
|
||||
conversation_id: "<start.conversationId>"
|
||||
uploaded_files: "<start.files>"
|
||||
```
|
||||
|
||||
- **`<start.input>`** - Contains the user's message text
|
||||
- **`<start.conversationId>`** - Unique identifier for the conversation thread
|
||||
- **`<start.files>`** - Array of files uploaded by the user (if any)
|
||||
|
||||
## API Execution
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# Database (Required)
|
||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres"
|
||||
|
||||
# PostgreSQL Port (Optional) - defaults to 5432 if not specified
|
||||
# POSTGRES_PORT=5432
|
||||
|
||||
# Authentication (Required)
|
||||
BETTER_AUTH_SECRET=your_secret_key # Use `openssl rand -hex 32` to generate, or visit https://www.better-auth.com/docs/installation
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { GithubIcon, GoogleIcon } from '@/components/icons'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { client } from '@/lib/auth-client'
|
||||
|
||||
interface SocialLoginButtonsProps {
|
||||
@@ -113,16 +114,58 @@ export function SocialLoginButtons({
|
||||
</Button>
|
||||
)
|
||||
|
||||
const hasAnyOAuthProvider = githubAvailable || googleAvailable
|
||||
const renderGithubButton = () => {
|
||||
if (githubAvailable) return githubButton
|
||||
|
||||
if (!hasAnyOAuthProvider) {
|
||||
return null
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>{githubButton}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className='border-neutral-700 bg-neutral-800 text-white'>
|
||||
<p>
|
||||
GitHub login requires OAuth credentials to be configured. Add the following
|
||||
environment variables:
|
||||
</p>
|
||||
<ul className='mt-2 space-y-1 text-neutral-300 text-xs'>
|
||||
<li>• GITHUB_CLIENT_ID</li>
|
||||
<li>• GITHUB_CLIENT_SECRET</li>
|
||||
</ul>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const renderGoogleButton = () => {
|
||||
if (googleAvailable) return googleButton
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>{googleButton}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className='border-neutral-700 bg-neutral-800 text-white'>
|
||||
<p>
|
||||
Google login requires OAuth credentials to be configured. Add the following
|
||||
environment variables:
|
||||
</p>
|
||||
<ul className='mt-2 space-y-1 text-neutral-300 text-xs'>
|
||||
<li>• GOOGLE_CLIENT_ID</li>
|
||||
<li>• GOOGLE_CLIENT_SECRET</li>
|
||||
</ul>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='grid gap-3'>
|
||||
{githubAvailable && githubButton}
|
||||
{googleAvailable && googleButton}
|
||||
{renderGithubButton()}
|
||||
{renderGoogleButton()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function AuthLayout({ children }: { children: React.ReactNode })
|
||||
const brand = useBrandConfig()
|
||||
|
||||
return (
|
||||
<main className='relative flex min-h-screen flex-col bg-[var(--brand-background-hex)] font-geist-sans text-white'>
|
||||
<main className='relative flex min-h-screen flex-col bg-[#0C0C0C] font-geist-sans text-white'>
|
||||
{/* Background pattern */}
|
||||
<GridPattern
|
||||
x={-5}
|
||||
@@ -28,12 +28,12 @@ export default function AuthLayout({ children }: { children: React.ReactNode })
|
||||
<img
|
||||
src={brand.logoUrl}
|
||||
alt={`${brand.name} Logo`}
|
||||
width={56}
|
||||
height={56}
|
||||
className='h-[56px] w-[56px] object-contain'
|
||||
width={42}
|
||||
height={42}
|
||||
className='h-[42px] w-[42px] object-contain'
|
||||
/>
|
||||
) : (
|
||||
<Image src='/sim.svg' alt={`${brand.name} Logo`} width={56} height={56} />
|
||||
<Image src='/sim.svg' alt={`${brand.name} Logo`} width={42} height={42} />
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -94,10 +94,10 @@ describe('LoginPage', () => {
|
||||
const emailInput = screen.getByPlaceholderText(/enter your email/i)
|
||||
const passwordInput = screen.getByPlaceholderText(/enter your password/i)
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'password123' } })
|
||||
|
||||
expect(emailInput).toHaveValue('user@company.com')
|
||||
expect(emailInput).toHaveValue('test@example.com')
|
||||
expect(passwordInput).toHaveValue('password123')
|
||||
})
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('LoginPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /sign in/i })
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'password123' } })
|
||||
fireEvent.click(submitButton)
|
||||
})
|
||||
@@ -140,14 +140,14 @@ describe('LoginPage', () => {
|
||||
const passwordInput = screen.getByPlaceholderText(/enter your password/i)
|
||||
const submitButton = screen.getByRole('button', { name: /sign in/i })
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'password123' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSignIn).toHaveBeenCalledWith(
|
||||
{
|
||||
email: 'user@company.com',
|
||||
email: 'test@example.com',
|
||||
password: 'password123',
|
||||
callbackURL: '/workspace',
|
||||
},
|
||||
@@ -181,7 +181,7 @@ describe('LoginPage', () => {
|
||||
const passwordInput = screen.getByPlaceholderText(/enter your password/i)
|
||||
const submitButton = screen.getByRole('button', { name: /sign in/i })
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'wrongpassword' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
@@ -242,13 +242,13 @@ describe('LoginPage', () => {
|
||||
const passwordInput = screen.getByPlaceholderText(/enter your password/i)
|
||||
const submitButton = screen.getByRole('button', { name: /sign in/i })
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'password123' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSendOtp).toHaveBeenCalledWith({
|
||||
email: 'user@company.com',
|
||||
email: 'test@example.com',
|
||||
type: 'email-verification',
|
||||
})
|
||||
expect(mockRouter.push).toHaveBeenCalledWith('/verify')
|
||||
|
||||
@@ -15,27 +15,25 @@ import {
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { client } from '@/lib/auth-client'
|
||||
import { quickValidateEmail } from '@/lib/email/validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
|
||||
|
||||
const logger = createLogger('LoginForm')
|
||||
|
||||
const validateEmailField = (emailValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
if (!emailValue || !emailValue.trim()) {
|
||||
errors.push('Email is required.')
|
||||
return errors
|
||||
}
|
||||
|
||||
const validation = quickValidateEmail(emailValue.trim().toLowerCase())
|
||||
if (!validation.isValid) {
|
||||
errors.push(validation.reason || 'Please enter a valid email address.')
|
||||
}
|
||||
|
||||
return errors
|
||||
const EMAIL_VALIDATIONS = {
|
||||
required: {
|
||||
test: (value: string) => Boolean(value && typeof value === 'string'),
|
||||
message: 'Email is required.',
|
||||
},
|
||||
notEmpty: {
|
||||
test: (value: string) => value.trim().length > 0,
|
||||
message: 'Email cannot be empty.',
|
||||
},
|
||||
basicFormat: {
|
||||
regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: 'Please enter a valid email address.',
|
||||
},
|
||||
}
|
||||
|
||||
const PASSWORD_VALIDATIONS = {
|
||||
@@ -49,12 +47,15 @@ const PASSWORD_VALIDATIONS = {
|
||||
},
|
||||
}
|
||||
|
||||
// Validate callback URL to prevent open redirect vulnerabilities
|
||||
const validateCallbackUrl = (url: string): boolean => {
|
||||
try {
|
||||
// If it's a relative URL, it's safe
|
||||
if (url.startsWith('/')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If absolute URL, check if it belongs to the same origin
|
||||
const currentOrigin = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
if (url.startsWith(currentOrigin)) {
|
||||
return true
|
||||
@@ -67,6 +68,28 @@ const validateCallbackUrl = (url: string): boolean => {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate email and return array of error messages
|
||||
const validateEmail = (emailValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
if (!EMAIL_VALIDATIONS.required.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.required.message)
|
||||
return errors // Return early for required field
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.notEmpty.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.notEmpty.message)
|
||||
return errors // Return early for empty field
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.basicFormat.regex.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.basicFormat.message)
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// Validate password and return array of error messages
|
||||
const validatePassword = (passwordValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
@@ -159,7 +182,7 @@ export default function LoginPage({
|
||||
setEmail(newEmail)
|
||||
|
||||
// Silently validate but don't show errors until submit
|
||||
const errors = validateEmailField(newEmail)
|
||||
const errors = validateEmail(newEmail)
|
||||
setEmailErrors(errors)
|
||||
setShowEmailValidationError(false)
|
||||
}
|
||||
@@ -182,7 +205,7 @@ export default function LoginPage({
|
||||
const email = formData.get('email') as string
|
||||
|
||||
// Validate email on submit
|
||||
const emailValidationErrors = validateEmailField(email)
|
||||
const emailValidationErrors = validateEmail(email)
|
||||
setEmailErrors(emailValidationErrors)
|
||||
setShowEmailValidationError(emailValidationErrors.length > 0)
|
||||
|
||||
@@ -362,13 +385,11 @@ export default function LoginPage({
|
||||
callbackURL={callbackUrl}
|
||||
/>
|
||||
|
||||
{(githubAvailable || googleAvailable) && (
|
||||
<div className='relative mt-2 py-4'>
|
||||
<div className='absolute inset-0 flex items-center'>
|
||||
<div className='w-full border-neutral-700/50 border-t' />
|
||||
</div>
|
||||
<div className='relative mt-2 py-4'>
|
||||
<div className='absolute inset-0 flex items-center'>
|
||||
<div className='w-full border-neutral-700/50 border-t' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSubmit} className='space-y-5'>
|
||||
<div className='space-y-4'>
|
||||
@@ -454,7 +475,7 @@ export default function LoginPage({
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
className='flex h-11 w-full items-center justify-center gap-2 bg-brand-primary font-medium text-base text-white shadow-[var(--brand-primary-hex)]/20 shadow-lg transition-colors duration-200 hover:bg-brand-primary-hover'
|
||||
className='flex h-11 w-full items-center justify-center gap-2 bg-[#701ffc] font-medium text-base text-white shadow-[#701ffc]/20 shadow-lg transition-colors duration-200 hover:bg-[#802FFF]'
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? 'Signing in...' : 'Sign In'}
|
||||
@@ -466,28 +487,11 @@ export default function LoginPage({
|
||||
<span className='text-neutral-400'>Don't have an account? </span>
|
||||
<Link
|
||||
href={isInviteFlow ? `/signup?invite_flow=true&callbackUrl=${callbackUrl}` : '/signup'}
|
||||
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
|
||||
className='font-medium text-[#9D54FF] underline-offset-4 transition hover:text-[#a66fff] hover:underline'
|
||||
>
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className='text-center text-neutral-500/80 text-xs leading-relaxed'>
|
||||
By signing in, you agree to our{' '}
|
||||
<Link
|
||||
href='/terms'
|
||||
className='text-neutral-400 underline-offset-4 transition hover:text-neutral-300 hover:underline'
|
||||
>
|
||||
Terms of Service
|
||||
</Link>{' '}
|
||||
and{' '}
|
||||
<Link
|
||||
href='/privacy'
|
||||
className='text-neutral-400 underline-offset-4 transition hover:text-neutral-300 hover:underline'
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog open={forgotPasswordOpen} onOpenChange={setForgotPasswordOpen}>
|
||||
@@ -512,12 +516,14 @@ export default function LoginPage({
|
||||
placeholder='Enter your email'
|
||||
required
|
||||
type='email'
|
||||
className='border-neutral-700/80 bg-neutral-900 text-white placeholder:text-white/60 focus:border-[var(--brand-primary-hover-hex)]/70 focus:ring-[var(--brand-primary-hover-hex)]/20'
|
||||
className='border-neutral-700/80 bg-neutral-900 text-white placeholder:text-white/60 focus:border-[#802FFF]/70 focus:ring-[#802FFF]/20'
|
||||
/>
|
||||
</div>
|
||||
{resetStatus.type && (
|
||||
<div
|
||||
className={`text-sm ${resetStatus.type === 'success' ? 'text-[#4CAF50]' : 'text-red-500'}`}
|
||||
className={`text-sm ${
|
||||
resetStatus.type === 'success' ? 'text-[#4CAF50]' : 'text-red-500'
|
||||
}`}
|
||||
>
|
||||
{resetStatus.message}
|
||||
</div>
|
||||
@@ -525,7 +531,7 @@ export default function LoginPage({
|
||||
<Button
|
||||
type='button'
|
||||
onClick={handleForgotPassword}
|
||||
className='h-11 w-full bg-[var(--brand-primary-hex)] font-medium text-base text-white shadow-[var(--brand-primary-hex)]/20 shadow-lg transition-colors duration-200 hover:bg-[var(--brand-primary-hover-hex)]'
|
||||
className='h-11 w-full bg-[#701ffc] font-medium text-base text-white shadow-[#701ffc]/20 shadow-lg transition-colors duration-200 hover:bg-[#802FFF]'
|
||||
disabled={isSubmittingReset}
|
||||
>
|
||||
{isSubmittingReset ? 'Sending...' : 'Send Reset Link'}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { client, useSession } from '@/lib/auth-client'
|
||||
import { client } from '@/lib/auth-client'
|
||||
import SignupPage from '@/app/(auth)/signup/signup-form'
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
@@ -22,7 +22,6 @@ vi.mock('@/lib/auth-client', () => ({
|
||||
sendVerificationOtp: vi.fn(),
|
||||
},
|
||||
},
|
||||
useSession: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/(auth)/components/social-login-buttons', () => ({
|
||||
@@ -44,9 +43,6 @@ describe('SignupPage', () => {
|
||||
vi.clearAllMocks()
|
||||
;(useRouter as any).mockReturnValue(mockRouter)
|
||||
;(useSearchParams as any).mockReturnValue(mockSearchParams)
|
||||
;(useSession as any).mockReturnValue({
|
||||
refetch: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
mockSearchParams.get.mockReturnValue(null)
|
||||
})
|
||||
|
||||
@@ -100,11 +96,11 @@ describe('SignupPage', () => {
|
||||
const passwordInput = screen.getByPlaceholderText(/enter your password/i)
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
|
||||
expect(nameInput).toHaveValue('John Doe')
|
||||
expect(emailInput).toHaveValue('user@company.com')
|
||||
expect(emailInput).toHaveValue('test@example.com')
|
||||
expect(passwordInput).toHaveValue('Password123!')
|
||||
})
|
||||
|
||||
@@ -122,7 +118,7 @@ describe('SignupPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /create account/i })
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
@@ -148,14 +144,14 @@ describe('SignupPage', () => {
|
||||
|
||||
// Use valid input that passes all validation rules
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSignUp).toHaveBeenCalledWith(
|
||||
{
|
||||
email: 'user@company.com',
|
||||
email: 'test@example.com',
|
||||
password: 'Password123!',
|
||||
name: 'John Doe',
|
||||
},
|
||||
@@ -178,7 +174,7 @@ describe('SignupPage', () => {
|
||||
|
||||
// Use name with leading/trailing spaces which should fail validation
|
||||
fireEvent.change(nameInput, { target: { value: ' John Doe ' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
@@ -210,13 +206,15 @@ describe('SignupPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /create account/i })
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
await waitFor(() => {
|
||||
// With sendVerificationOnSignUp: true, OTP is sent automatically by Better Auth
|
||||
// No manual OTP sending in the component anymore
|
||||
expect(mockSendOtp).toHaveBeenCalledWith({
|
||||
email: 'test@example.com',
|
||||
type: 'email-verification',
|
||||
})
|
||||
expect(mockRouter.push).toHaveBeenCalledWith('/verify?fromSignup=true')
|
||||
})
|
||||
})
|
||||
@@ -269,7 +267,7 @@ describe('SignupPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /create account/i })
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: longName } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'ValidPass123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
@@ -297,7 +295,7 @@ describe('SignupPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /create account/i })
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: exactLengthName } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'ValidPass123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
@@ -310,7 +308,7 @@ describe('SignupPage', () => {
|
||||
await waitFor(() => {
|
||||
expect(mockSignUp).toHaveBeenCalledWith(
|
||||
{
|
||||
email: 'user@company.com',
|
||||
email: 'test@example.com',
|
||||
password: 'ValidPass123!',
|
||||
name: exactLengthName,
|
||||
},
|
||||
@@ -345,7 +343,7 @@ describe('SignupPage', () => {
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
})
|
||||
@@ -387,12 +385,12 @@ describe('SignupPage', () => {
|
||||
const submitButton = screen.getByRole('button', { name: /create account/i })
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'user@company.com' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'test@example.com' } })
|
||||
fireEvent.change(passwordInput, { target: { value: 'Password123!' } })
|
||||
fireEvent.click(submitButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockRouter.push).toHaveBeenCalledWith('/verify?fromSignup=true')
|
||||
expect(mockRouter.push).toHaveBeenCalledWith('/invite/123')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -7,14 +7,10 @@ import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { client, useSession } from '@/lib/auth-client'
|
||||
import { quickValidateEmail } from '@/lib/email/validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { client } from '@/lib/auth-client'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
|
||||
|
||||
const logger = createLogger('SignupForm')
|
||||
|
||||
const PASSWORD_VALIDATIONS = {
|
||||
minLength: { regex: /.{8,}/, message: 'Password must be at least 8 characters long.' },
|
||||
uppercase: {
|
||||
@@ -55,20 +51,31 @@ const NAME_VALIDATIONS = {
|
||||
},
|
||||
}
|
||||
|
||||
const validateEmailField = (emailValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
if (!emailValue || !emailValue.trim()) {
|
||||
errors.push('Email is required.')
|
||||
return errors
|
||||
}
|
||||
|
||||
const validation = quickValidateEmail(emailValue.trim().toLowerCase())
|
||||
if (!validation.isValid) {
|
||||
errors.push(validation.reason || 'Please enter a valid email address.')
|
||||
}
|
||||
|
||||
return errors
|
||||
const EMAIL_VALIDATIONS = {
|
||||
required: {
|
||||
test: (value: string) => Boolean(value && typeof value === 'string'),
|
||||
message: 'Email is required.',
|
||||
},
|
||||
notEmpty: {
|
||||
test: (value: string) => value.trim().length > 0,
|
||||
message: 'Email cannot be empty.',
|
||||
},
|
||||
maxLength: {
|
||||
test: (value: string) => value.length <= 254,
|
||||
message: 'Email must be less than 254 characters.',
|
||||
},
|
||||
basicFormat: {
|
||||
regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
message: 'Please enter a valid email address.',
|
||||
},
|
||||
noSpaces: {
|
||||
regex: /^[^\s]*$/,
|
||||
message: 'Email cannot contain spaces.',
|
||||
},
|
||||
validStart: {
|
||||
regex: /^[a-zA-Z0-9]/,
|
||||
message: 'Email must start with a letter or number.',
|
||||
},
|
||||
}
|
||||
|
||||
function SignupFormContent({
|
||||
@@ -82,7 +89,6 @@ function SignupFormContent({
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { refetch: refetchSession } = useSession()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [, setMounted] = useState(false)
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
@@ -182,6 +188,39 @@ function SignupFormContent({
|
||||
return errors
|
||||
}
|
||||
|
||||
// Validate email and return array of error messages
|
||||
const validateEmail = (emailValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
if (!EMAIL_VALIDATIONS.required.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.required.message)
|
||||
return errors // Return early for required field
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.notEmpty.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.notEmpty.message)
|
||||
return errors // Return early for empty field
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.maxLength.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.maxLength.message)
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.noSpaces.regex.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.noSpaces.message)
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.validStart.regex.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.validStart.message)
|
||||
}
|
||||
|
||||
if (!EMAIL_VALIDATIONS.basicFormat.regex.test(emailValue)) {
|
||||
errors.push(EMAIL_VALIDATIONS.basicFormat.message)
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newPassword = e.target.value
|
||||
setPassword(newPassword)
|
||||
@@ -207,7 +246,7 @@ function SignupFormContent({
|
||||
setEmail(newEmail)
|
||||
|
||||
// Silently validate but don't show errors until submit
|
||||
const errors = validateEmailField(newEmail)
|
||||
const errors = validateEmail(newEmail)
|
||||
setEmailErrors(errors)
|
||||
setShowEmailValidationError(false)
|
||||
|
||||
@@ -232,7 +271,7 @@ function SignupFormContent({
|
||||
setShowNameValidationError(nameValidationErrors.length > 0)
|
||||
|
||||
// Validate email on submit
|
||||
const emailValidationErrors = validateEmailField(emailValue)
|
||||
const emailValidationErrors = validateEmail(emailValue)
|
||||
setEmailErrors(emailValidationErrors)
|
||||
setShowEmailValidationError(emailValidationErrors.length > 0)
|
||||
|
||||
@@ -285,7 +324,7 @@ function SignupFormContent({
|
||||
},
|
||||
{
|
||||
onError: (ctx) => {
|
||||
logger.error('Signup error:', ctx.error)
|
||||
console.error('Signup error:', ctx.error)
|
||||
const errorMessage: string[] = ['Failed to create account']
|
||||
|
||||
if (ctx.error.code?.includes('USER_ALREADY_EXISTS')) {
|
||||
@@ -331,46 +370,30 @@ function SignupFormContent({
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh session to get the new user data immediately after signup
|
||||
try {
|
||||
await refetchSession()
|
||||
logger.info('Session refreshed after successful signup')
|
||||
} catch (sessionError) {
|
||||
logger.error('Failed to refresh session after signup:', sessionError)
|
||||
// Continue anyway - the verification flow will handle this
|
||||
// Handle invitation flow redirect
|
||||
if (isInviteFlow && redirectUrl) {
|
||||
router.push(redirectUrl)
|
||||
return
|
||||
}
|
||||
|
||||
// For new signups, always require verification
|
||||
if (typeof window !== 'undefined') {
|
||||
sessionStorage.setItem('verificationEmail', emailValue)
|
||||
localStorage.setItem('has_logged_in_before', 'true')
|
||||
|
||||
// Set cookie flag for middleware check
|
||||
document.cookie = 'requiresEmailVerification=true; path=/; max-age=900; SameSite=Lax' // 15 min expiry
|
||||
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax'
|
||||
|
||||
// Store invitation flow state if applicable
|
||||
if (isInviteFlow && redirectUrl) {
|
||||
sessionStorage.setItem('inviteRedirectUrl', redirectUrl)
|
||||
sessionStorage.setItem('isInviteFlow', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
// Send verification OTP manually
|
||||
try {
|
||||
await client.emailOtp.sendVerificationOtp({
|
||||
email: emailValue,
|
||||
type: 'email-verification',
|
||||
})
|
||||
} catch (otpError) {
|
||||
logger.error('Failed to send OTP:', otpError)
|
||||
// Continue anyway - user can use resend button
|
||||
} catch (err) {
|
||||
console.error('Failed to send verification OTP:', err)
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
sessionStorage.setItem('verificationEmail', emailValue)
|
||||
localStorage.setItem('has_logged_in_before', 'true')
|
||||
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
|
||||
}
|
||||
|
||||
// Always redirect to verification for new signups
|
||||
router.push('/verify?fromSignup=true')
|
||||
} catch (error) {
|
||||
logger.error('Signup error:', error)
|
||||
console.error('Signup error:', error)
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
@@ -391,13 +414,11 @@ function SignupFormContent({
|
||||
isProduction={isProduction}
|
||||
/>
|
||||
|
||||
{(githubAvailable || googleAvailable) && (
|
||||
<div className='relative mt-2 py-4'>
|
||||
<div className='absolute inset-0 flex items-center'>
|
||||
<div className='w-full border-neutral-700/50 border-t' />
|
||||
</div>
|
||||
<div className='relative mt-2 py-4'>
|
||||
<div className='absolute inset-0 flex items-center'>
|
||||
<div className='w-full border-neutral-700/50 border-t' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSubmit} className='space-y-5'>
|
||||
<div className='space-y-4'>
|
||||
@@ -500,7 +521,7 @@ function SignupFormContent({
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
className='flex h-11 w-full items-center justify-center gap-2 bg-[var(--brand-primary-hex)] font-medium text-base text-white shadow-[var(--brand-primary-hex)]/20 shadow-lg transition-colors duration-200 hover:bg-[var(--brand-primary-hover-hex)]'
|
||||
className='flex h-11 w-full items-center justify-center gap-2 bg-[#701ffc] font-medium text-base text-white shadow-[#701ffc]/20 shadow-lg transition-colors duration-200 hover:bg-[#802FFF]'
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? 'Creating account...' : 'Create Account'}
|
||||
@@ -512,28 +533,11 @@ function SignupFormContent({
|
||||
<span className='text-neutral-400'>Already have an account? </span>
|
||||
<Link
|
||||
href={isInviteFlow ? `/login?invite_flow=true&callbackUrl=${redirectUrl}` : '/login'}
|
||||
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
|
||||
className='font-medium text-[#9D54FF] underline-offset-4 transition hover:text-[#a66fff] hover:underline'
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className='text-center text-neutral-500/80 text-xs leading-relaxed'>
|
||||
By creating an account, you agree to our{' '}
|
||||
<Link
|
||||
href='/terms'
|
||||
className='text-neutral-400 underline-offset-4 transition hover:text-neutral-300 hover:underline'
|
||||
>
|
||||
Terms of Service
|
||||
</Link>{' '}
|
||||
and{' '}
|
||||
<Link
|
||||
href='/privacy'
|
||||
className='text-neutral-400 underline-offset-4 transition hover:text-neutral-300 hover:underline'
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { client, useSession } from '@/lib/auth-client'
|
||||
import { client } from '@/lib/auth-client'
|
||||
import { env, isTruthy } from '@/lib/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
@@ -34,7 +34,6 @@ export function useVerification({
|
||||
}: UseVerificationParams): UseVerificationReturn {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { refetch: refetchSession } = useSession()
|
||||
const [otp, setOtp] = useState('')
|
||||
const [email, setEmail] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
@@ -122,14 +121,10 @@ export function useVerification({
|
||||
if (response && !response.error) {
|
||||
setIsVerified(true)
|
||||
|
||||
// Clear verification requirements and session storage
|
||||
// Clear email from sessionStorage after successful verification
|
||||
if (typeof window !== 'undefined') {
|
||||
sessionStorage.removeItem('verificationEmail')
|
||||
|
||||
// Clear the verification requirement flag
|
||||
document.cookie =
|
||||
'requiresEmailVerification=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
|
||||
|
||||
// Also clear invite-related items
|
||||
if (isInviteFlow) {
|
||||
sessionStorage.removeItem('inviteRedirectUrl')
|
||||
@@ -137,15 +132,16 @@ export function useVerification({
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to proper page after a short delay
|
||||
setTimeout(() => {
|
||||
if (isInviteFlow && redirectUrl) {
|
||||
// For invitation flow, redirect to the invitation page
|
||||
window.location.href = redirectUrl
|
||||
router.push(redirectUrl)
|
||||
} else {
|
||||
// Default redirect to dashboard
|
||||
window.location.href = '/workspace'
|
||||
router.push('/workspace')
|
||||
}
|
||||
}, 1000)
|
||||
}, 2000)
|
||||
} else {
|
||||
logger.info('Setting invalid OTP state - API error response')
|
||||
const message = 'Invalid verification code. Please check and try again.'
|
||||
@@ -227,13 +223,8 @@ export function useVerification({
|
||||
// Auto-verify and redirect in development/docker environments
|
||||
if (isDevOrDocker || !hasResendKey) {
|
||||
setIsVerified(true)
|
||||
|
||||
// Clear verification requirement cookie (same as manual verification)
|
||||
document.cookie =
|
||||
'requiresEmailVerification=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
window.location.href = '/workspace'
|
||||
router.push('/workspace')
|
||||
}, 1000)
|
||||
|
||||
return () => clearTimeout(timeoutId)
|
||||
|
||||
@@ -124,7 +124,7 @@ function VerificationForm({
|
||||
|
||||
<Button
|
||||
onClick={verifyCode}
|
||||
className='h-11 w-full bg-[var(--brand-primary-hex)] font-medium text-base text-white shadow-[var(--brand-primary-hex)]/20 shadow-lg transition-colors duration-200 hover:bg-[var(--brand-primary-hover-hex)]'
|
||||
className='h-11 w-full bg-[#701ffc] font-medium text-base text-white shadow-[#701ffc]/20 shadow-lg transition-colors duration-200 hover:bg-[#802FFF]'
|
||||
disabled={!isOtpComplete || isLoading}
|
||||
>
|
||||
{isLoading ? 'Verifying...' : 'Verify Email'}
|
||||
@@ -140,7 +140,7 @@ function VerificationForm({
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
|
||||
className='font-medium text-[#9D54FF] underline-offset-4 transition hover:text-[#a66fff] hover:underline'
|
||||
onClick={handleResend}
|
||||
disabled={isLoading || isResendDisabled}
|
||||
>
|
||||
|
||||
@@ -35,7 +35,7 @@ export const BlogCard = ({
|
||||
}: BlogCardProps) => {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<div className='flex flex-col rounded-3xl border border-[#606060]/40 bg-[#101010] p-8 transition-all duration-500 hover:bg-[var(--surface-elevated)]'>
|
||||
<div className='flex flex-col rounded-3xl border border-[#606060]/40 bg-[#101010] p-8 transition-all duration-500 hover:bg-[#202020]'>
|
||||
{image ? (
|
||||
<Image
|
||||
src={image}
|
||||
|
||||
@@ -245,7 +245,7 @@ export default function NavClient({
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Button className='h-[43px] bg-[var(--brand-primary-hex)] px-6 py-2 font-geist-sans font-medium text-base text-neutral-100 transition-colors duration-200 hover:bg-[var(--brand-primary-hover-hex)]'>
|
||||
<Button className='h-[43px] bg-[#701ffc] px-6 py-2 font-geist-sans font-medium text-base text-neutral-100 transition-colors duration-200 hover:bg-[#802FFF]'>
|
||||
Contact
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -277,7 +277,7 @@ export default function NavClient({
|
||||
>
|
||||
<SheetContent
|
||||
side='right'
|
||||
className='flex h-full w-[280px] flex-col border-[#181818] border-l bg-[var(--brand-background-hex)] p-6 pt-6 text-white shadow-xl sm:w-[320px] [&>button]:hidden'
|
||||
className='flex h-full w-[280px] flex-col border-[#181818] border-l bg-[#0C0C0C] p-6 pt-6 text-white shadow-xl sm:w-[320px] [&>button]:hidden'
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
onCloseAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
@@ -311,7 +311,7 @@ export default function NavClient({
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Button className='w-full bg-[var(--brand-primary-hex)] py-6 font-medium text-base text-white shadow-[var(--brand-primary-hex)]/20 shadow-lg transition-colors duration-200 hover:bg-[var(--brand-primary-hover-hex)]'>
|
||||
<Button className='w-full bg-[#701ffc] py-6 font-medium text-base text-white shadow-[#701ffc]/20 shadow-lg transition-colors duration-200 hover:bg-[#802FFF]'>
|
||||
Contact
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -62,7 +62,7 @@ function Hero() {
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
onClick={handleNavigate}
|
||||
className='animate-fade-in items-center bg-[var(--brand-primary-hex)] px-7 py-6 font-[420] font-geist-sans text-lg text-neutral-100 tracking-normal shadow-[var(--brand-primary-hex)]/30 shadow-lg hover:bg-[var(--brand-primary-hover-hex)]'
|
||||
className='animate-fade-in items-center bg-[#701ffc] px-7 py-6 font-[420] font-geist-sans text-lg text-neutral-100 tracking-normal shadow-[#701ffc]/30 shadow-lg hover:bg-[#802FFF]'
|
||||
aria-label='Start using the platform'
|
||||
>
|
||||
<div className='text-[1.15rem]'>Start now</div>
|
||||
@@ -104,7 +104,7 @@ function Hero() {
|
||||
className='aspect-[5/3] h-auto md:aspect-auto'
|
||||
>
|
||||
<g filter='url(#filter0_b_0_1)'>
|
||||
<ellipse cx='300' cy='240' rx='290' ry='220' fill='var(--brand-background-hex)' />
|
||||
<ellipse cx='300' cy='240' rx='290' ry='220' fill='#0C0C0C' />
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
|
||||
@@ -151,7 +151,7 @@ export default function ContributorsPage() {
|
||||
)
|
||||
|
||||
return (
|
||||
<main className='relative min-h-screen bg-[var(--brand-background-hex)] font-geist-sans text-white'>
|
||||
<main className='relative min-h-screen bg-[#0C0C0C] font-geist-sans text-white'>
|
||||
{/* Grid pattern background */}
|
||||
<div className='absolute inset-0 bottom-[400px] z-0'>
|
||||
<GridPattern
|
||||
@@ -239,7 +239,7 @@ export default function ContributorsPage() {
|
||||
<div className='mb-6 grid grid-cols-1 gap-3 sm:mb-8 sm:grid-cols-2 sm:gap-4 lg:grid-cols-5'>
|
||||
<div className='rounded-lg border border-[#606060]/20 bg-neutral-800/30 p-3 text-center sm:rounded-xl sm:p-4'>
|
||||
<div className='mb-1 flex items-center justify-center sm:mb-2'>
|
||||
<Star className='h-4 w-4 text-[var(--brand-primary-hex)] sm:h-5 sm:w-5' />
|
||||
<Star className='h-4 w-4 text-[#701ffc] sm:h-5 sm:w-5' />
|
||||
</div>
|
||||
<div className='font-bold text-lg text-white sm:text-xl'>{repoStats.stars}</div>
|
||||
<div className='text-neutral-400 text-xs'>Stars</div>
|
||||
@@ -247,7 +247,7 @@ export default function ContributorsPage() {
|
||||
|
||||
<div className='rounded-lg border border-[#606060]/20 bg-neutral-800/30 p-3 text-center sm:rounded-xl sm:p-4'>
|
||||
<div className='mb-1 flex items-center justify-center sm:mb-2'>
|
||||
<GitFork className='h-4 w-4 text-[var(--brand-primary-hex)] sm:h-5 sm:w-5' />
|
||||
<GitFork className='h-4 w-4 text-[#701ffc] sm:h-5 sm:w-5' />
|
||||
</div>
|
||||
<div className='font-bold text-lg text-white sm:text-xl'>{repoStats.forks}</div>
|
||||
<div className='text-neutral-400 text-xs'>Forks</div>
|
||||
@@ -255,7 +255,7 @@ export default function ContributorsPage() {
|
||||
|
||||
<div className='rounded-lg border border-[#606060]/20 bg-neutral-800/30 p-3 text-center sm:rounded-xl sm:p-4'>
|
||||
<div className='mb-1 flex items-center justify-center sm:mb-2'>
|
||||
<GitGraph className='h-4 w-4 text-[var(--brand-primary-hex)] sm:h-5 sm:w-5' />
|
||||
<GitGraph className='h-4 w-4 text-[#701ffc] sm:h-5 sm:w-5' />
|
||||
</div>
|
||||
<div className='font-bold text-lg text-white sm:text-xl'>
|
||||
{filteredContributors?.length || 0}
|
||||
@@ -265,7 +265,7 @@ export default function ContributorsPage() {
|
||||
|
||||
<div className='rounded-lg border border-[#606060]/20 bg-neutral-800/30 p-3 text-center sm:rounded-xl sm:p-4'>
|
||||
<div className='mb-1 flex items-center justify-center sm:mb-2'>
|
||||
<MessageCircle className='h-4 w-4 text-[var(--brand-primary-hex)] sm:h-5 sm:w-5' />
|
||||
<MessageCircle className='h-4 w-4 text-[#701ffc] sm:h-5 sm:w-5' />
|
||||
</div>
|
||||
<div className='font-bold text-lg text-white sm:text-xl'>
|
||||
{repoStats.openIssues}
|
||||
@@ -275,7 +275,7 @@ export default function ContributorsPage() {
|
||||
|
||||
<div className='rounded-lg border border-[#606060]/20 bg-neutral-800/30 p-3 text-center sm:rounded-xl sm:p-4'>
|
||||
<div className='mb-1 flex items-center justify-center sm:mb-2'>
|
||||
<GitPullRequest className='h-4 w-4 text-[var(--brand-primary-hex)] sm:h-5 sm:w-5' />
|
||||
<GitPullRequest className='h-4 w-4 text-[#701ffc] sm:h-5 sm:w-5' />
|
||||
</div>
|
||||
<div className='font-bold text-lg text-white sm:text-xl'>{repoStats.openPRs}</div>
|
||||
<div className='text-neutral-400 text-xs'>Pull Requests</div>
|
||||
@@ -291,8 +291,8 @@ export default function ContributorsPage() {
|
||||
<AreaChart data={timelineData} className='-mx-2 sm:-mx-5 mt-1 sm:mt-2'>
|
||||
<defs>
|
||||
<linearGradient id='commits' x1='0' y1='0' x2='0' y2='1'>
|
||||
<stop offset='5%' stopColor='var(--brand-primary-hex)' stopOpacity={0.3} />
|
||||
<stop offset='95%' stopColor='var(--brand-primary-hex)' stopOpacity={0} />
|
||||
<stop offset='5%' stopColor='#701ffc' stopOpacity={0.3} />
|
||||
<stop offset='95%' stopColor='#701ffc' stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<XAxis
|
||||
@@ -320,7 +320,7 @@ export default function ContributorsPage() {
|
||||
<div className='rounded-lg border border-[#606060]/30 bg-[#0f0f0f] p-2 shadow-lg backdrop-blur-sm sm:p-3'>
|
||||
<div className='grid gap-1 sm:gap-2'>
|
||||
<div className='flex items-center gap-1 sm:gap-2'>
|
||||
<GitGraph className='h-3 w-3 text-[var(--brand-primary-hex)] sm:h-4 sm:w-4' />
|
||||
<GitGraph className='h-3 w-3 text-[#701ffc] sm:h-4 sm:w-4' />
|
||||
<span className='text-neutral-400 text-xs sm:text-sm'>
|
||||
Commits:
|
||||
</span>
|
||||
@@ -338,7 +338,7 @@ export default function ContributorsPage() {
|
||||
<Area
|
||||
type='monotone'
|
||||
dataKey='commits'
|
||||
stroke='var(--brand-primary-hex)'
|
||||
stroke='#701ffc'
|
||||
strokeWidth={2}
|
||||
fill='url(#commits)'
|
||||
/>
|
||||
@@ -393,7 +393,7 @@ export default function ContributorsPage() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
<Avatar className='h-12 w-12 ring-2 ring-[#606060]/30 transition-transform group-hover:scale-105 group-hover:ring-[var(--brand-primary-hex)]/60 sm:h-16 sm:w-16'>
|
||||
<Avatar className='h-12 w-12 ring-2 ring-[#606060]/30 transition-transform group-hover:scale-105 group-hover:ring-[#701ffc]/60 sm:h-16 sm:w-16'>
|
||||
<AvatarImage
|
||||
src={contributor.avatar_url}
|
||||
alt={contributor.login}
|
||||
@@ -405,13 +405,13 @@ export default function ContributorsPage() {
|
||||
</Avatar>
|
||||
|
||||
<div className='mt-2 text-center sm:mt-3'>
|
||||
<span className='block font-medium text-white text-xs transition-colors group-hover:text-[var(--brand-primary-hex)] sm:text-sm'>
|
||||
<span className='block font-medium text-white text-xs transition-colors group-hover:text-[#701ffc] sm:text-sm'>
|
||||
{contributor.login.length > 12
|
||||
? `${contributor.login.slice(0, 12)}...`
|
||||
: contributor.login}
|
||||
</span>
|
||||
<div className='mt-1 flex items-center justify-center gap-1 sm:mt-2'>
|
||||
<GitGraph className='h-2 w-2 text-neutral-400 transition-colors group-hover:text-[var(--brand-primary-hex)] sm:h-3 sm:w-3' />
|
||||
<GitGraph className='h-2 w-2 text-neutral-400 transition-colors group-hover:text-[#701ffc] sm:h-3 sm:w-3' />
|
||||
<span className='font-medium text-neutral-300 text-xs transition-colors group-hover:text-white sm:text-sm'>
|
||||
{contributor.contributions}
|
||||
</span>
|
||||
@@ -508,7 +508,7 @@ export default function ContributorsPage() {
|
||||
/>
|
||||
<Bar
|
||||
dataKey='contributions'
|
||||
className='fill-[var(--brand-primary-hex)]'
|
||||
className='fill-[#701ffc]'
|
||||
radius={[4, 4, 0, 0]}
|
||||
/>
|
||||
</BarChart>
|
||||
@@ -532,7 +532,7 @@ export default function ContributorsPage() {
|
||||
>
|
||||
<div className='relative p-6 sm:p-8 md:p-12 lg:p-16'>
|
||||
<div className='text-center'>
|
||||
<div className='mb-4 inline-flex items-center rounded-full border border-[var(--brand-primary-hex)]/20 bg-[var(--brand-primary-hex)]/10 px-3 py-1 font-medium text-[var(--brand-primary-hex)] text-xs sm:mb-6 sm:px-4 sm:py-2 sm:text-sm'>
|
||||
<div className='mb-4 inline-flex items-center rounded-full border border-[#701ffc]/20 bg-[#701ffc]/10 px-3 py-1 font-medium text-[#701ffc] text-xs sm:mb-6 sm:px-4 sm:py-2 sm:text-sm'>
|
||||
<Github className='mr-1 h-3 w-3 sm:mr-2 sm:h-4 sm:w-4' />
|
||||
Apache-2.0 Licensed
|
||||
</div>
|
||||
@@ -550,7 +550,7 @@ export default function ContributorsPage() {
|
||||
<Button
|
||||
asChild
|
||||
size='lg'
|
||||
className='bg-[var(--brand-primary-hex)] text-white transition-colors duration-500 hover:bg-[var(--brand-primary-hover-hex)]'
|
||||
className='bg-[#701ffc] text-white transition-colors duration-500 hover:bg-[#802FFF]'
|
||||
>
|
||||
<a
|
||||
href='https://github.com/simstudioai/sim/blob/main/.github/CONTRIBUTING.md'
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function Landing() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className='relative min-h-screen bg-[var(--brand-background-hex)] font-geist-sans'>
|
||||
<main className='relative min-h-screen bg-[#0C0C0C] font-geist-sans'>
|
||||
<NavWrapper onOpenTypeformLink={handleOpenTypeformLink} />
|
||||
|
||||
<Hero />
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function PrivacyPolicy() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className='relative min-h-screen overflow-hidden bg-[var(--brand-background-hex)] text-white'>
|
||||
<main className='relative min-h-screen overflow-hidden bg-[#0C0C0C] text-white'>
|
||||
{/* Grid pattern background - only covers content area */}
|
||||
<div className='absolute inset-0 bottom-[400px] z-0 overflow-hidden'>
|
||||
<GridPattern
|
||||
@@ -42,7 +42,7 @@ export default function PrivacyPolicy() {
|
||||
className='h-full w-full'
|
||||
>
|
||||
<g filter='url(#filter0_b_privacy)'>
|
||||
<rect width='600' height='1600' rx='0' fill='var(--brand-background-hex)' />
|
||||
<rect width='600' height='1600' rx='0' fill='#0C0C0C' />
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
@@ -391,7 +391,7 @@ export default function PrivacyPolicy() {
|
||||
Privacy & Terms web page:{' '}
|
||||
<Link
|
||||
href='https://policies.google.com/privacy?hl=en'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
className='text-[#B5A1D4] hover:text-[#701ffc]'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
@@ -569,7 +569,7 @@ export default function PrivacyPolicy() {
|
||||
Please note that we may ask you to verify your identity before responding to such
|
||||
requests.
|
||||
</p>
|
||||
<p className='mb-4 border-[var(--brand-primary-hex)] border-l-4 bg-[var(--brand-primary-hex)]/10 p-3'>
|
||||
<p className='mb-4 border-[#701ffc] border-l-4 bg-[#701ffc]/10 p-3'>
|
||||
You have the right to complain to a Data Protection Authority about our collection
|
||||
and use of your Personal Information. For more information, please contact your
|
||||
local data protection authority in the European Economic Area (EEA).
|
||||
@@ -661,7 +661,7 @@ export default function PrivacyPolicy() {
|
||||
policy (if any). Before beginning your inquiry, email us at{' '}
|
||||
<Link
|
||||
href='mailto:security@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
className='text-[#B5A1D4] hover:text-[#701ffc]'
|
||||
>
|
||||
security@sim.ai
|
||||
</Link>{' '}
|
||||
@@ -686,7 +686,7 @@ export default function PrivacyPolicy() {
|
||||
To report any security flaws, send an email to{' '}
|
||||
<Link
|
||||
href='mailto:security@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
className='text-[#B5A1D4] hover:text-[#701ffc]'
|
||||
>
|
||||
security@sim.ai
|
||||
</Link>
|
||||
@@ -726,7 +726,7 @@ export default function PrivacyPolicy() {
|
||||
If you have any questions about this Privacy Policy, please contact us at:{' '}
|
||||
<Link
|
||||
href='mailto:privacy@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
className='text-[#B5A1D4] hover:text-[#701ffc]'
|
||||
>
|
||||
privacy@sim.ai
|
||||
</Link>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function TermsOfService() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className='relative min-h-screen overflow-hidden bg-[var(--brand-background-hex)] text-white'>
|
||||
<main className='relative min-h-screen overflow-hidden bg-[#0C0C0C] text-white'>
|
||||
{/* Grid pattern background */}
|
||||
<div className='absolute inset-0 bottom-[400px] z-0 overflow-hidden'>
|
||||
<GridPattern
|
||||
@@ -42,7 +42,7 @@ export default function TermsOfService() {
|
||||
className='h-full w-full'
|
||||
>
|
||||
<g filter='url(#filter0_b_terms)'>
|
||||
<rect width='600' height='1600' rx='0' fill='var(--brand-background-hex)' />
|
||||
<rect width='600' height='1600' rx='0' fill='#0C0C0C' />
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
@@ -268,7 +268,7 @@ export default function TermsOfService() {
|
||||
Arbitration Agreement. The arbitration will be conducted by JAMS, an established
|
||||
alternative dispute resolution provider.
|
||||
</p>
|
||||
<p className='mb-4 border-[var(--brand-primary-hex)] border-l-4 bg-[var(--brand-primary-hex)]/10 p-3'>
|
||||
<p className='mb-4 border-[#701ffc] border-l-4 bg-[#701ffc]/10 p-3'>
|
||||
YOU AND COMPANY AGREE THAT EACH OF US MAY BRING CLAIMS AGAINST THE OTHER ONLY ON
|
||||
AN INDIVIDUAL BASIS AND NOT ON A CLASS, REPRESENTATIVE, OR COLLECTIVE BASIS. ONLY
|
||||
INDIVIDUAL RELIEF IS AVAILABLE, AND DISPUTES OF MORE THAN ONE CUSTOMER OR USER
|
||||
@@ -277,10 +277,7 @@ export default function TermsOfService() {
|
||||
<p className='mb-4'>
|
||||
You have the right to opt out of the provisions of this Arbitration Agreement by
|
||||
sending a timely written notice of your decision to opt out to:{' '}
|
||||
<Link
|
||||
href='mailto:legal@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
>
|
||||
<Link href='mailto:legal@sim.ai' className='text-[#B5A1D4] hover:text-[#701ffc]'>
|
||||
legal@sim.ai{' '}
|
||||
</Link>
|
||||
within 30 days after first becoming subject to this Arbitration Agreement.
|
||||
@@ -333,7 +330,7 @@ export default function TermsOfService() {
|
||||
Our Copyright Agent can be reached at:{' '}
|
||||
<Link
|
||||
href='mailto:copyright@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
className='text-[#B5A1D4] hover:text-[#701ffc]'
|
||||
>
|
||||
copyright@sim.ai
|
||||
</Link>
|
||||
@@ -344,10 +341,7 @@ export default function TermsOfService() {
|
||||
<h2 className='mb-4 font-semibold text-2xl text-white'>12. Contact Us</h2>
|
||||
<p>
|
||||
If you have any questions about these Terms, please contact us at:{' '}
|
||||
<Link
|
||||
href='mailto:legal@sim.ai'
|
||||
className='text-[#B5A1D4] hover:text-[var(--brand-primary-hex)]'
|
||||
>
|
||||
<Link href='mailto:legal@sim.ai' className='text-[#B5A1D4] hover:text-[#701ffc]'>
|
||||
legal@sim.ai
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -99,7 +99,6 @@ export const sampleWorkflowState = {
|
||||
horizontalHandles: true,
|
||||
isWide: false,
|
||||
advancedMode: false,
|
||||
triggerMode: false,
|
||||
height: 95,
|
||||
},
|
||||
'agent-id': {
|
||||
@@ -128,7 +127,6 @@ export const sampleWorkflowState = {
|
||||
horizontalHandles: true,
|
||||
isWide: false,
|
||||
advancedMode: false,
|
||||
triggerMode: false,
|
||||
height: 680,
|
||||
},
|
||||
},
|
||||
@@ -354,18 +352,6 @@ export function mockExecutionDependencies() {
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock Trigger.dev SDK (tasks.trigger and task factory) for tests that import background modules
|
||||
*/
|
||||
export function mockTriggerDevSdk() {
|
||||
vi.mock('@trigger.dev/sdk', () => ({
|
||||
tasks: {
|
||||
trigger: vi.fn().mockResolvedValue({ id: 'mock-task-id' }),
|
||||
},
|
||||
task: vi.fn().mockReturnValue({}),
|
||||
}))
|
||||
}
|
||||
|
||||
export function mockWorkflowAccessValidation(shouldSucceed = true) {
|
||||
if (shouldSucceed) {
|
||||
vi.mock('@/app/api/workflows/middleware', () => ({
|
||||
@@ -726,7 +712,6 @@ export function mockFileSystem(
|
||||
}
|
||||
return Promise.reject(new Error('File not found'))
|
||||
}),
|
||||
mkdir: vi.fn().mockResolvedValue(undefined),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -776,15 +761,14 @@ export function createStorageProviderMocks(options: StorageProviderMockOptions =
|
||||
getStorageProvider: vi.fn().mockReturnValue(provider),
|
||||
isUsingCloudStorage: vi.fn().mockReturnValue(isCloudEnabled),
|
||||
uploadFile: vi.fn().mockResolvedValue({
|
||||
path: '/api/files/serve/test-key.txt',
|
||||
key: 'test-key.txt',
|
||||
path: '/api/files/serve/test-key',
|
||||
key: 'test-key',
|
||||
name: 'test.txt',
|
||||
size: 100,
|
||||
type: 'text/plain',
|
||||
}),
|
||||
downloadFile: vi.fn().mockResolvedValue(Buffer.from('test content')),
|
||||
deleteFile: vi.fn().mockResolvedValue(undefined),
|
||||
getPresignedUrl: vi.fn().mockResolvedValue(presignedUrl),
|
||||
}))
|
||||
|
||||
if (provider === 's3') {
|
||||
@@ -1251,15 +1235,14 @@ export function setupFileApiMocks(
|
||||
getStorageProvider: vi.fn().mockReturnValue('local'),
|
||||
isUsingCloudStorage: vi.fn().mockReturnValue(cloudEnabled),
|
||||
uploadFile: vi.fn().mockResolvedValue({
|
||||
path: '/api/files/serve/test-key.txt',
|
||||
key: 'test-key.txt',
|
||||
path: '/api/files/serve/test-key',
|
||||
key: 'test-key',
|
||||
name: 'test.txt',
|
||||
size: 100,
|
||||
type: 'text/plain',
|
||||
}),
|
||||
downloadFile: vi.fn().mockResolvedValue(Buffer.from('test content')),
|
||||
deleteFile: vi.fn().mockResolvedValue(undefined),
|
||||
getPresignedUrl: vi.fn().mockResolvedValue('https://example.com/presigned-url'),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1364,8 +1347,8 @@ export function mockUploadUtils(
|
||||
const {
|
||||
isCloudStorage = false,
|
||||
uploadResult = {
|
||||
path: '/api/files/serve/test-key.txt',
|
||||
key: 'test-key.txt',
|
||||
path: '/api/files/serve/test-key',
|
||||
key: 'test-key',
|
||||
name: 'test.txt',
|
||||
size: 100,
|
||||
type: 'text/plain',
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('OAuth Credentials API Route', () => {
|
||||
})
|
||||
expect(data.credentials[1]).toMatchObject({
|
||||
id: 'credential-2',
|
||||
provider: 'google-default',
|
||||
provider: 'google-email',
|
||||
isDefault: true,
|
||||
})
|
||||
})
|
||||
@@ -158,7 +158,7 @@ describe('OAuth Credentials API Route', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(400)
|
||||
expect(data.error).toBe('Provider or credentialId is required')
|
||||
expect(data.error).toBe('Provider is required')
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { jwtDecode } from 'jwt-decode'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { OAuthService } from '@/lib/oauth/oauth'
|
||||
import { parseProvider } from '@/lib/oauth/oauth'
|
||||
import { getUserEntityPermissions } from '@/lib/permissions/utils'
|
||||
import { db } from '@/db'
|
||||
import { account, user, workflow } from '@/db/schema'
|
||||
import { account, user } from '@/db/schema'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
@@ -26,96 +25,36 @@ export async function GET(request: NextRequest) {
|
||||
const requestId = crypto.randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
// Get query params
|
||||
const { searchParams } = new URL(request.url)
|
||||
const providerParam = searchParams.get('provider') as OAuthService | null
|
||||
const workflowId = searchParams.get('workflowId')
|
||||
const credentialId = searchParams.get('credentialId')
|
||||
// Get the session
|
||||
const session = await getSession()
|
||||
|
||||
// Authenticate requester (supports session, API key, internal JWT)
|
||||
const authResult = await checkHybridAuth(request)
|
||||
if (!authResult.success || !authResult.userId) {
|
||||
// Check if the user is authenticated
|
||||
if (!session?.user?.id) {
|
||||
logger.warn(`[${requestId}] Unauthenticated credentials request rejected`)
|
||||
return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
|
||||
}
|
||||
const requesterUserId = authResult.userId
|
||||
|
||||
// Resolve effective user id: workflow owner if workflowId provided (with access check); else requester
|
||||
let effectiveUserId: string
|
||||
if (workflowId) {
|
||||
// Load workflow owner and workspace for access control
|
||||
const rows = await db
|
||||
.select({ userId: workflow.userId, workspaceId: workflow.workspaceId })
|
||||
.from(workflow)
|
||||
.where(eq(workflow.id, workflowId))
|
||||
.limit(1)
|
||||
// Get the provider from the query params
|
||||
const { searchParams } = new URL(request.url)
|
||||
const provider = searchParams.get('provider') as OAuthService | null
|
||||
|
||||
if (!rows.length) {
|
||||
logger.warn(`[${requestId}] Workflow not found for credentials request`, { workflowId })
|
||||
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const wf = rows[0]
|
||||
|
||||
if (requesterUserId !== wf.userId) {
|
||||
if (!wf.workspaceId) {
|
||||
logger.warn(
|
||||
`[${requestId}] Forbidden - workflow has no workspace and requester is not owner`,
|
||||
{
|
||||
requesterUserId,
|
||||
}
|
||||
)
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
||||
}
|
||||
|
||||
const perm = await getUserEntityPermissions(requesterUserId, 'workspace', wf.workspaceId)
|
||||
if (perm === null) {
|
||||
logger.warn(`[${requestId}] Forbidden credentials request - no workspace access`, {
|
||||
requesterUserId,
|
||||
workspaceId: wf.workspaceId,
|
||||
})
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
||||
}
|
||||
}
|
||||
|
||||
effectiveUserId = wf.userId
|
||||
} else {
|
||||
effectiveUserId = requesterUserId
|
||||
}
|
||||
|
||||
if (!providerParam && !credentialId) {
|
||||
if (!provider) {
|
||||
logger.warn(`[${requestId}] Missing provider parameter`)
|
||||
return NextResponse.json({ error: 'Provider or credentialId is required' }, { status: 400 })
|
||||
return NextResponse.json({ error: 'Provider is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Parse the provider to get base provider and feature type (if provider is present)
|
||||
const { baseProvider } = parseProvider(providerParam || 'google-default')
|
||||
// Parse the provider to get base provider and feature type
|
||||
const { baseProvider } = parseProvider(provider)
|
||||
|
||||
let accountsData
|
||||
|
||||
if (credentialId) {
|
||||
// Foreign-aware lookup for a specific credential by id
|
||||
// If workflowId is provided and requester has access (checked above), allow fetching by id only
|
||||
if (workflowId) {
|
||||
accountsData = await db.select().from(account).where(eq(account.id, credentialId))
|
||||
} else {
|
||||
// Fallback: constrain to requester's own credentials when not in a workflow context
|
||||
accountsData = await db
|
||||
.select()
|
||||
.from(account)
|
||||
.where(and(eq(account.userId, effectiveUserId), eq(account.id, credentialId)))
|
||||
}
|
||||
} else {
|
||||
// Fetch all credentials for provider and effective user
|
||||
accountsData = await db
|
||||
.select()
|
||||
.from(account)
|
||||
.where(and(eq(account.userId, effectiveUserId), eq(account.providerId, providerParam!)))
|
||||
}
|
||||
// Get all accounts for this user and provider
|
||||
const accounts = await db
|
||||
.select()
|
||||
.from(account)
|
||||
.where(and(eq(account.userId, session.user.id), eq(account.providerId, provider)))
|
||||
|
||||
// Transform accounts into credentials
|
||||
const credentials = await Promise.all(
|
||||
accountsData.map(async (acc) => {
|
||||
accounts.map(async (acc) => {
|
||||
// Extract the feature type from providerId (e.g., 'google-default' -> 'default')
|
||||
const [_, featureType = 'default'] = acc.providerId.split('-')
|
||||
|
||||
@@ -170,7 +109,7 @@ export async function GET(request: NextRequest) {
|
||||
return {
|
||||
id: acc.id,
|
||||
name: displayName,
|
||||
provider: acc.providerId,
|
||||
provider,
|
||||
lastUsed: acc.updatedAt.toISOString(),
|
||||
isDefault: featureType === 'default',
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ describe('OAuth Token API Routes', () => {
|
||||
const mockGetUserId = vi.fn()
|
||||
const mockGetCredential = vi.fn()
|
||||
const mockRefreshTokenIfNeeded = vi.fn()
|
||||
const mockAuthorizeCredentialUse = vi.fn()
|
||||
const mockCheckHybridAuth = vi.fn()
|
||||
|
||||
const mockLogger = {
|
||||
info: vi.fn(),
|
||||
@@ -39,14 +37,6 @@ describe('OAuth Token API Routes', () => {
|
||||
vi.doMock('@/lib/logs/console/logger', () => ({
|
||||
createLogger: vi.fn().mockReturnValue(mockLogger),
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/auth/credential-access', () => ({
|
||||
authorizeCredentialUse: mockAuthorizeCredentialUse,
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||
checkHybridAuth: mockCheckHybridAuth,
|
||||
}))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -58,12 +48,7 @@ describe('OAuth Token API Routes', () => {
|
||||
*/
|
||||
describe('POST handler', () => {
|
||||
it('should return access token successfully', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
authType: 'session',
|
||||
requesterUserId: 'test-user-id',
|
||||
credentialOwnerUserId: 'owner-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: 'test-token',
|
||||
@@ -93,18 +78,13 @@ describe('OAuth Token API Routes', () => {
|
||||
expect(data).toHaveProperty('accessToken', 'fresh-token')
|
||||
|
||||
// Verify mocks were called correctly
|
||||
expect(mockAuthorizeCredentialUse).toHaveBeenCalled()
|
||||
expect(mockGetCredential).toHaveBeenCalled()
|
||||
expect(mockGetUserId).toHaveBeenCalledWith(mockRequestId, undefined)
|
||||
expect(mockGetCredential).toHaveBeenCalledWith(mockRequestId, 'credential-id', 'test-user-id')
|
||||
expect(mockRefreshTokenIfNeeded).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should handle workflowId for server-side authentication', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
authType: 'internal_jwt',
|
||||
requesterUserId: 'workflow-owner-id',
|
||||
credentialOwnerUserId: 'workflow-owner-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('workflow-owner-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: 'test-token',
|
||||
@@ -130,8 +110,12 @@ describe('OAuth Token API Routes', () => {
|
||||
expect(response.status).toBe(200)
|
||||
expect(data).toHaveProperty('accessToken', 'fresh-token')
|
||||
|
||||
expect(mockAuthorizeCredentialUse).toHaveBeenCalled()
|
||||
expect(mockGetCredential).toHaveBeenCalled()
|
||||
expect(mockGetUserId).toHaveBeenCalledWith(mockRequestId, 'workflow-id')
|
||||
expect(mockGetCredential).toHaveBeenCalledWith(
|
||||
mockRequestId,
|
||||
'credential-id',
|
||||
'workflow-owner-id'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle missing credentialId', async () => {
|
||||
@@ -148,10 +132,7 @@ describe('OAuth Token API Routes', () => {
|
||||
})
|
||||
|
||||
it('should handle authentication failure', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
error: 'Authentication required',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce(undefined)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
credentialId: 'credential-id',
|
||||
@@ -162,12 +143,12 @@ describe('OAuth Token API Routes', () => {
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(403)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(response.status).toBe(401)
|
||||
expect(data).toHaveProperty('error', 'User not authenticated')
|
||||
})
|
||||
|
||||
it('should handle workflow not found', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({ ok: false, error: 'Workflow not found' })
|
||||
mockGetUserId.mockResolvedValueOnce(undefined)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
credentialId: 'credential-id',
|
||||
@@ -179,16 +160,12 @@ describe('OAuth Token API Routes', () => {
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(403)
|
||||
expect(response.status).toBe(404)
|
||||
expect(data).toHaveProperty('error', 'Workflow not found')
|
||||
})
|
||||
|
||||
it('should handle credential not found', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
authType: 'session',
|
||||
requesterUserId: 'test-user-id',
|
||||
credentialOwnerUserId: 'owner-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce(undefined)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
@@ -200,17 +177,12 @@ describe('OAuth Token API Routes', () => {
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(401)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(response.status).toBe(404)
|
||||
expect(data).toHaveProperty('error', 'Credential not found')
|
||||
})
|
||||
|
||||
it('should handle token refresh failure', async () => {
|
||||
mockAuthorizeCredentialUse.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
authType: 'session',
|
||||
requesterUserId: 'test-user-id',
|
||||
credentialOwnerUserId: 'owner-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: 'test-token',
|
||||
@@ -239,11 +211,7 @@ describe('OAuth Token API Routes', () => {
|
||||
*/
|
||||
describe('GET handler', () => {
|
||||
it('should return access token successfully', async () => {
|
||||
mockCheckHybridAuth.mockResolvedValueOnce({
|
||||
success: true,
|
||||
authType: 'session',
|
||||
userId: 'test-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: 'test-token',
|
||||
@@ -268,7 +236,7 @@ describe('OAuth Token API Routes', () => {
|
||||
expect(response.status).toBe(200)
|
||||
expect(data).toHaveProperty('accessToken', 'fresh-token')
|
||||
|
||||
expect(mockCheckHybridAuth).toHaveBeenCalled()
|
||||
expect(mockGetUserId).toHaveBeenCalledWith(mockRequestId)
|
||||
expect(mockGetCredential).toHaveBeenCalledWith(mockRequestId, 'credential-id', 'test-user-id')
|
||||
expect(mockRefreshTokenIfNeeded).toHaveBeenCalled()
|
||||
})
|
||||
@@ -287,10 +255,7 @@ describe('OAuth Token API Routes', () => {
|
||||
})
|
||||
|
||||
it('should handle authentication failure', async () => {
|
||||
mockCheckHybridAuth.mockResolvedValueOnce({
|
||||
success: false,
|
||||
error: 'Authentication required',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce(undefined)
|
||||
|
||||
const req = new Request(
|
||||
'http://localhost:3000/api/auth/oauth/token?credentialId=credential-id'
|
||||
@@ -302,15 +267,11 @@ describe('OAuth Token API Routes', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(401)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(data).toHaveProperty('error', 'User not authenticated')
|
||||
})
|
||||
|
||||
it('should handle credential not found', async () => {
|
||||
mockCheckHybridAuth.mockResolvedValueOnce({
|
||||
success: true,
|
||||
authType: 'session',
|
||||
userId: 'test-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce(undefined)
|
||||
|
||||
const req = new Request(
|
||||
@@ -323,15 +284,11 @@ describe('OAuth Token API Routes', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(404)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(data).toHaveProperty('error', 'Credential not found')
|
||||
})
|
||||
|
||||
it('should handle missing access token', async () => {
|
||||
mockCheckHybridAuth.mockResolvedValueOnce({
|
||||
success: true,
|
||||
authType: 'session',
|
||||
userId: 'test-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: null,
|
||||
@@ -349,15 +306,12 @@ describe('OAuth Token API Routes', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(400)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(data).toHaveProperty('error', 'No access token available')
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should handle token refresh failure', async () => {
|
||||
mockCheckHybridAuth.mockResolvedValueOnce({
|
||||
success: true,
|
||||
authType: 'session',
|
||||
userId: 'test-user-id',
|
||||
})
|
||||
mockGetUserId.mockResolvedValueOnce('test-user-id')
|
||||
mockGetCredential.mockResolvedValueOnce({
|
||||
id: 'credential-id',
|
||||
accessToken: 'test-token',
|
||||
@@ -377,7 +331,7 @@ describe('OAuth Token API Routes', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(401)
|
||||
expect(data).toHaveProperty('error')
|
||||
expect(data).toHaveProperty('error', 'Failed to refresh access token')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getCredential, refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
import { getCredential, getUserId, refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
@@ -28,18 +26,23 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Credential ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
// We already have workflowId from the parsed body; avoid forcing hybrid auth to re-read it
|
||||
const authz = await authorizeCredentialUse(request, {
|
||||
credentialId,
|
||||
workflowId,
|
||||
requireWorkflowIdForInternal: false,
|
||||
})
|
||||
if (!authz.ok || !authz.credentialOwnerUserId) {
|
||||
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
|
||||
// Determine the user ID based on the context
|
||||
const userId = await getUserId(requestId, workflowId)
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json(
|
||||
{ error: workflowId ? 'Workflow not found' : 'User not authenticated' },
|
||||
{ status: workflowId ? 404 : 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch the credential as the owner to enforce ownership scoping
|
||||
const credential = await getCredential(requestId, credentialId, authz.credentialOwnerUserId)
|
||||
// Get the credential from the database
|
||||
const credential = await getCredential(requestId, credentialId, userId)
|
||||
|
||||
if (!credential) {
|
||||
logger.error(`[${requestId}] Credential not found: ${credentialId}`)
|
||||
return NextResponse.json({ error: 'Credential not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
try {
|
||||
// Refresh the token if needed
|
||||
@@ -72,24 +75,27 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
// For GET requests, we only support session-based authentication
|
||||
const auth = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!auth.success || auth.authType !== 'session' || !auth.userId) {
|
||||
const userId = await getUserId(requestId)
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
|
||||
}
|
||||
|
||||
// Get the credential from the database
|
||||
const credential = await getCredential(requestId, credentialId, auth.userId)
|
||||
const credential = await getCredential(requestId, credentialId, userId)
|
||||
|
||||
if (!credential) {
|
||||
return NextResponse.json({ error: 'Credential not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Check if the access token is valid
|
||||
if (!credential.accessToken) {
|
||||
logger.warn(`[${requestId}] No access token available for credential`)
|
||||
return NextResponse.json({ error: 'No access token available' }, { status: 400 })
|
||||
}
|
||||
|
||||
try {
|
||||
// Refresh the token if needed
|
||||
const { accessToken } = await refreshTokenIfNeeded(requestId, credential, credentialId)
|
||||
return NextResponse.json({ accessToken }, { status: 200 })
|
||||
} catch (_error) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { refreshOAuthToken } from '@/lib/oauth/oauth'
|
||||
@@ -70,8 +70,7 @@ export async function getOAuthToken(userId: string, providerId: string): Promise
|
||||
})
|
||||
.from(account)
|
||||
.where(and(eq(account.userId, userId), eq(account.providerId, providerId)))
|
||||
// Always use the most recently updated credential for this provider
|
||||
.orderBy(desc(account.updatedAt))
|
||||
.orderBy(account.createdAt)
|
||||
.limit(1)
|
||||
|
||||
if (connections.length === 0) {
|
||||
@@ -81,13 +80,19 @@ export async function getOAuthToken(userId: string, providerId: string): Promise
|
||||
|
||||
const credential = connections[0]
|
||||
|
||||
// Determine whether we should refresh: missing token OR expired token
|
||||
// Check if we have a valid access token
|
||||
if (!credential.accessToken) {
|
||||
logger.warn(`Access token is null for user ${userId}, provider ${providerId}`)
|
||||
return null
|
||||
}
|
||||
|
||||
// Check if the token is expired and needs refreshing
|
||||
const now = new Date()
|
||||
const tokenExpiry = credential.accessTokenExpiresAt
|
||||
const shouldAttemptRefresh =
|
||||
!!credential.refreshToken && (!credential.accessToken || (tokenExpiry && tokenExpiry < now))
|
||||
// Only refresh if we have an expiration time AND it's expired AND we have a refresh token
|
||||
const needsRefresh = tokenExpiry && tokenExpiry < now && !!credential.refreshToken
|
||||
|
||||
if (shouldAttemptRefresh) {
|
||||
if (needsRefresh) {
|
||||
logger.info(
|
||||
`Access token expired for user ${userId}, provider ${providerId}. Attempting to refresh.`
|
||||
)
|
||||
@@ -136,13 +141,6 @@ export async function getOAuthToken(userId: string, providerId: string): Promise
|
||||
}
|
||||
}
|
||||
|
||||
if (!credential.accessToken) {
|
||||
logger.warn(
|
||||
`Access token is null and no refresh attempted or available for user ${userId}, provider ${providerId}`
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
logger.info(`Found valid OAuth token for user ${userId}, provider ${providerId}`)
|
||||
return credential.accessToken
|
||||
}
|
||||
@@ -166,21 +164,19 @@ export async function refreshAccessTokenIfNeeded(
|
||||
return null
|
||||
}
|
||||
|
||||
// Decide if we should refresh: token missing OR expired
|
||||
// Check if we need to refresh the token
|
||||
const expiresAt = credential.accessTokenExpiresAt
|
||||
const now = new Date()
|
||||
const shouldRefresh =
|
||||
!!credential.refreshToken && (!credential.accessToken || (expiresAt && expiresAt <= now))
|
||||
// Only refresh if we have an expiration time AND it's expired
|
||||
// If no expiration time is set (newly created credentials), assume token is valid
|
||||
const needsRefresh = expiresAt && expiresAt <= now
|
||||
|
||||
const accessToken = credential.accessToken
|
||||
|
||||
if (shouldRefresh) {
|
||||
if (needsRefresh && credential.refreshToken) {
|
||||
logger.info(`[${requestId}] Token expired, attempting to refresh for credential`)
|
||||
try {
|
||||
const refreshedToken = await refreshOAuthToken(
|
||||
credential.providerId,
|
||||
credential.refreshToken!
|
||||
)
|
||||
const refreshedToken = await refreshOAuthToken(credential.providerId, credential.refreshToken)
|
||||
|
||||
if (!refreshedToken) {
|
||||
logger.error(`[${requestId}] Failed to refresh token for credential: ${credentialId}`, {
|
||||
@@ -221,7 +217,6 @@ export async function refreshAccessTokenIfNeeded(
|
||||
return null
|
||||
}
|
||||
} else if (!accessToken) {
|
||||
// We have no access token and either no refresh token or not eligible to refresh
|
||||
logger.error(`[${requestId}] Missing access token for credential`)
|
||||
return null
|
||||
}
|
||||
@@ -238,20 +233,21 @@ export async function refreshTokenIfNeeded(
|
||||
credential: any,
|
||||
credentialId: string
|
||||
): Promise<{ accessToken: string; refreshed: boolean }> {
|
||||
// Decide if we should refresh: token missing OR expired
|
||||
// Check if we need to refresh the token
|
||||
const expiresAt = credential.accessTokenExpiresAt
|
||||
const now = new Date()
|
||||
const shouldRefresh =
|
||||
!!credential.refreshToken && (!credential.accessToken || (expiresAt && expiresAt <= now))
|
||||
// Only refresh if we have an expiration time AND it's expired
|
||||
// If no expiration time is set (newly created credentials), assume token is valid
|
||||
const needsRefresh = expiresAt && expiresAt <= now
|
||||
|
||||
// If token appears valid and present, return it directly
|
||||
if (!shouldRefresh) {
|
||||
// If token is still valid, return it directly
|
||||
if (!needsRefresh || !credential.refreshToken) {
|
||||
logger.info(`[${requestId}] Access token is valid`)
|
||||
return { accessToken: credential.accessToken, refreshed: false }
|
||||
}
|
||||
|
||||
try {
|
||||
const refreshResult = await refreshOAuthToken(credential.providerId, credential.refreshToken!)
|
||||
const refreshResult = await refreshOAuthToken(credential.providerId, credential.refreshToken)
|
||||
|
||||
if (!refreshResult) {
|
||||
logger.error(`[${requestId}] Failed to refresh token for credential`)
|
||||
|
||||
@@ -4,9 +4,8 @@ import { auth } from '@/lib/auth'
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
const hdrs = await headers()
|
||||
const response = await auth.api.generateOneTimeToken({
|
||||
headers: hdrs,
|
||||
headers: await headers(),
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
@@ -15,6 +14,7 @@ export async function POST() {
|
||||
|
||||
return NextResponse.json({ token: response.token })
|
||||
} catch (error) {
|
||||
console.error('Error generating one-time token:', error)
|
||||
return NextResponse.json({ error: 'Failed to generate token' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { toNextJsHandler } from 'better-auth/next-js'
|
||||
import { auth } from '@/lib/auth'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
// Handle Stripe webhooks through better-auth
|
||||
export const { GET, POST } = toNextJsHandler(auth.handler)
|
||||
109
apps/sim/app/api/billing/daily/route.ts
Normal file
109
apps/sim/app/api/billing/daily/route.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { verifyCronAuth } from '@/lib/auth/internal'
|
||||
import { processDailyBillingCheck } from '@/lib/billing/core/billing'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('DailyBillingCron')
|
||||
|
||||
/**
|
||||
* Daily billing CRON job endpoint that checks individual billing periods
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const authError = verifyCronAuth(request, 'daily billing check')
|
||||
if (authError) {
|
||||
return authError
|
||||
}
|
||||
|
||||
logger.info('Starting daily billing check cron job')
|
||||
|
||||
const startTime = Date.now()
|
||||
|
||||
// Process overage billing for users and organizations with periods ending today
|
||||
const result = await processDailyBillingCheck()
|
||||
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
if (result.success) {
|
||||
logger.info('Daily billing check completed successfully', {
|
||||
processedUsers: result.processedUsers,
|
||||
processedOrganizations: result.processedOrganizations,
|
||||
totalChargedAmount: result.totalChargedAmount,
|
||||
duration: `${duration}ms`,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
summary: {
|
||||
processedUsers: result.processedUsers,
|
||||
processedOrganizations: result.processedOrganizations,
|
||||
totalChargedAmount: result.totalChargedAmount,
|
||||
duration: `${duration}ms`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
logger.error('Daily billing check completed with errors', {
|
||||
processedUsers: result.processedUsers,
|
||||
processedOrganizations: result.processedOrganizations,
|
||||
totalChargedAmount: result.totalChargedAmount,
|
||||
errorCount: result.errors.length,
|
||||
errors: result.errors,
|
||||
duration: `${duration}ms`,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
summary: {
|
||||
processedUsers: result.processedUsers,
|
||||
processedOrganizations: result.processedOrganizations,
|
||||
totalChargedAmount: result.totalChargedAmount,
|
||||
errorCount: result.errors.length,
|
||||
duration: `${duration}ms`,
|
||||
},
|
||||
errors: result.errors,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
} catch (error) {
|
||||
logger.error('Fatal error in monthly billing cron job', { error })
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Internal server error during daily billing check',
|
||||
details: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET endpoint for manual testing and health checks
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const authError = verifyCronAuth(request, 'daily billing check health check')
|
||||
if (authError) {
|
||||
return authError
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
status: 'ready',
|
||||
message:
|
||||
'Daily billing check cron job is ready to process users and organizations with periods ending today',
|
||||
currentDate: new Date().toISOString().split('T')[0],
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error in billing health check', { error })
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: 'error',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
import { env } from '@/lib/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { db } from '@/db'
|
||||
import { subscription as subscriptionTable, user } from '@/db/schema'
|
||||
|
||||
const logger = createLogger('BillingPortal')
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const session = await getSession()
|
||||
|
||||
try {
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json().catch(() => ({}))
|
||||
const context: 'user' | 'organization' =
|
||||
body?.context === 'organization' ? 'organization' : 'user'
|
||||
const organizationId: string | undefined = body?.organizationId || undefined
|
||||
const returnUrl: string =
|
||||
body?.returnUrl || `${env.NEXT_PUBLIC_APP_URL}/workspace?billing=updated`
|
||||
|
||||
const stripe = requireStripeClient()
|
||||
|
||||
let stripeCustomerId: string | null = null
|
||||
|
||||
if (context === 'organization') {
|
||||
if (!organizationId) {
|
||||
return NextResponse.json({ error: 'organizationId is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({ customer: subscriptionTable.stripeCustomerId })
|
||||
.from(subscriptionTable)
|
||||
.where(
|
||||
and(
|
||||
eq(subscriptionTable.referenceId, organizationId),
|
||||
eq(subscriptionTable.status, 'active')
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
stripeCustomerId = rows.length > 0 ? rows[0].customer || null : null
|
||||
} else {
|
||||
const rows = await db
|
||||
.select({ customer: user.stripeCustomerId })
|
||||
.from(user)
|
||||
.where(eq(user.id, session.user.id))
|
||||
.limit(1)
|
||||
|
||||
stripeCustomerId = rows.length > 0 ? rows[0].customer || null : null
|
||||
}
|
||||
|
||||
if (!stripeCustomerId) {
|
||||
logger.error('Stripe customer not found for portal session', {
|
||||
context,
|
||||
organizationId,
|
||||
userId: session.user.id,
|
||||
})
|
||||
return NextResponse.json({ error: 'Stripe customer not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const portal = await stripe.billingPortal.sessions.create({
|
||||
customer: stripeCustomerId,
|
||||
return_url: returnUrl,
|
||||
})
|
||||
|
||||
return NextResponse.json({ url: portal.url })
|
||||
} catch (error) {
|
||||
logger.error('Failed to create billing portal session', { error })
|
||||
return NextResponse.json({ error: 'Failed to create billing portal session' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { getSimplifiedBillingSummary } from '@/lib/billing/core/billing'
|
||||
import { getOrganizationBillingData } from '@/lib/billing/core/organization-billing'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { db } from '@/db'
|
||||
import { member, userStats } from '@/db/schema'
|
||||
import { member } from '@/db/schema'
|
||||
|
||||
const logger = createLogger('UnifiedBillingAPI')
|
||||
|
||||
@@ -45,16 +45,6 @@ export async function GET(request: NextRequest) {
|
||||
if (context === 'user') {
|
||||
// Get user billing (may include organization if they're part of one)
|
||||
billingData = await getSimplifiedBillingSummary(session.user.id, contextId || undefined)
|
||||
// Attach billingBlocked status for the current user
|
||||
const stats = await db
|
||||
.select({ blocked: userStats.billingBlocked })
|
||||
.from(userStats)
|
||||
.where(eq(userStats.userId, session.user.id))
|
||||
.limit(1)
|
||||
billingData = {
|
||||
...billingData,
|
||||
billingBlocked: stats.length > 0 ? !!stats[0].blocked : false,
|
||||
}
|
||||
} else {
|
||||
// Get user role in organization for permission checks first
|
||||
const memberRecord = await db
|
||||
@@ -88,10 +78,8 @@ export async function GET(request: NextRequest) {
|
||||
subscriptionStatus: rawBillingData.subscriptionStatus,
|
||||
totalSeats: rawBillingData.totalSeats,
|
||||
usedSeats: rawBillingData.usedSeats,
|
||||
seatsCount: rawBillingData.seatsCount,
|
||||
totalCurrentUsage: rawBillingData.totalCurrentUsage,
|
||||
totalUsageLimit: rawBillingData.totalUsageLimit,
|
||||
minimumBillingAmount: rawBillingData.minimumBillingAmount,
|
||||
averageUsagePerMember: rawBillingData.averageUsagePerMember,
|
||||
billingPeriodStart: rawBillingData.billingPeriodStart?.toISOString() || null,
|
||||
billingPeriodEnd: rawBillingData.billingPeriodEnd?.toISOString() || null,
|
||||
@@ -104,25 +92,11 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const userRole = memberRecord[0].role
|
||||
|
||||
// Include the requesting user's blocked flag as well so UI can reflect it
|
||||
const stats = await db
|
||||
.select({ blocked: userStats.billingBlocked })
|
||||
.from(userStats)
|
||||
.where(eq(userStats.userId, session.user.id))
|
||||
.limit(1)
|
||||
|
||||
// Merge blocked flag into data for convenience
|
||||
billingData = {
|
||||
...billingData,
|
||||
billingBlocked: stats.length > 0 ? !!stats[0].blocked : false,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
context,
|
||||
data: billingData,
|
||||
userRole,
|
||||
billingBlocked: billingData.billingBlocked,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user