mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-24 22:38:00 -05:00
Compare commits
6 Commits
fix/copilo
...
fix/nested
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf22dd75ad | ||
|
|
eb767b5ede | ||
|
|
594bcac5f2 | ||
|
|
d3f20311d0 | ||
|
|
587d44ad6f | ||
|
|
8bf2e69942 |
@@ -124,44 +124,11 @@ Choose between four types of loops:
|
|||||||
3. Drag other blocks inside the loop container
|
3. Drag other blocks inside the loop container
|
||||||
4. Connect the blocks as needed
|
4. Connect the blocks as needed
|
||||||
|
|
||||||
### Referencing Loop Data
|
### Accessing Results
|
||||||
|
|
||||||
There's an important distinction between referencing loop data from **inside** vs **outside** the loop:
|
After a loop completes, you can access aggregated results:
|
||||||
|
|
||||||
<Tabs items={['Inside the Loop', 'Outside the Loop']}>
|
- **`<loop.results>`**: Array of results from all loop iterations
|
||||||
<Tab>
|
|
||||||
**Inside the loop**, use `<loop.>` references to access the current iteration context:
|
|
||||||
|
|
||||||
- **`<loop.index>`**: Current iteration number (0-based)
|
|
||||||
- **`<loop.currentItem>`**: Current item being processed (forEach only)
|
|
||||||
- **`<loop.items>`**: Full collection being iterated (forEach only)
|
|
||||||
|
|
||||||
```
|
|
||||||
// Inside a Function block within the loop
|
|
||||||
const idx = <loop.index>; // 0, 1, 2, ...
|
|
||||||
const item = <loop.currentItem>; // Current item
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
These references are only available for blocks **inside** the loop container. They give you access to the current iteration's context.
|
|
||||||
</Callout>
|
|
||||||
</Tab>
|
|
||||||
<Tab>
|
|
||||||
**Outside the loop** (after it completes), reference the loop block by its name to access aggregated results:
|
|
||||||
|
|
||||||
- **`<LoopBlockName.results>`**: Array of results from all iterations
|
|
||||||
|
|
||||||
```
|
|
||||||
// If your loop block is named "Process Items"
|
|
||||||
const allResults = <processitems.results>;
|
|
||||||
// Returns: [result1, result2, result3, ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
After the loop completes, use the loop's block name (not `loop.`) to access the collected results. The block name is normalized (lowercase, no spaces).
|
|
||||||
</Callout>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Example Use Cases
|
## Example Use Cases
|
||||||
|
|
||||||
@@ -217,29 +184,28 @@ Variables (i=0) → Loop (While i<10) → Agent (Process) → Variables (i++)
|
|||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab>
|
<Tab>
|
||||||
Available **inside** the loop only:
|
|
||||||
<ul className="list-disc space-y-2 pl-6">
|
<ul className="list-disc space-y-2 pl-6">
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<loop.index>"}</strong>: Current iteration number (0-based)
|
<strong>loop.currentItem</strong>: Current item being processed
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<loop.currentItem>"}</strong>: Current item being processed (forEach only)
|
<strong>loop.index</strong>: Current iteration number (0-based)
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<loop.items>"}</strong>: Full collection (forEach only)
|
<strong>loop.items</strong>: Full collection (forEach loops)
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab>
|
<Tab>
|
||||||
<ul className="list-disc space-y-2 pl-6">
|
<ul className="list-disc space-y-2 pl-6">
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<blockname.results>"}</strong>: Array of all iteration results (accessed via block name)
|
<strong>loop.results</strong>: Array of all iteration results
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Structure</strong>: Results maintain iteration order
|
<strong>Structure</strong>: Results maintain iteration order
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Access</strong>: Available in blocks after the loop completes
|
<strong>Access</strong>: Available in blocks after the loop
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -76,44 +76,11 @@ Choose between two types of parallel execution:
|
|||||||
3. Drag a single block inside the parallel container
|
3. Drag a single block inside the parallel container
|
||||||
4. Connect the block as needed
|
4. Connect the block as needed
|
||||||
|
|
||||||
### Referencing Parallel Data
|
### Accessing Results
|
||||||
|
|
||||||
There's an important distinction between referencing parallel data from **inside** vs **outside** the parallel block:
|
After a parallel block completes, you can access aggregated results:
|
||||||
|
|
||||||
<Tabs items={['Inside the Parallel', 'Outside the Parallel']}>
|
- **`<parallel.results>`**: Array of results from all parallel instances
|
||||||
<Tab>
|
|
||||||
**Inside the parallel**, use `<parallel.>` references to access the current instance context:
|
|
||||||
|
|
||||||
- **`<parallel.index>`**: Current instance number (0-based)
|
|
||||||
- **`<parallel.currentItem>`**: Item for this instance (collection-based only)
|
|
||||||
- **`<parallel.items>`**: Full collection being distributed (collection-based only)
|
|
||||||
|
|
||||||
```
|
|
||||||
// Inside a Function block within the parallel
|
|
||||||
const idx = <parallel.index>; // 0, 1, 2, ...
|
|
||||||
const item = <parallel.currentItem>; // This instance's item
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
These references are only available for blocks **inside** the parallel container. They give you access to the current instance's context.
|
|
||||||
</Callout>
|
|
||||||
</Tab>
|
|
||||||
<Tab>
|
|
||||||
**Outside the parallel** (after it completes), reference the parallel block by its name to access aggregated results:
|
|
||||||
|
|
||||||
- **`<ParallelBlockName.results>`**: Array of results from all instances
|
|
||||||
|
|
||||||
```
|
|
||||||
// If your parallel block is named "Process Tasks"
|
|
||||||
const allResults = <processtasks.results>;
|
|
||||||
// Returns: [result1, result2, result3, ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
After the parallel completes, use the parallel's block name (not `parallel.`) to access the collected results. The block name is normalized (lowercase, no spaces).
|
|
||||||
</Callout>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Example Use Cases
|
## Example Use Cases
|
||||||
|
|
||||||
@@ -131,11 +98,11 @@ Parallel (["gpt-4o", "claude-3.7-sonnet", "gemini-2.5-pro"]) → Agent → Evalu
|
|||||||
|
|
||||||
### Result Aggregation
|
### Result Aggregation
|
||||||
|
|
||||||
Results from all parallel instances are automatically collected and accessible via the block name:
|
Results from all parallel instances are automatically collected:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In a Function block after a parallel named "Process Tasks"
|
// In a Function block after the parallel
|
||||||
const allResults = <processtasks.results>;
|
const allResults = input.parallel.results;
|
||||||
// Returns: [result1, result2, result3, ...]
|
// Returns: [result1, result2, result3, ...]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -191,26 +158,25 @@ Understanding when to use each:
|
|||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab>
|
<Tab>
|
||||||
Available **inside** the parallel only:
|
|
||||||
<ul className="list-disc space-y-2 pl-6">
|
<ul className="list-disc space-y-2 pl-6">
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<parallel.index>"}</strong>: Instance number (0-based)
|
<strong>parallel.currentItem</strong>: Item for this instance
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<parallel.currentItem>"}</strong>: Item for this instance (collection-based only)
|
<strong>parallel.index</strong>: Instance number (0-based)
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<parallel.items>"}</strong>: Full collection (collection-based only)
|
<strong>parallel.items</strong>: Full collection (collection-based)
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab>
|
<Tab>
|
||||||
<ul className="list-disc space-y-2 pl-6">
|
<ul className="list-disc space-y-2 pl-6">
|
||||||
<li>
|
<li>
|
||||||
<strong>{"<blockname.results>"}</strong>: Array of all instance results (accessed via block name)
|
<strong>parallel.results</strong>: Array of all instance results
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Access</strong>: Available in blocks after the parallel completes
|
<strong>Access</strong>: Available in blocks after the parallel
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -85,10 +85,10 @@ vi.mock('@/lib/execution/isolated-vm', () => ({
|
|||||||
vi.mock('@sim/logger', () => loggerMock)
|
vi.mock('@sim/logger', () => loggerMock)
|
||||||
|
|
||||||
vi.mock('@/lib/auth/hybrid', () => ({
|
vi.mock('@/lib/auth/hybrid', () => ({
|
||||||
checkInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: true,
|
success: true,
|
||||||
userId: 'user-123',
|
userId: 'user-123',
|
||||||
authType: 'internal_jwt',
|
authType: 'session',
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -119,8 +119,8 @@ describe('Function Execute API Route', () => {
|
|||||||
|
|
||||||
describe('Security Tests', () => {
|
describe('Security Tests', () => {
|
||||||
it('should reject unauthorized requests', async () => {
|
it('should reject unauthorized requests', async () => {
|
||||||
const { checkInternalAuth } = await import('@/lib/auth/hybrid')
|
const { checkHybridAuth } = await import('@/lib/auth/hybrid')
|
||||||
vi.mocked(checkInternalAuth).mockResolvedValueOnce({
|
vi.mocked(checkHybridAuth).mockResolvedValueOnce({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { isE2bEnabled } from '@/lib/core/config/feature-flags'
|
import { isE2bEnabled } from '@/lib/core/config/feature-flags'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { executeInE2B } from '@/lib/execution/e2b'
|
import { executeInE2B } from '@/lib/execution/e2b'
|
||||||
@@ -582,7 +582,7 @@ export async function POST(req: NextRequest) {
|
|||||||
let resolvedCode = '' // Store resolved code for error reporting
|
let resolvedCode = '' // Store resolved code for error reporting
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(req)
|
const auth = await checkHybridAuth(req)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized function execution attempt`)
|
logger.warn(`[${requestId}] Unauthorized function execution attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { account } from '@sim/db/schema'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { eq } from 'drizzle-orm'
|
import { eq } from 'drizzle-orm'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
|
||||||
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request, { requireWorkflowId: false })
|
const auth = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ describe('Custom Tools API Routes', () => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||||
checkSessionOrInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: true,
|
success: true,
|
||||||
userId: 'user-123',
|
userId: 'user-123',
|
||||||
authType: 'session',
|
authType: 'session',
|
||||||
@@ -254,7 +254,7 @@ describe('Custom Tools API Routes', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||||
checkSessionOrInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
}),
|
}),
|
||||||
@@ -304,7 +304,7 @@ describe('Custom Tools API Routes', () => {
|
|||||||
describe('POST /api/tools/custom', () => {
|
describe('POST /api/tools/custom', () => {
|
||||||
it('should reject unauthorized requests', async () => {
|
it('should reject unauthorized requests', async () => {
|
||||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||||
checkSessionOrInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
}),
|
}),
|
||||||
@@ -390,7 +390,7 @@ describe('Custom Tools API Routes', () => {
|
|||||||
|
|
||||||
it('should prevent unauthorized deletion of user-scoped tool', async () => {
|
it('should prevent unauthorized deletion of user-scoped tool', async () => {
|
||||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||||
checkSessionOrInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: true,
|
success: true,
|
||||||
userId: 'user-456',
|
userId: 'user-456',
|
||||||
authType: 'session',
|
authType: 'session',
|
||||||
@@ -413,7 +413,7 @@ describe('Custom Tools API Routes', () => {
|
|||||||
|
|
||||||
it('should reject unauthorized requests', async () => {
|
it('should reject unauthorized requests', async () => {
|
||||||
vi.doMock('@/lib/auth/hybrid', () => ({
|
vi.doMock('@/lib/auth/hybrid', () => ({
|
||||||
checkSessionOrInternalAuth: vi.fn().mockResolvedValue({
|
checkHybridAuth: vi.fn().mockResolvedValue({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Unauthorized',
|
error: 'Unauthorized',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { and, desc, eq, isNull, or } from 'drizzle-orm'
|
import { and, desc, eq, isNull, or } from 'drizzle-orm'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { upsertCustomTools } from '@/lib/workflows/custom-tools/operations'
|
import { upsertCustomTools } from '@/lib/workflows/custom-tools/operations'
|
||||||
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
|
||||||
@@ -42,8 +42,8 @@ export async function GET(request: NextRequest) {
|
|||||||
const workflowId = searchParams.get('workflowId')
|
const workflowId = searchParams.get('workflowId')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use session/internal auth to support session and internal JWT (no API key access)
|
// Use hybrid auth to support session, API key, and internal JWT
|
||||||
const authResult = await checkSessionOrInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized custom tools access attempt`)
|
logger.warn(`[${requestId}] Unauthorized custom tools access attempt`)
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
@@ -69,8 +69,8 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check workspace permissions
|
// Check workspace permissions
|
||||||
// For internal JWT with workflowId: checkSessionOrInternalAuth already resolved userId from workflow owner
|
// For internal JWT with workflowId: checkHybridAuth already resolved userId from workflow owner
|
||||||
// For session: verify user has access to the workspace
|
// For session/API key: verify user has access to the workspace
|
||||||
// For legacy (no workspaceId): skip workspace check, rely on userId match
|
// For legacy (no workspaceId): skip workspace check, rely on userId match
|
||||||
if (resolvedWorkspaceId && !(authResult.authType === 'internal_jwt' && workflowId)) {
|
if (resolvedWorkspaceId && !(authResult.authType === 'internal_jwt' && workflowId)) {
|
||||||
const userPermission = await getUserEntityPermissions(
|
const userPermission = await getUserEntityPermissions(
|
||||||
@@ -116,8 +116,8 @@ export async function POST(req: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use session/internal auth (no API key access)
|
// Use hybrid auth (though this endpoint is only called from UI)
|
||||||
const authResult = await checkSessionOrInternalAuth(req, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(req, { requireWorkflowId: false })
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized custom tools update attempt`)
|
logger.warn(`[${requestId}] Unauthorized custom tools update attempt`)
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
@@ -193,8 +193,8 @@ export async function DELETE(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use session/internal auth (no API key access)
|
// Use hybrid auth (though this endpoint is only called from UI)
|
||||||
const authResult = await checkSessionOrInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized custom tool deletion attempt`)
|
logger.warn(`[${requestId}] Unauthorized custom tool deletion attempt`)
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateNumericId } from '@/lib/core/security/input-validation'
|
import { validateNumericId } from '@/lib/core/security/input-validation'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Discord send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Discord send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail add label attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail add label attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail archive attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail archive attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail delete attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail delete attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -35,7 +35,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail draft attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail draft attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail mark read attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail mark read attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail mark unread attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail mark unread attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail move attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail move attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail remove label attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail remove label attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -35,7 +35,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Gmail unarchive attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Gmail unarchive attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -56,7 +56,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Google Drive upload attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Google Drive upload attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateImageUrl } from '@/lib/core/security/input-validation'
|
import { validateImageUrl } from '@/lib/core/security/input-validation'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export async function GET(request: NextRequest) {
|
|||||||
const imageUrl = url.searchParams.get('url')
|
const imageUrl = url.searchParams.get('url')
|
||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.error(`[${requestId}] Authentication failed for image proxy:`, authResult.error)
|
logger.error(`[${requestId}] Authentication failed for image proxy:`, authResult.error)
|
||||||
return new NextResponse('Unauthorized', { status: 401 })
|
return new NextResponse('Unauthorized', { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { Resend } from 'resend'
|
import { Resend } from 'resend'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized mail send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized mail send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -18,7 +18,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Teams chat delete attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Teams chat delete attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Teams channel write attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Teams channel write attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Teams chat write attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Teams chat write attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||||
import { StorageService } from '@/lib/uploads'
|
import { StorageService } from '@/lib/uploads'
|
||||||
@@ -30,7 +30,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Mistral parse attempt`, {
|
logger.warn(`[${requestId}] Unauthorized Mistral parse attempt`, {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { buildDeleteQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
import { buildDeleteQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLDeleteAPI')
|
const logger = createLogger('MySQLDeleteAPI')
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL delete attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL delete attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
|
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLExecuteAPI')
|
const logger = createLogger('MySQLExecuteAPI')
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL execute attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL execute attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { buildInsertQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
import { buildInsertQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLInsertAPI')
|
const logger = createLogger('MySQLInsertAPI')
|
||||||
@@ -43,7 +43,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL insert attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL insert attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createMySQLConnection, executeIntrospect } from '@/app/api/tools/mysql/utils'
|
import { createMySQLConnection, executeIntrospect } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLIntrospectAPI')
|
const logger = createLogger('MySQLIntrospectAPI')
|
||||||
@@ -20,7 +20,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL introspect attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL introspect attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
|
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLQueryAPI')
|
const logger = createLogger('MySQLQueryAPI')
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL query attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL query attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { buildUpdateQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
import { buildUpdateQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
|
||||||
|
|
||||||
const logger = createLogger('MySQLUpdateAPI')
|
const logger = createLogger('MySQLUpdateAPI')
|
||||||
@@ -41,7 +41,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized MySQL update attempt`)
|
logger.warn(`[${requestId}] Unauthorized MySQL update attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import * as XLSX from 'xlsx'
|
import * as XLSX from 'xlsx'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation'
|
import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import {
|
import {
|
||||||
@@ -39,7 +39,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized OneDrive upload attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized OneDrive upload attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -18,7 +18,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook copy attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook copy attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -17,7 +17,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook delete attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook delete attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -25,7 +25,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook draft attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook draft attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -17,7 +17,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook mark read attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook mark read attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -17,7 +17,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook mark unread attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook mark unread attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -18,7 +18,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook move attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook move attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -27,7 +27,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Outlook send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Outlook send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createPostgresConnection, executeDelete } from '@/app/api/tools/postgresql/utils'
|
import { createPostgresConnection, executeDelete } from '@/app/api/tools/postgresql/utils'
|
||||||
|
|
||||||
const logger = createLogger('PostgreSQLDeleteAPI')
|
const logger = createLogger('PostgreSQLDeleteAPI')
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL delete attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL delete attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createPostgresConnection,
|
createPostgresConnection,
|
||||||
executeQuery,
|
executeQuery,
|
||||||
@@ -25,7 +25,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL execute attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL execute attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createPostgresConnection, executeInsert } from '@/app/api/tools/postgresql/utils'
|
import { createPostgresConnection, executeInsert } from '@/app/api/tools/postgresql/utils'
|
||||||
|
|
||||||
const logger = createLogger('PostgreSQLInsertAPI')
|
const logger = createLogger('PostgreSQLInsertAPI')
|
||||||
@@ -43,7 +43,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL insert attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL insert attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createPostgresConnection, executeIntrospect } from '@/app/api/tools/postgresql/utils'
|
import { createPostgresConnection, executeIntrospect } from '@/app/api/tools/postgresql/utils'
|
||||||
|
|
||||||
const logger = createLogger('PostgreSQLIntrospectAPI')
|
const logger = createLogger('PostgreSQLIntrospectAPI')
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL introspect attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL introspect attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createPostgresConnection, executeQuery } from '@/app/api/tools/postgresql/utils'
|
import { createPostgresConnection, executeQuery } from '@/app/api/tools/postgresql/utils'
|
||||||
|
|
||||||
const logger = createLogger('PostgreSQLQueryAPI')
|
const logger = createLogger('PostgreSQLQueryAPI')
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL query attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL query attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createPostgresConnection, executeUpdate } from '@/app/api/tools/postgresql/utils'
|
import { createPostgresConnection, executeUpdate } from '@/app/api/tools/postgresql/utils'
|
||||||
|
|
||||||
const logger = createLogger('PostgreSQLUpdateAPI')
|
const logger = createLogger('PostgreSQLUpdateAPI')
|
||||||
@@ -41,7 +41,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized PostgreSQL update attempt`)
|
logger.warn(`[${requestId}] Unauthorized PostgreSQL update attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||||
import { StorageService } from '@/lib/uploads'
|
import { StorageService } from '@/lib/uploads'
|
||||||
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Pulse parse attempt`, {
|
logger.warn(`[${requestId}] Unauthorized Pulse parse attempt`, {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||||
import { StorageService } from '@/lib/uploads'
|
import { StorageService } from '@/lib/uploads'
|
||||||
@@ -27,7 +27,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Reducto parse attempt`, {
|
logger.warn(`[${requestId}] Unauthorized Reducto parse attempt`, {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CopyObjectCommand, type ObjectCannedACL, S3Client } from '@aws-sdk/clie
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -24,7 +24,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized S3 copy object attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized S3 copy object attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized S3 delete object attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized S3 delete object attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized S3 list objects attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized S3 list objects attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { type ObjectCannedACL, PutObjectCommand, S3Client } from '@aws-sdk/clien
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -27,7 +27,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized S3 put object attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized S3 put object attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { SEARCH_TOOL_COST } from '@/lib/billing/constants'
|
import { SEARCH_TOOL_COST } from '@/lib/billing/constants'
|
||||||
import { env } from '@/lib/core/config/env'
|
import { env } from '@/lib/core/config/env'
|
||||||
import { executeTool } from '@/tools'
|
import { executeTool } from '@/tools'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const { searchParams: urlParams } = new URL(request.url)
|
const { searchParams: urlParams } = new URL(request.url)
|
||||||
const workflowId = urlParams.get('workflowId') || undefined
|
const workflowId = urlParams.get('workflowId') || undefined
|
||||||
|
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
const errorMessage = workflowId ? 'Workflow not found' : authResult.error || 'Unauthorized'
|
const errorMessage = workflowId ? 'Workflow not found' : authResult.error || 'Unauthorized'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { SFTPWrapper } from 'ssh2'
|
import type { SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import {
|
import {
|
||||||
createSftpConnection,
|
createSftpConnection,
|
||||||
@@ -72,7 +72,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SFTP delete attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SFTP delete attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import path from 'path'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { createSftpConnection, getSftp, isPathSafe, sanitizePath } from '@/app/api/tools/sftp/utils'
|
import { createSftpConnection, getSftp, isPathSafe, sanitizePath } from '@/app/api/tools/sftp/utils'
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SFTP download attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SFTP download attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import {
|
import {
|
||||||
createSftpConnection,
|
createSftpConnection,
|
||||||
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SFTP list attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SFTP list attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { SFTPWrapper } from 'ssh2'
|
import type { SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import {
|
import {
|
||||||
createSftpConnection,
|
createSftpConnection,
|
||||||
@@ -60,7 +60,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SFTP mkdir attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SFTP mkdir attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -44,7 +44,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SFTP upload attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SFTP upload attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SharePoint upload attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SharePoint upload attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const SlackAddReactionSchema = z.object({
|
|||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ const SlackDeleteMessageSchema = z.object({
|
|||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { openDMChannel } from '../utils'
|
import { openDMChannel } from '../utils'
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Slack read messages attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Slack read messages attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { sendSlackMessage } from '../utils'
|
import { sendSlackMessage } from '../utils'
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Slack send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Slack send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Slack update message attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Slack update message attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { env } from '@/lib/core/config/env'
|
import { env } from '@/lib/core/config/env'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { type SMSOptions, sendSMS } from '@/lib/messaging/sms/service'
|
import { type SMSOptions, sendSMS } from '@/lib/messaging/sms/service'
|
||||||
@@ -19,7 +19,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SMS send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SMS send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -35,7 +35,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SMTP send attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized SMTP send attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHCheckCommandExistsAPI')
|
const logger = createLogger('SSHCheckCommandExistsAPI')
|
||||||
@@ -21,7 +21,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH check command exists attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH check command exists attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, SFTPWrapper, Stats } from 'ssh2'
|
import type { Client, SFTPWrapper, Stats } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createSSHConnection,
|
createSSHConnection,
|
||||||
getFileType,
|
getFileType,
|
||||||
@@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH check file exists attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH check file exists attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createSSHConnection,
|
createSSHConnection,
|
||||||
escapeShellArg,
|
escapeShellArg,
|
||||||
@@ -28,7 +28,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH create directory attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH create directory attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createSSHConnection,
|
createSSHConnection,
|
||||||
escapeShellArg,
|
escapeShellArg,
|
||||||
@@ -28,7 +28,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH delete file attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH delete file attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, SFTPWrapper } from 'ssh2'
|
import type { Client, SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHDownloadFileAPI')
|
const logger = createLogger('SSHDownloadFileAPI')
|
||||||
@@ -35,7 +35,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH download file attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH download file attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, executeSSHCommand, sanitizeCommand } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, executeSSHCommand, sanitizeCommand } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHExecuteCommandAPI')
|
const logger = createLogger('SSHExecuteCommandAPI')
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH execute command attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH execute command attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHExecuteScriptAPI')
|
const logger = createLogger('SSHExecuteScriptAPI')
|
||||||
@@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH execute script attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH execute script attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHGetSystemInfoAPI')
|
const logger = createLogger('SSHGetSystemInfoAPI')
|
||||||
@@ -20,7 +20,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH get system info attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH get system info attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, FileEntry, SFTPWrapper } from 'ssh2'
|
import type { Client, FileEntry, SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createSSHConnection,
|
createSSHConnection,
|
||||||
getFileType,
|
getFileType,
|
||||||
@@ -61,7 +61,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH list directory attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH list directory attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
createSSHConnection,
|
createSSHConnection,
|
||||||
escapeShellArg,
|
escapeShellArg,
|
||||||
@@ -28,7 +28,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH move/rename attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH move/rename attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, SFTPWrapper } from 'ssh2'
|
import type { Client, SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHReadFileContentAPI')
|
const logger = createLogger('SSHReadFileContentAPI')
|
||||||
@@ -36,7 +36,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH read file content attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH read file content attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, SFTPWrapper } from 'ssh2'
|
import type { Client, SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHUploadFileAPI')
|
const logger = createLogger('SSHUploadFileAPI')
|
||||||
@@ -38,7 +38,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH upload file attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH upload file attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createLogger } from '@sim/logger'
|
|||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import type { Client, SFTPWrapper } from 'ssh2'
|
import type { Client, SFTPWrapper } from 'ssh2'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||||
|
|
||||||
const logger = createLogger('SSHWriteFileContentAPI')
|
const logger = createLogger('SSHWriteFileContentAPI')
|
||||||
@@ -37,7 +37,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = randomUUID().slice(0, 8)
|
const requestId = randomUUID().slice(0, 8)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auth = await checkInternalAuth(request)
|
const auth = await checkHybridAuth(request)
|
||||||
if (!auth.success || !auth.userId) {
|
if (!auth.success || !auth.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized SSH write file content attempt`)
|
logger.warn(`[${requestId}] Unauthorized SSH write file content attempt`)
|
||||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { extractAudioFromVideo, isVideoFile } from '@/lib/audio/extractor'
|
import { extractAudioFromVideo, isVideoFile } from '@/lib/audio/extractor'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
import type { UserFile } from '@/executor/types'
|
import type { UserFile } from '@/executor/types'
|
||||||
import type { TranscriptSegment } from '@/tools/stt/types'
|
import type { TranscriptSegment } from '@/tools/stt/types'
|
||||||
@@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
|
|||||||
logger.info(`[${requestId}] STT transcription request started`)
|
logger.info(`[${requestId}] STT transcription request started`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, {
|
const authResult = await checkHybridAuth(request, {
|
||||||
requireWorkflowId: false,
|
requireWorkflowId: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import crypto from 'crypto'
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import {
|
import {
|
||||||
validateAwsRegion,
|
validateAwsRegion,
|
||||||
validateExternalUrl,
|
validateExternalUrl,
|
||||||
@@ -292,7 +292,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success || !authResult.userId) {
|
if (!authResult.success || !authResult.userId) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Textract parse attempt`, {
|
logger.warn(`[${requestId}] Unauthorized Textract parse attempt`, {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
import { NextResponse } from 'next/server'
|
import { NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||||
import { StorageService } from '@/lib/uploads'
|
import { StorageService } from '@/lib/uploads'
|
||||||
@@ -10,7 +10,7 @@ const logger = createLogger('ProxyTTSAPI')
|
|||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.error('Authentication failed for TTS proxy:', authResult.error)
|
logger.error('Authentication failed for TTS proxy:', authResult.error)
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
import { NextResponse } from 'next/server'
|
import { NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||||
import { StorageService } from '@/lib/uploads'
|
import { StorageService } from '@/lib/uploads'
|
||||||
@@ -87,7 +87,7 @@ export async function POST(request: NextRequest) {
|
|||||||
logger.info(`[${requestId}] TTS unified request started`)
|
logger.info(`[${requestId}] TTS unified request started`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.error('Authentication failed for TTS unified proxy:', authResult.error)
|
logger.error('Authentication failed for TTS unified proxy:', authResult.error)
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
import type { UserFile } from '@/executor/types'
|
import type { UserFile } from '@/executor/types'
|
||||||
import type { VideoRequestBody } from '@/tools/video/types'
|
import type { VideoRequestBody } from '@/tools/video/types'
|
||||||
@@ -15,7 +15,7 @@ export async function POST(request: NextRequest) {
|
|||||||
logger.info(`[${requestId}] Video generation request started`)
|
logger.info(`[${requestId}] Video generation request started`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
||||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||||
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized Vision analyze attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized Vision analyze attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createLogger } from '@sim/logger'
|
import { createLogger } from '@sim/logger'
|
||||||
import { type NextRequest, NextResponse } from 'next/server'
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||||
import { generateRequestId } from '@/lib/core/utils/request'
|
import { generateRequestId } from '@/lib/core/utils/request'
|
||||||
import {
|
import {
|
||||||
getFileExtension,
|
getFileExtension,
|
||||||
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const requestId = generateRequestId()
|
const requestId = generateRequestId()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||||
|
|
||||||
if (!authResult.success) {
|
if (!authResult.success) {
|
||||||
logger.warn(`[${requestId}] Unauthorized WordPress upload attempt: ${authResult.error}`)
|
logger.warn(`[${requestId}] Unauthorized WordPress upload attempt: ${authResult.error}`)
|
||||||
|
|||||||
@@ -1260,6 +1260,7 @@ async function handleRun(
|
|||||||
const instance = getClientTool(toolCall.id)
|
const instance = getClientTool(toolCall.id)
|
||||||
|
|
||||||
if (!instance && isIntegrationTool(toolCall.name)) {
|
if (!instance && isIntegrationTool(toolCall.name)) {
|
||||||
|
setToolCallState(toolCall, 'executing')
|
||||||
onStateChange?.('executing')
|
onStateChange?.('executing')
|
||||||
try {
|
try {
|
||||||
await useCopilotStore.getState().executeIntegrationTool(toolCall.id)
|
await useCopilotStore.getState().executeIntegrationTool(toolCall.id)
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ export function DeployModal({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{apiDeployWarnings.length > 0 && (
|
{apiDeployWarnings.length > 0 && (
|
||||||
<div className='mb-3 rounded-[4px] border border-amber-500/30 bg-amber-500/10 p-3 text-amber-700 text-sm dark:text-amber-400'>
|
<div className='mb-3 rounded-[4px] border border-amber-500/30 bg-amber-500/10 p-3 text-amber-700 dark:text-amber-400 text-sm'>
|
||||||
<div className='font-semibold'>Deployment Warning</div>
|
<div className='font-semibold'>Deployment Warning</div>
|
||||||
{apiDeployWarnings.map((warning, index) => (
|
{apiDeployWarnings.map((warning, index) => (
|
||||||
<div key={index}>{warning}</div>
|
<div key={index}>{warning}</div>
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ import { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
|
|||||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||||
import { getUniqueBlockName, prepareBlockState } from '@/stores/workflows/utils'
|
import { getUniqueBlockName, prepareBlockState } from '@/stores/workflows/utils'
|
||||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||||
import type { BlockState } from '@/stores/workflows/workflow/types'
|
|
||||||
|
|
||||||
/** Lazy-loaded components for non-critical UI that can load after initial render */
|
/** Lazy-loaded components for non-critical UI that can load after initial render */
|
||||||
const LazyChat = lazy(() =>
|
const LazyChat = lazy(() =>
|
||||||
@@ -536,7 +535,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
return edgesToFilter.filter((edge) => {
|
return edgesToFilter.filter((edge) => {
|
||||||
const sourceBlock = blocks[edge.source]
|
const sourceBlock = blocks[edge.source]
|
||||||
const targetBlock = blocks[edge.target]
|
const targetBlock = blocks[edge.target]
|
||||||
return Boolean(sourceBlock && targetBlock)
|
if (!sourceBlock || !targetBlock) return false
|
||||||
|
return !isAnnotationOnlyBlock(sourceBlock.type) && !isAnnotationOnlyBlock(targetBlock.type)
|
||||||
})
|
})
|
||||||
}, [edges, isShowingDiff, isDiffReady, diffAnalysis, blocks])
|
}, [edges, isShowingDiff, isDiffReady, diffAnalysis, blocks])
|
||||||
|
|
||||||
@@ -1097,13 +1097,6 @@ const WorkflowContent = React.memo(() => {
|
|||||||
[collaborativeBatchRemoveEdges]
|
[collaborativeBatchRemoveEdges]
|
||||||
)
|
)
|
||||||
|
|
||||||
const isAutoConnectSourceCandidate = useCallback((block: BlockState): boolean => {
|
|
||||||
if (!block.enabled) return false
|
|
||||||
if (block.type === 'response') return false
|
|
||||||
if (isAnnotationOnlyBlock(block.type)) return false
|
|
||||||
return true
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
/** Finds the closest block to a position for auto-connect. */
|
/** Finds the closest block to a position for auto-connect. */
|
||||||
const findClosestOutput = useCallback(
|
const findClosestOutput = useCallback(
|
||||||
(newNodePosition: { x: number; y: number }): BlockData | null => {
|
(newNodePosition: { x: number; y: number }): BlockData | null => {
|
||||||
@@ -1116,7 +1109,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
position: { x: number; y: number }
|
position: { x: number; y: number }
|
||||||
distanceSquared: number
|
distanceSquared: number
|
||||||
} | null>((acc, [id, block]) => {
|
} | null>((acc, [id, block]) => {
|
||||||
if (!isAutoConnectSourceCandidate(block)) return acc
|
if (!block.enabled) return acc
|
||||||
|
if (block.type === 'response') return acc
|
||||||
const node = nodeIndex.get(id)
|
const node = nodeIndex.get(id)
|
||||||
if (!node) return acc
|
if (!node) return acc
|
||||||
|
|
||||||
@@ -1146,7 +1140,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
position: closest.position,
|
position: closest.position,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[blocks, getNodes, getNodeAnchorPosition, isPointInLoopNode, isAutoConnectSourceCandidate]
|
[blocks, getNodes, getNodeAnchorPosition, isPointInLoopNode]
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Determines the appropriate source handle based on block type. */
|
/** Determines the appropriate source handle based on block type. */
|
||||||
@@ -1214,8 +1208,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
position: { x: number; y: number }
|
position: { x: number; y: number }
|
||||||
distanceSquared: number
|
distanceSquared: number
|
||||||
} | null>((acc, block) => {
|
} | null>((acc, block) => {
|
||||||
const blockState = blocks[block.id]
|
if (block.type === 'response') return acc
|
||||||
if (!blockState || !isAutoConnectSourceCandidate(blockState)) return acc
|
|
||||||
const distanceSquared =
|
const distanceSquared =
|
||||||
(block.position.x - targetPosition.x) ** 2 + (block.position.y - targetPosition.y) ** 2
|
(block.position.x - targetPosition.x) ** 2 + (block.position.y - targetPosition.y) ** 2
|
||||||
if (!acc || distanceSquared < acc.distanceSquared) {
|
if (!acc || distanceSquared < acc.distanceSquared) {
|
||||||
@@ -1232,7 +1225,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
},
|
},
|
||||||
[blocks, isAutoConnectSourceCandidate]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1248,6 +1241,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
position: { x: number; y: number },
|
position: { x: number; y: number },
|
||||||
targetBlockId: string,
|
targetBlockId: string,
|
||||||
options: {
|
options: {
|
||||||
|
blockType: string
|
||||||
|
enableTriggerMode?: boolean
|
||||||
targetParentId?: string | null
|
targetParentId?: string | null
|
||||||
existingChildBlocks?: { id: string; type: string; position: { x: number; y: number } }[]
|
existingChildBlocks?: { id: string; type: string; position: { x: number; y: number } }[]
|
||||||
containerId?: string
|
containerId?: string
|
||||||
@@ -1255,6 +1250,17 @@ const WorkflowContent = React.memo(() => {
|
|||||||
): Edge | undefined => {
|
): Edge | undefined => {
|
||||||
if (!autoConnectRef.current) return undefined
|
if (!autoConnectRef.current) return undefined
|
||||||
|
|
||||||
|
// Don't auto-connect starter or annotation-only blocks
|
||||||
|
if (options.blockType === 'starter' || isAnnotationOnlyBlock(options.blockType)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if target is a trigger block
|
||||||
|
const targetBlockConfig = getBlock(options.blockType)
|
||||||
|
const isTargetTrigger =
|
||||||
|
options.enableTriggerMode || targetBlockConfig?.category === 'triggers'
|
||||||
|
if (isTargetTrigger) return undefined
|
||||||
|
|
||||||
// Case 1: Adding block inside a container with existing children
|
// Case 1: Adding block inside a container with existing children
|
||||||
if (options.existingChildBlocks && options.existingChildBlocks.length > 0) {
|
if (options.existingChildBlocks && options.existingChildBlocks.length > 0) {
|
||||||
const closestBlock = findClosestBlockInSet(options.existingChildBlocks, position)
|
const closestBlock = findClosestBlockInSet(options.existingChildBlocks, position)
|
||||||
@@ -1362,6 +1368,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
const name = getUniqueBlockName(baseName, blocks)
|
const name = getUniqueBlockName(baseName, blocks)
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
|
||||||
|
blockType: data.type,
|
||||||
targetParentId: null,
|
targetParentId: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1432,6 +1439,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
|
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(relativePosition, id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(relativePosition, id, {
|
||||||
|
blockType: data.type,
|
||||||
|
enableTriggerMode: data.enableTriggerMode,
|
||||||
targetParentId: containerInfo.loopId,
|
targetParentId: containerInfo.loopId,
|
||||||
existingChildBlocks,
|
existingChildBlocks,
|
||||||
containerId: containerInfo.loopId,
|
containerId: containerInfo.loopId,
|
||||||
@@ -1460,6 +1469,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
if (checkTriggerConstraints(data.type)) return
|
if (checkTriggerConstraints(data.type)) return
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
|
||||||
|
blockType: data.type,
|
||||||
|
enableTriggerMode: data.enableTriggerMode,
|
||||||
targetParentId: null,
|
targetParentId: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1515,6 +1526,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
const name = getUniqueBlockName(baseName, blocks)
|
const name = getUniqueBlockName(baseName, blocks)
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
|
||||||
|
blockType: type,
|
||||||
targetParentId: null,
|
targetParentId: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1550,6 +1562,8 @@ const WorkflowContent = React.memo(() => {
|
|||||||
const name = getUniqueBlockName(baseName, blocks)
|
const name = getUniqueBlockName(baseName, blocks)
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
|
||||||
|
blockType: type,
|
||||||
|
enableTriggerMode,
|
||||||
targetParentId: null,
|
targetParentId: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -2350,6 +2364,24 @@ const WorkflowContent = React.memo(() => {
|
|||||||
|
|
||||||
if (!sourceNode || !targetNode) return
|
if (!sourceNode || !targetNode) return
|
||||||
|
|
||||||
|
// Prevent connections to/from annotation-only blocks (non-executable)
|
||||||
|
if (
|
||||||
|
isAnnotationOnlyBlock(sourceNode.data?.type) ||
|
||||||
|
isAnnotationOnlyBlock(targetNode.data?.type)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent incoming connections to trigger blocks (webhook, schedule, etc.)
|
||||||
|
if (targetNode.data?.config?.category === 'triggers') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent incoming connections to starter blocks (still keep separate for backward compatibility)
|
||||||
|
if (targetNode.data?.type === 'starter') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get parent information (handle container start node case)
|
// Get parent information (handle container start node case)
|
||||||
const sourceParentId =
|
const sourceParentId =
|
||||||
blocks[sourceNode.id]?.data?.parentId ||
|
blocks[sourceNode.id]?.data?.parentId ||
|
||||||
@@ -2755,6 +2787,7 @@ const WorkflowContent = React.memo(() => {
|
|||||||
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
|
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
|
||||||
|
|
||||||
const autoConnectEdge = tryCreateAutoConnectEdge(relativePositionBefore, node.id, {
|
const autoConnectEdge = tryCreateAutoConnectEdge(relativePositionBefore, node.id, {
|
||||||
|
blockType: node.data?.type || '',
|
||||||
targetParentId: potentialParentId,
|
targetParentId: potentialParentId,
|
||||||
existingChildBlocks,
|
existingChildBlocks,
|
||||||
containerId: potentialParentId,
|
containerId: potentialParentId,
|
||||||
|
|||||||
@@ -3,26 +3,11 @@
|
|||||||
import { memo, useMemo } from 'react'
|
import { memo, useMemo } from 'react'
|
||||||
import { Handle, type NodeProps, Position } from 'reactflow'
|
import { Handle, type NodeProps, Position } from 'reactflow'
|
||||||
import { HANDLE_POSITIONS } from '@/lib/workflows/blocks/block-dimensions'
|
import { HANDLE_POSITIONS } from '@/lib/workflows/blocks/block-dimensions'
|
||||||
import {
|
|
||||||
buildCanonicalIndex,
|
|
||||||
evaluateSubBlockCondition,
|
|
||||||
isSubBlockFeatureEnabled,
|
|
||||||
isSubBlockVisibleForMode,
|
|
||||||
} from '@/lib/workflows/subblocks/visibility'
|
|
||||||
import { getDisplayValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block'
|
|
||||||
import { getBlock } from '@/blocks'
|
import { getBlock } from '@/blocks'
|
||||||
import { SELECTOR_TYPES_HYDRATION_REQUIRED, type SubBlockConfig } from '@/blocks/types'
|
|
||||||
import { useVariablesStore } from '@/stores/panel/variables/store'
|
|
||||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
|
||||||
|
|
||||||
/** Execution status for blocks in preview mode */
|
/** Execution status for blocks in preview mode */
|
||||||
type ExecutionStatus = 'success' | 'error' | 'not-executed'
|
type ExecutionStatus = 'success' | 'error' | 'not-executed'
|
||||||
|
|
||||||
/** Subblock value structure matching workflow state */
|
|
||||||
interface SubBlockValueEntry {
|
|
||||||
value: unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WorkflowPreviewBlockData {
|
interface WorkflowPreviewBlockData {
|
||||||
type: string
|
type: string
|
||||||
name: string
|
name: string
|
||||||
@@ -33,220 +18,12 @@ interface WorkflowPreviewBlockData {
|
|||||||
isPreviewSelected?: boolean
|
isPreviewSelected?: boolean
|
||||||
/** Execution status for highlighting error/success states */
|
/** Execution status for highlighting error/success states */
|
||||||
executionStatus?: ExecutionStatus
|
executionStatus?: ExecutionStatus
|
||||||
/** Subblock values from the workflow state */
|
|
||||||
subBlockValues?: Record<string, SubBlockValueEntry | unknown>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the raw value from a subblock value entry.
|
|
||||||
* Handles both wrapped ({ value: ... }) and unwrapped formats.
|
|
||||||
*/
|
|
||||||
function extractValue(entry: SubBlockValueEntry | unknown): unknown {
|
|
||||||
if (entry && typeof entry === 'object' && 'value' in entry) {
|
|
||||||
return (entry as SubBlockValueEntry).value
|
|
||||||
}
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SubBlockRowProps {
|
|
||||||
title: string
|
|
||||||
value?: string
|
|
||||||
subBlock?: SubBlockConfig
|
|
||||||
rawValue?: unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves dropdown/combobox value to its display label.
|
|
||||||
* Returns null if not a dropdown/combobox or no matching option found.
|
|
||||||
*/
|
|
||||||
function resolveDropdownLabel(
|
|
||||||
subBlock: SubBlockConfig | undefined,
|
|
||||||
rawValue: unknown
|
|
||||||
): string | null {
|
|
||||||
if (!subBlock || (subBlock.type !== 'dropdown' && subBlock.type !== 'combobox')) return null
|
|
||||||
if (!rawValue || typeof rawValue !== 'string') return null
|
|
||||||
|
|
||||||
const options = typeof subBlock.options === 'function' ? subBlock.options() : subBlock.options
|
|
||||||
if (!options) return null
|
|
||||||
|
|
||||||
const option = options.find((opt) =>
|
|
||||||
typeof opt === 'string' ? opt === rawValue : opt.id === rawValue
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!option) return null
|
|
||||||
return typeof option === 'string' ? option : option.label
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves workflow ID to workflow name using the workflow registry.
|
|
||||||
* Uses synchronous store access to avoid hook dependencies.
|
|
||||||
*/
|
|
||||||
function resolveWorkflowName(
|
|
||||||
subBlock: SubBlockConfig | undefined,
|
|
||||||
rawValue: unknown
|
|
||||||
): string | null {
|
|
||||||
if (subBlock?.type !== 'workflow-selector') return null
|
|
||||||
if (!rawValue || typeof rawValue !== 'string') return null
|
|
||||||
|
|
||||||
const workflowMap = useWorkflowRegistry.getState().workflows
|
|
||||||
return workflowMap[rawValue]?.name ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type guard for variable assignments array
|
|
||||||
*/
|
|
||||||
function isVariableAssignmentsArray(
|
|
||||||
value: unknown
|
|
||||||
): value is Array<{ id?: string; variableId?: string; variableName?: string; value: unknown }> {
|
|
||||||
return (
|
|
||||||
Array.isArray(value) &&
|
|
||||||
value.length > 0 &&
|
|
||||||
value.every(
|
|
||||||
(item) =>
|
|
||||||
typeof item === 'object' &&
|
|
||||||
item !== null &&
|
|
||||||
('variableName' in item || 'variableId' in item)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves variables-input to display names.
|
|
||||||
* Uses synchronous store access to avoid hook dependencies.
|
|
||||||
*/
|
|
||||||
function resolveVariablesDisplay(
|
|
||||||
subBlock: SubBlockConfig | undefined,
|
|
||||||
rawValue: unknown
|
|
||||||
): string | null {
|
|
||||||
if (subBlock?.type !== 'variables-input') return null
|
|
||||||
if (!isVariableAssignmentsArray(rawValue)) return null
|
|
||||||
|
|
||||||
const variables = useVariablesStore.getState().variables
|
|
||||||
const variablesArray = Object.values(variables)
|
|
||||||
|
|
||||||
const names = rawValue
|
|
||||||
.map((a) => {
|
|
||||||
if (a.variableId) {
|
|
||||||
const variable = variablesArray.find((v) => v.id === a.variableId)
|
|
||||||
return variable?.name
|
|
||||||
}
|
|
||||||
if (a.variableName) return a.variableName
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
.filter((name): name is string => !!name)
|
|
||||||
|
|
||||||
if (names.length === 0) return null
|
|
||||||
if (names.length === 1) return names[0]
|
|
||||||
if (names.length === 2) return `${names[0]}, ${names[1]}`
|
|
||||||
return `${names[0]}, ${names[1]} +${names.length - 2}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves tool-input to display names.
|
|
||||||
* Resolves built-in tools from block registry (no API needed).
|
|
||||||
*/
|
|
||||||
function resolveToolsDisplay(
|
|
||||||
subBlock: SubBlockConfig | undefined,
|
|
||||||
rawValue: unknown
|
|
||||||
): string | null {
|
|
||||||
if (subBlock?.type !== 'tool-input') return null
|
|
||||||
if (!Array.isArray(rawValue) || rawValue.length === 0) return null
|
|
||||||
|
|
||||||
const toolNames = rawValue
|
|
||||||
.map((tool: unknown) => {
|
|
||||||
if (!tool || typeof tool !== 'object') return null
|
|
||||||
const t = tool as Record<string, unknown>
|
|
||||||
|
|
||||||
// Priority 1: Use tool.title if already populated
|
|
||||||
if (t.title && typeof t.title === 'string') return t.title
|
|
||||||
|
|
||||||
// Priority 2: Extract from inline schema (legacy format)
|
|
||||||
const schema = t.schema as Record<string, unknown> | undefined
|
|
||||||
if (schema?.function && typeof schema.function === 'object') {
|
|
||||||
const fn = schema.function as Record<string, unknown>
|
|
||||||
if (fn.name && typeof fn.name === 'string') return fn.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority 3: Extract from OpenAI function format
|
|
||||||
const fn = t.function as Record<string, unknown> | undefined
|
|
||||||
if (fn?.name && typeof fn.name === 'string') return fn.name
|
|
||||||
|
|
||||||
// Priority 4: Resolve built-in tool blocks from registry
|
|
||||||
if (
|
|
||||||
typeof t.type === 'string' &&
|
|
||||||
t.type !== 'custom-tool' &&
|
|
||||||
t.type !== 'mcp' &&
|
|
||||||
t.type !== 'workflow' &&
|
|
||||||
t.type !== 'workflow_input'
|
|
||||||
) {
|
|
||||||
const blockConfig = getBlock(t.type)
|
|
||||||
if (blockConfig?.name) return blockConfig.name
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
.filter((name): name is string => !!name)
|
|
||||||
|
|
||||||
if (toolNames.length === 0) return null
|
|
||||||
if (toolNames.length === 1) return toolNames[0]
|
|
||||||
if (toolNames.length === 2) return `${toolNames[0]}, ${toolNames[1]}`
|
|
||||||
return `${toolNames[0]}, ${toolNames[1]} +${toolNames.length - 2}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a single subblock row with title and optional value.
|
|
||||||
* Matches the SubBlockRow component in WorkflowBlock.
|
|
||||||
* - Masks password fields with bullets
|
|
||||||
* - Resolves dropdown/combobox labels
|
|
||||||
* - Resolves workflow names from registry
|
|
||||||
* - Resolves variable names from store
|
|
||||||
* - Resolves tool names from block registry
|
|
||||||
* - Shows '-' for other selector types that need hydration
|
|
||||||
*/
|
|
||||||
function SubBlockRow({ title, value, subBlock, rawValue }: SubBlockRowProps) {
|
|
||||||
// Mask password fields
|
|
||||||
const isPasswordField = subBlock?.password === true
|
|
||||||
const maskedValue = isPasswordField && value && value !== '-' ? '•••' : null
|
|
||||||
|
|
||||||
// Resolve various display names (synchronous access, matching WorkflowBlock priority)
|
|
||||||
const dropdownLabel = resolveDropdownLabel(subBlock, rawValue)
|
|
||||||
const variablesDisplay = resolveVariablesDisplay(subBlock, rawValue)
|
|
||||||
const toolsDisplay = resolveToolsDisplay(subBlock, rawValue)
|
|
||||||
const workflowName = resolveWorkflowName(subBlock, rawValue)
|
|
||||||
|
|
||||||
// Check if this is a selector type that needs hydration (show '-' for raw IDs)
|
|
||||||
const isSelectorType = subBlock?.type && SELECTOR_TYPES_HYDRATION_REQUIRED.includes(subBlock.type)
|
|
||||||
|
|
||||||
// Compute final display value matching WorkflowBlock logic
|
|
||||||
// Priority order matches WorkflowBlock: masked > hydrated names > selector fallback > raw value
|
|
||||||
const hydratedName = dropdownLabel || variablesDisplay || toolsDisplay || workflowName
|
|
||||||
const displayValue = maskedValue || hydratedName || (isSelectorType && value ? '-' : value)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex items-center gap-[8px]'>
|
|
||||||
<span
|
|
||||||
className='min-w-0 truncate text-[14px] text-[var(--text-tertiary)] capitalize'
|
|
||||||
title={title}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
{displayValue !== undefined && (
|
|
||||||
<span
|
|
||||||
className='flex-1 truncate text-right text-[14px] text-[var(--text-primary)]'
|
|
||||||
title={displayValue}
|
|
||||||
>
|
|
||||||
{displayValue}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preview block component for workflow visualization.
|
* Preview block component for workflow visualization.
|
||||||
* Renders block header, subblock values, and handles without
|
* Renders block header, subblocks skeleton, and handles without
|
||||||
* hooks, store subscriptions, or interactive features.
|
* hooks, store subscriptions, or interactive features.
|
||||||
* Matches the visual structure of WorkflowBlock exactly.
|
|
||||||
*/
|
*/
|
||||||
function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>) {
|
function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>) {
|
||||||
const {
|
const {
|
||||||
@@ -257,111 +34,21 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
|
|||||||
enabled = true,
|
enabled = true,
|
||||||
isPreviewSelected = false,
|
isPreviewSelected = false,
|
||||||
executionStatus,
|
executionStatus,
|
||||||
subBlockValues,
|
|
||||||
} = data
|
} = data
|
||||||
|
|
||||||
const blockConfig = getBlock(type)
|
const blockConfig = getBlock(type)
|
||||||
|
|
||||||
const canonicalIndex = useMemo(
|
|
||||||
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
|
|
||||||
[blockConfig?.subBlocks]
|
|
||||||
)
|
|
||||||
|
|
||||||
const rawValues = useMemo(() => {
|
|
||||||
if (!subBlockValues) return {}
|
|
||||||
return Object.entries(subBlockValues).reduce<Record<string, unknown>>((acc, [key, entry]) => {
|
|
||||||
acc[key] = extractValue(entry)
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}, [subBlockValues])
|
|
||||||
|
|
||||||
const visibleSubBlocks = useMemo(() => {
|
const visibleSubBlocks = useMemo(() => {
|
||||||
if (!blockConfig?.subBlocks) return []
|
if (!blockConfig?.subBlocks) return []
|
||||||
|
|
||||||
const isStarterOrTrigger =
|
|
||||||
blockConfig.category === 'triggers' || type === 'starter' || isTrigger
|
|
||||||
|
|
||||||
return blockConfig.subBlocks.filter((subBlock) => {
|
return blockConfig.subBlocks.filter((subBlock) => {
|
||||||
if (subBlock.hidden) return false
|
if (subBlock.hidden) return false
|
||||||
if (subBlock.hideFromPreview) return false
|
if (subBlock.hideFromPreview) return false
|
||||||
if (!isSubBlockFeatureEnabled(subBlock)) return false
|
if (subBlock.mode === 'trigger' && blockConfig.category !== 'triggers') return false
|
||||||
|
if (subBlock.mode === 'advanced') return false
|
||||||
// Handle trigger mode visibility
|
return true
|
||||||
if (subBlock.mode === 'trigger' && !isStarterOrTrigger) return false
|
|
||||||
|
|
||||||
// Check advanced mode visibility
|
|
||||||
if (!isSubBlockVisibleForMode(subBlock, false, canonicalIndex, rawValues, undefined)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check condition visibility
|
|
||||||
if (!subBlock.condition) return true
|
|
||||||
return evaluateSubBlockCondition(subBlock.condition, rawValues)
|
|
||||||
})
|
})
|
||||||
}, [blockConfig?.subBlocks, blockConfig?.category, type, isTrigger, canonicalIndex, rawValues])
|
}, [blockConfig?.subBlocks])
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute condition rows for condition blocks
|
|
||||||
*/
|
|
||||||
const conditionRows = useMemo(() => {
|
|
||||||
if (type !== 'condition') return []
|
|
||||||
|
|
||||||
const conditionsValue = rawValues.conditions
|
|
||||||
const raw = typeof conditionsValue === 'string' ? conditionsValue : undefined
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (raw) {
|
|
||||||
const parsed = JSON.parse(raw) as unknown
|
|
||||||
if (Array.isArray(parsed)) {
|
|
||||||
return parsed.map((item: unknown, index: number) => {
|
|
||||||
const conditionItem = item as { id?: string; value?: unknown }
|
|
||||||
const title = index === 0 ? 'if' : index === parsed.length - 1 ? 'else' : 'else if'
|
|
||||||
return {
|
|
||||||
id: conditionItem?.id ?? `cond-${index}`,
|
|
||||||
title,
|
|
||||||
value: typeof conditionItem?.value === 'string' ? conditionItem.value : '',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Failed to parse, use fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{ id: 'if', title: 'if', value: '' },
|
|
||||||
{ id: 'else', title: 'else', value: '' },
|
|
||||||
]
|
|
||||||
}, [type, rawValues])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute router rows for router_v2 blocks
|
|
||||||
*/
|
|
||||||
const routerRows = useMemo(() => {
|
|
||||||
if (type !== 'router_v2') return []
|
|
||||||
|
|
||||||
const routesValue = rawValues.routes
|
|
||||||
const raw = typeof routesValue === 'string' ? routesValue : undefined
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (raw) {
|
|
||||||
const parsed = JSON.parse(raw) as unknown
|
|
||||||
if (Array.isArray(parsed)) {
|
|
||||||
return parsed.map((item: unknown, index: number) => {
|
|
||||||
const routeItem = item as { id?: string; value?: string }
|
|
||||||
return {
|
|
||||||
id: routeItem?.id ?? `route${index + 1}`,
|
|
||||||
value: routeItem?.value ?? '',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Failed to parse, use fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
return [{ id: 'route1', value: '' }]
|
|
||||||
}, [type, rawValues])
|
|
||||||
|
|
||||||
if (!blockConfig) {
|
if (!blockConfig) {
|
||||||
return null
|
return null
|
||||||
@@ -370,14 +57,8 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
|
|||||||
const IconComponent = blockConfig.icon
|
const IconComponent = blockConfig.icon
|
||||||
const isStarterOrTrigger = blockConfig.category === 'triggers' || type === 'starter' || isTrigger
|
const isStarterOrTrigger = blockConfig.category === 'triggers' || type === 'starter' || isTrigger
|
||||||
|
|
||||||
const shouldShowDefaultHandles = !isStarterOrTrigger
|
|
||||||
const hasSubBlocks = visibleSubBlocks.length > 0
|
const hasSubBlocks = visibleSubBlocks.length > 0
|
||||||
const hasContentBelowHeader =
|
const showErrorRow = !isStarterOrTrigger
|
||||||
type === 'condition'
|
|
||||||
? conditionRows.length > 0 || shouldShowDefaultHandles
|
|
||||||
: type === 'router_v2'
|
|
||||||
? routerRows.length > 0 || shouldShowDefaultHandles
|
|
||||||
: hasSubBlocks || shouldShowDefaultHandles
|
|
||||||
|
|
||||||
const horizontalHandleClass = '!border-none !bg-[var(--surface-7)] !h-5 !w-[7px] !rounded-[2px]'
|
const horizontalHandleClass = '!border-none !bg-[var(--surface-7)] !h-5 !w-[7px] !rounded-[2px]'
|
||||||
const verticalHandleClass = '!border-none !bg-[var(--surface-7)] !h-[7px] !w-5 !rounded-[2px]'
|
const verticalHandleClass = '!border-none !bg-[var(--surface-7)] !h-[7px] !w-5 !rounded-[2px]'
|
||||||
@@ -386,7 +67,7 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
|
|||||||
const hasSuccess = executionStatus === 'success'
|
const hasSuccess = executionStatus === 'success'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative w-[250px] select-none rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-2)]'>
|
<div className='relative w-[250px] select-none rounded-[8px] border border-[var(--border)] bg-[var(--surface-2)]'>
|
||||||
{/* Selection ring overlay (takes priority over execution rings) */}
|
{/* Selection ring overlay (takes priority over execution rings) */}
|
||||||
{isPreviewSelected && (
|
{isPreviewSelected && (
|
||||||
<div className='pointer-events-none absolute inset-0 z-40 rounded-[8px] ring-[1.75px] ring-[var(--brand-secondary)]' />
|
<div className='pointer-events-none absolute inset-0 z-40 rounded-[8px] ring-[1.75px] ring-[var(--brand-secondary)]' />
|
||||||
@@ -401,7 +82,7 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Target handle - not shown for triggers/starters */}
|
{/* Target handle - not shown for triggers/starters */}
|
||||||
{shouldShowDefaultHandles && (
|
{!isStarterOrTrigger && (
|
||||||
<Handle
|
<Handle
|
||||||
type='target'
|
type='target'
|
||||||
position={horizontalHandles ? Position.Left : Position.Top}
|
position={horizontalHandles ? Position.Left : Position.Top}
|
||||||
@@ -415,67 +96,49 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Header - matches WorkflowBlock structure */}
|
{/* Header */}
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-between p-[8px] ${hasContentBelowHeader ? 'border-[var(--border-1)] border-b' : ''}`}
|
className={`flex items-center gap-[10px] p-[8px] ${hasSubBlocks || showErrorRow ? 'border-[var(--divider)] border-b' : ''}`}
|
||||||
>
|
>
|
||||||
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-[10px]'>
|
<div
|
||||||
<div
|
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
|
||||||
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
|
style={{ background: enabled ? blockConfig.bgColor : 'gray' }}
|
||||||
style={{ background: enabled ? blockConfig.bgColor : 'gray' }}
|
>
|
||||||
>
|
<IconComponent className='h-[16px] w-[16px] text-white' />
|
||||||
<IconComponent className='h-[16px] w-[16px] text-white' />
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
className={`truncate font-medium text-[16px] ${!enabled ? 'text-[var(--text-muted)]' : ''}`}
|
|
||||||
title={name}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
className={`truncate font-medium text-[16px] ${!enabled ? 'text-[#808080]' : ''}`}
|
||||||
|
title={name}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content area with subblocks */}
|
{/* Subblocks skeleton */}
|
||||||
{hasContentBelowHeader && (
|
{(hasSubBlocks || showErrorRow) && (
|
||||||
<div className='flex flex-col gap-[8px] p-[8px]'>
|
<div className='flex flex-col gap-[8px] p-[8px]'>
|
||||||
{type === 'condition' ? (
|
{visibleSubBlocks.slice(0, 4).map((subBlock) => (
|
||||||
// Condition block: render condition rows
|
<div key={subBlock.id} className='flex items-center gap-[8px]'>
|
||||||
conditionRows.map((cond) => (
|
<span className='min-w-0 truncate text-[14px] text-[var(--text-tertiary)] capitalize'>
|
||||||
<SubBlockRow key={cond.id} title={cond.title} value={getDisplayValue(cond.value)} />
|
{subBlock.title ?? subBlock.id}
|
||||||
))
|
</span>
|
||||||
) : type === 'router_v2' ? (
|
<span className='flex-1 truncate text-right text-[14px] text-[var(--white)]'>-</span>
|
||||||
// Router block: render context + route rows
|
</div>
|
||||||
<>
|
))}
|
||||||
<SubBlockRow
|
{visibleSubBlocks.length > 4 && (
|
||||||
key='context'
|
<div className='flex items-center gap-[8px]'>
|
||||||
title='Context'
|
<span className='text-[14px] text-[var(--text-tertiary)]'>
|
||||||
value={getDisplayValue(rawValues.context)}
|
+{visibleSubBlocks.length - 4} more
|
||||||
/>
|
</span>
|
||||||
{routerRows.map((route, index) => (
|
</div>
|
||||||
<SubBlockRow
|
)}
|
||||||
key={route.id}
|
{showErrorRow && (
|
||||||
title={`Route ${index + 1}`}
|
<div className='flex items-center gap-[8px]'>
|
||||||
value={getDisplayValue(route.value)}
|
<span className='min-w-0 truncate text-[14px] text-[var(--text-tertiary)] capitalize'>
|
||||||
/>
|
error
|
||||||
))}
|
</span>
|
||||||
</>
|
</div>
|
||||||
) : (
|
|
||||||
// Standard blocks: render visible subblocks
|
|
||||||
visibleSubBlocks.map((subBlock) => {
|
|
||||||
const rawValue = rawValues[subBlock.id]
|
|
||||||
return (
|
|
||||||
<SubBlockRow
|
|
||||||
key={subBlock.id}
|
|
||||||
title={subBlock.title ?? subBlock.id}
|
|
||||||
value={getDisplayValue(rawValue)}
|
|
||||||
subBlock={subBlock}
|
|
||||||
rawValue={rawValue}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
{/* Error row for non-trigger blocks */}
|
|
||||||
{shouldShowDefaultHandles && <SubBlockRow title='error' />}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -499,47 +162,16 @@ function shouldSkipPreviewBlockRender(
|
|||||||
prevProps: NodeProps<WorkflowPreviewBlockData>,
|
prevProps: NodeProps<WorkflowPreviewBlockData>,
|
||||||
nextProps: NodeProps<WorkflowPreviewBlockData>
|
nextProps: NodeProps<WorkflowPreviewBlockData>
|
||||||
): boolean {
|
): boolean {
|
||||||
// Check primitive props first (fast path)
|
return (
|
||||||
if (
|
prevProps.id === nextProps.id &&
|
||||||
prevProps.id !== nextProps.id ||
|
prevProps.data.type === nextProps.data.type &&
|
||||||
prevProps.data.type !== nextProps.data.type ||
|
prevProps.data.name === nextProps.data.name &&
|
||||||
prevProps.data.name !== nextProps.data.name ||
|
prevProps.data.isTrigger === nextProps.data.isTrigger &&
|
||||||
prevProps.data.isTrigger !== nextProps.data.isTrigger ||
|
prevProps.data.horizontalHandles === nextProps.data.horizontalHandles &&
|
||||||
prevProps.data.horizontalHandles !== nextProps.data.horizontalHandles ||
|
prevProps.data.enabled === nextProps.data.enabled &&
|
||||||
prevProps.data.enabled !== nextProps.data.enabled ||
|
prevProps.data.isPreviewSelected === nextProps.data.isPreviewSelected &&
|
||||||
prevProps.data.isPreviewSelected !== nextProps.data.isPreviewSelected ||
|
prevProps.data.executionStatus === nextProps.data.executionStatus
|
||||||
prevProps.data.executionStatus !== nextProps.data.executionStatus
|
)
|
||||||
) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare subBlockValues by reference first
|
|
||||||
const prevValues = prevProps.data.subBlockValues
|
|
||||||
const nextValues = nextProps.data.subBlockValues
|
|
||||||
|
|
||||||
if (prevValues === nextValues) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevValues || !nextValues) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shallow compare keys and values
|
|
||||||
const prevKeys = Object.keys(prevValues)
|
|
||||||
const nextKeys = Object.keys(nextValues)
|
|
||||||
|
|
||||||
if (prevKeys.length !== nextKeys.length) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of prevKeys) {
|
|
||||||
if (prevValues[key] !== nextValues[key]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkflowPreviewBlock = memo(WorkflowPreviewBlockInner, shouldSkipPreviewBlockRender)
|
export const WorkflowPreviewBlock = memo(WorkflowPreviewBlockInner, shouldSkipPreviewBlockRender)
|
||||||
|
|||||||
@@ -347,7 +347,6 @@ export function WorkflowPreview({
|
|||||||
enabled: block.enabled ?? true,
|
enabled: block.enabled ?? true,
|
||||||
isPreviewSelected: isSelected,
|
isPreviewSelected: isSelected,
|
||||||
executionStatus,
|
executionStatus,
|
||||||
subBlockValues: block.subBlocks,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
useWorkflowStore.getState().replaceWorkflowState({
|
useWorkflowStore.setState({
|
||||||
blocks: workflowState.blocks || {},
|
blocks: workflowState.blocks || {},
|
||||||
edges: workflowState.edges || [],
|
edges: workflowState.edges || [],
|
||||||
loops: workflowState.loops || {},
|
loops: workflowState.loops || {},
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Copilot supports slash commands that trigger specialized capabilities:
|
|||||||
|
|
||||||
- `/fast` — uses a faster model for quick responses when you need speed over depth
|
- `/fast` — uses a faster model for quick responses when you need speed over depth
|
||||||
- `/research` — performs multi-step web research on a topic, synthesizing results from multiple sources
|
- `/research` — performs multi-step web research on a topic, synthesizing results from multiple sources
|
||||||
- `/actions` — lets Copilot directly use your connected integrations as tools, like reading your Gmail, sending Slack messages, or querying your database—all outside the context of a workflow
|
- `/actions` — enables agentic mode where Copilot can take actions on your behalf, like modifying blocks or creating workflows
|
||||||
- `/search` — searches the web for relevant information
|
- `/search` — searches the web for relevant information
|
||||||
- `/read` — reads and extracts content from a URL
|
- `/read` — reads and extracts content from a URL
|
||||||
- `/scrape` — scrapes structured data from web pages
|
- `/scrape` — scrapes structured data from web pages
|
||||||
|
|||||||
@@ -144,22 +144,25 @@ describe('AgentBlockHandler', () => {
|
|||||||
}
|
}
|
||||||
mockGetProviderFromModel.mockReturnValue('mock-provider')
|
mockGetProviderFromModel.mockReturnValue('mock-provider')
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValue({
|
mockFetch.mockImplementation(() => {
|
||||||
content: 'Mocked response content',
|
|
||||||
model: 'mock-model',
|
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
|
||||||
toolCalls: [],
|
|
||||||
cost: 0.001,
|
|
||||||
timing: { total: 100 },
|
|
||||||
})
|
|
||||||
|
|
||||||
mockFetch.mockImplementation((url: string) => {
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
ok: true,
|
ok: true,
|
||||||
headers: {
|
headers: {
|
||||||
get: () => null,
|
get: (name: string) => {
|
||||||
|
if (name === 'Content-Type') return 'application/json'
|
||||||
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
json: () => Promise.resolve({}),
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Mocked response content',
|
||||||
|
model: 'mock-model',
|
||||||
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
|
toolCalls: [],
|
||||||
|
cost: 0.001,
|
||||||
|
timing: { total: 100 },
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -241,7 +244,7 @@ describe('AgentBlockHandler', () => {
|
|||||||
const result = await handler.execute(mockContext, mockBlock, inputs)
|
const result = await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockGetProviderFromModel).toHaveBeenCalledWith('gpt-4o')
|
expect(mockGetProviderFromModel).toHaveBeenCalledWith('gpt-4o')
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
expect(result).toEqual(expectedOutput)
|
expect(result).toEqual(expectedOutput)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -260,21 +263,34 @@ describe('AgentBlockHandler', () => {
|
|||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Using tools to respond',
|
return Promise.resolve({
|
||||||
model: 'mock-model',
|
ok: true,
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
headers: {
|
||||||
toolCalls: [
|
get: (name: string) => {
|
||||||
{
|
if (name === 'Content-Type') return 'application/json'
|
||||||
name: 'auto_tool',
|
if (name === 'X-Execution-Data') return null
|
||||||
arguments: { input: 'test input for auto tool' },
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
json: () =>
|
||||||
name: 'force_tool',
|
Promise.resolve({
|
||||||
arguments: { input: 'test input for force tool' },
|
content: 'Using tools to respond',
|
||||||
},
|
model: 'mock-model',
|
||||||
],
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
timing: { total: 100 },
|
toolCalls: [
|
||||||
|
{
|
||||||
|
name: 'auto_tool',
|
||||||
|
arguments: { input: 'test input for auto tool' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'force_tool',
|
||||||
|
arguments: { input: 'test input for force tool' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timing: { total: 100 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -387,8 +403,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
expect.any(Object) // execution context
|
expect.any(Object) // execution context
|
||||||
)
|
)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
expect(requestBody.tools.length).toBe(2)
|
expect(requestBody.tools.length).toBe(2)
|
||||||
})
|
})
|
||||||
@@ -427,8 +443,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
expect(requestBody.tools.length).toBe(2)
|
expect(requestBody.tools.length).toBe(2)
|
||||||
|
|
||||||
@@ -472,8 +488,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
expect(requestBody.tools[0].usageControl).toBe('auto')
|
expect(requestBody.tools[0].usageControl).toBe('auto')
|
||||||
expect(requestBody.tools[1].usageControl).toBe('force')
|
expect(requestBody.tools[1].usageControl).toBe('force')
|
||||||
@@ -537,8 +553,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
expect(requestBody.tools.length).toBe(2)
|
expect(requestBody.tools.length).toBe(2)
|
||||||
|
|
||||||
@@ -567,7 +583,7 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute with standard block tools', async () => {
|
it('should execute with standard block tools', async () => {
|
||||||
@@ -609,7 +625,7 @@ describe('AgentBlockHandler', () => {
|
|||||||
inputs.tools[0],
|
inputs.tools[0],
|
||||||
expect.objectContaining({ selectedOperation: 'analyze' })
|
expect.objectContaining({ selectedOperation: 'analyze' })
|
||||||
)
|
)
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
expect(result).toEqual(expectedOutput)
|
expect(result).toEqual(expectedOutput)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -660,17 +676,30 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle responseFormat with valid JSON', async () => {
|
it('should handle responseFormat with valid JSON', async () => {
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: '{"result": "Success", "score": 0.95}',
|
return Promise.resolve({
|
||||||
model: 'mock-model',
|
ok: true,
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
headers: {
|
||||||
timing: { total: 100 },
|
get: (name: string) => {
|
||||||
toolCalls: [],
|
if (name === 'Content-Type') return 'application/json'
|
||||||
cost: undefined,
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: '{"result": "Success", "score": 0.95}',
|
||||||
|
model: 'mock-model',
|
||||||
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
|
timing: { total: 100 },
|
||||||
|
toolCalls: [],
|
||||||
|
cost: undefined,
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -694,11 +723,24 @@ describe('AgentBlockHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle responseFormat when it is an empty string', async () => {
|
it('should handle responseFormat when it is an empty string', async () => {
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Regular text response',
|
return Promise.resolve({
|
||||||
model: 'mock-model',
|
ok: true,
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
headers: {
|
||||||
timing: { total: 100 },
|
get: (name: string) => {
|
||||||
|
if (name === 'Content-Type') return 'application/json'
|
||||||
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Regular text response',
|
||||||
|
model: 'mock-model',
|
||||||
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
|
timing: { total: 100 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -721,13 +763,26 @@ describe('AgentBlockHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle invalid JSON in responseFormat gracefully', async () => {
|
it('should handle invalid JSON in responseFormat gracefully', async () => {
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Regular text response',
|
return Promise.resolve({
|
||||||
model: 'mock-model',
|
ok: true,
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
headers: {
|
||||||
timing: { total: 100 },
|
get: (name: string) => {
|
||||||
toolCalls: [],
|
if (name === 'Content-Type') return 'application/json'
|
||||||
cost: undefined,
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Regular text response',
|
||||||
|
model: 'mock-model',
|
||||||
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
|
timing: { total: 100 },
|
||||||
|
toolCalls: [],
|
||||||
|
cost: undefined,
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -751,13 +806,26 @@ describe('AgentBlockHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle variable references in responseFormat gracefully', async () => {
|
it('should handle variable references in responseFormat gracefully', async () => {
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Regular text response',
|
return Promise.resolve({
|
||||||
model: 'mock-model',
|
ok: true,
|
||||||
tokens: { input: 10, output: 20, total: 30 },
|
headers: {
|
||||||
timing: { total: 100 },
|
get: (name: string) => {
|
||||||
toolCalls: [],
|
if (name === 'Content-Type') return 'application/json'
|
||||||
cost: undefined,
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Regular text response',
|
||||||
|
model: 'mock-model',
|
||||||
|
tokens: { input: 10, output: 20, total: 30 },
|
||||||
|
timing: { total: 100 },
|
||||||
|
toolCalls: [],
|
||||||
|
cost: undefined,
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -788,7 +856,7 @@ describe('AgentBlockHandler', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockGetProviderFromModel.mockReturnValue('openai')
|
mockGetProviderFromModel.mockReturnValue('openai')
|
||||||
mockExecuteProviderRequest.mockRejectedValueOnce(new Error('Provider API Error'))
|
mockFetch.mockRejectedValue(new Error('Provider API Error'))
|
||||||
|
|
||||||
await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow(
|
await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow(
|
||||||
'Provider API Error'
|
'Provider API Error'
|
||||||
@@ -802,17 +870,30 @@ describe('AgentBlockHandler', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
stream: mockStreamBody,
|
return Promise.resolve({
|
||||||
execution: {
|
ok: true,
|
||||||
success: true,
|
headers: {
|
||||||
output: {},
|
get: (name: string) => {
|
||||||
logs: [],
|
if (name === 'Content-Type') return 'application/json'
|
||||||
metadata: {
|
if (name === 'X-Execution-Data') return null
|
||||||
duration: 0,
|
return null
|
||||||
startTime: new Date().toISOString(),
|
},
|
||||||
},
|
},
|
||||||
},
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
stream: mockStreamBody,
|
||||||
|
execution: {
|
||||||
|
success: true,
|
||||||
|
output: {},
|
||||||
|
logs: [],
|
||||||
|
metadata: {
|
||||||
|
duration: 0,
|
||||||
|
startTime: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -866,9 +947,22 @@ describe('AgentBlockHandler', () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
stream: mockStreamBody,
|
return Promise.resolve({
|
||||||
execution: mockExecutionData,
|
ok: true,
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => {
|
||||||
|
if (name === 'Content-Type') return 'application/json'
|
||||||
|
if (name === 'X-Execution-Data') return JSON.stringify(mockExecutionData)
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
stream: mockStreamBody,
|
||||||
|
execution: mockExecutionData,
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -902,21 +996,30 @@ describe('AgentBlockHandler', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
stream: {}, // Serialized stream placeholder
|
return Promise.resolve({
|
||||||
execution: {
|
ok: true,
|
||||||
success: true,
|
headers: {
|
||||||
output: {
|
get: (name: string) => (name === 'Content-Type' ? 'application/json' : null),
|
||||||
content: 'Test streaming content',
|
|
||||||
model: 'gpt-4o',
|
|
||||||
tokens: { input: 10, output: 5, total: 15 },
|
|
||||||
},
|
},
|
||||||
logs: [],
|
json: () =>
|
||||||
metadata: {
|
Promise.resolve({
|
||||||
startTime: new Date().toISOString(),
|
stream: {}, // Serialized stream placeholder
|
||||||
duration: 150,
|
execution: {
|
||||||
},
|
success: true,
|
||||||
},
|
output: {
|
||||||
|
content: 'Test streaming content',
|
||||||
|
model: 'gpt-4o',
|
||||||
|
tokens: { input: 10, output: 5, total: 15 },
|
||||||
|
},
|
||||||
|
logs: [],
|
||||||
|
metadata: {
|
||||||
|
startTime: new Date().toISOString(),
|
||||||
|
duration: 150,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -957,8 +1060,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1007,8 +1110,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1046,8 +1149,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1078,8 +1181,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
// Agent system (1) + legacy memories (3) + user from messages (1) = 5
|
// Agent system (1) + legacy memories (3) + user from messages (1) = 5
|
||||||
@@ -1122,8 +1225,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1165,8 +1268,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify messages were built correctly
|
// Verify messages were built correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1207,8 +1310,8 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
// Verify user prompt content was extracted correctly
|
// Verify user prompt content was extracted correctly
|
||||||
expect(requestBody.messages).toBeDefined()
|
expect(requestBody.messages).toBeDefined()
|
||||||
@@ -1234,14 +1337,15 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
|
// Check that Azure parameters are included in the request
|
||||||
expect(requestBody.azureEndpoint).toBe('https://my-azure-resource.openai.azure.com')
|
expect(requestBody.azureEndpoint).toBe('https://my-azure-resource.openai.azure.com')
|
||||||
expect(requestBody.azureApiVersion).toBe('2024-07-01-preview')
|
expect(requestBody.azureApiVersion).toBe('2024-07-01-preview')
|
||||||
expect(providerCall[0]).toBe('azure-openai')
|
expect(requestBody.provider).toBe('azure-openai')
|
||||||
expect(requestBody.model).toBe('azure/gpt-4o')
|
expect(requestBody.model).toBe('azure/gpt-4o')
|
||||||
expect(requestBody.apiKey).toBe('test-azure-api-key')
|
expect(requestBody.apiKey).toBe('test-azure-api-key')
|
||||||
})
|
})
|
||||||
@@ -1261,14 +1365,15 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
|
// Check that GPT-5 parameters are included in the request
|
||||||
expect(requestBody.reasoningEffort).toBe('minimal')
|
expect(requestBody.reasoningEffort).toBe('minimal')
|
||||||
expect(requestBody.verbosity).toBe('high')
|
expect(requestBody.verbosity).toBe('high')
|
||||||
expect(providerCall[0]).toBe('openai')
|
expect(requestBody.provider).toBe('openai')
|
||||||
expect(requestBody.model).toBe('gpt-5')
|
expect(requestBody.model).toBe('gpt-5')
|
||||||
expect(requestBody.apiKey).toBe('test-api-key')
|
expect(requestBody.apiKey).toBe('test-api-key')
|
||||||
})
|
})
|
||||||
@@ -1280,20 +1385,22 @@ describe('AgentBlockHandler', () => {
|
|||||||
userPrompt: 'Hello!',
|
userPrompt: 'Hello!',
|
||||||
apiKey: 'test-api-key',
|
apiKey: 'test-api-key',
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
|
// No reasoningEffort or verbosity provided
|
||||||
}
|
}
|
||||||
|
|
||||||
mockGetProviderFromModel.mockReturnValue('openai')
|
mockGetProviderFromModel.mockReturnValue('openai')
|
||||||
|
|
||||||
await handler.execute(mockContext, mockBlock, inputs)
|
await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
|
||||||
|
|
||||||
const providerCall = mockExecuteProviderRequest.mock.calls[0]
|
const fetchCall = mockFetch.mock.calls[0]
|
||||||
const requestBody = providerCall[1]
|
const requestBody = JSON.parse(fetchCall[1].body)
|
||||||
|
|
||||||
|
// Check that GPT-5 parameters are undefined when not provided
|
||||||
expect(requestBody.reasoningEffort).toBeUndefined()
|
expect(requestBody.reasoningEffort).toBeUndefined()
|
||||||
expect(requestBody.verbosity).toBeUndefined()
|
expect(requestBody.verbosity).toBeUndefined()
|
||||||
expect(providerCall[0]).toBe('openai')
|
expect(requestBody.provider).toBe('openai')
|
||||||
expect(requestBody.model).toBe('gpt-5')
|
expect(requestBody.model).toBe('gpt-5')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1315,29 +1422,42 @@ describe('AgentBlockHandler', () => {
|
|||||||
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'I will use MCP tools to help you.',
|
return Promise.resolve({
|
||||||
model: 'gpt-4o',
|
ok: true,
|
||||||
tokens: { input: 15, output: 25, total: 40 },
|
headers: {
|
||||||
toolCalls: [
|
get: (name: string) => {
|
||||||
{
|
if (name === 'Content-Type') return 'application/json'
|
||||||
name: 'mcp-server1-list_files',
|
if (name === 'X-Execution-Data') return null
|
||||||
arguments: { path: '/tmp' },
|
return null
|
||||||
result: {
|
|
||||||
success: true,
|
|
||||||
output: { content: [{ type: 'text', text: 'Files listed' }] },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
json: () =>
|
||||||
name: 'mcp-server2-search',
|
Promise.resolve({
|
||||||
arguments: { query: 'test', limit: 5 },
|
content: 'I will use MCP tools to help you.',
|
||||||
result: {
|
model: 'gpt-4o',
|
||||||
success: true,
|
tokens: { input: 15, output: 25, total: 40 },
|
||||||
output: { content: [{ type: 'text', text: 'Search results' }] },
|
toolCalls: [
|
||||||
},
|
{
|
||||||
},
|
name: 'mcp-server1-list_files',
|
||||||
],
|
arguments: { path: '/tmp' },
|
||||||
timing: { total: 150 },
|
result: {
|
||||||
|
success: true,
|
||||||
|
output: { content: [{ type: 'text', text: 'Files listed' }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mcp-server2-search',
|
||||||
|
arguments: { query: 'test', limit: 5 },
|
||||||
|
result: {
|
||||||
|
success: true,
|
||||||
|
output: { content: [{ type: 'text', text: 'Search results' }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timing: { total: 150 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -1413,21 +1533,34 @@ describe('AgentBlockHandler', () => {
|
|||||||
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Let me try to use this tool.',
|
return Promise.resolve({
|
||||||
model: 'gpt-4o',
|
ok: true,
|
||||||
tokens: { input: 10, output: 15, total: 25 },
|
headers: {
|
||||||
toolCalls: [
|
get: (name: string) => {
|
||||||
{
|
if (name === 'Content-Type') return 'application/json'
|
||||||
name: 'mcp-server1-failing_tool',
|
if (name === 'X-Execution-Data') return null
|
||||||
arguments: { param: 'value' },
|
return null
|
||||||
result: {
|
|
||||||
success: false,
|
|
||||||
error: 'MCP server connection failed',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
json: () =>
|
||||||
timing: { total: 100 },
|
Promise.resolve({
|
||||||
|
content: 'Let me try to use this tool.',
|
||||||
|
model: 'gpt-4o',
|
||||||
|
tokens: { input: 10, output: 15, total: 25 },
|
||||||
|
toolCalls: [
|
||||||
|
{
|
||||||
|
name: 'mcp-server1-failing_tool',
|
||||||
|
arguments: { param: 'value' },
|
||||||
|
result: {
|
||||||
|
success: false,
|
||||||
|
error: 'MCP server connection failed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timing: { total: 100 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -1505,12 +1638,25 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
mockGetProviderFromModel.mockReturnValue('openai')
|
mockGetProviderFromModel.mockReturnValue('openai')
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Used MCP tools successfully',
|
return Promise.resolve({
|
||||||
model: 'gpt-4o',
|
ok: true,
|
||||||
tokens: { input: 20, output: 30, total: 50 },
|
headers: {
|
||||||
toolCalls: [],
|
get: (name: string) => {
|
||||||
timing: { total: 200 },
|
if (name === 'Content-Type') return 'application/json'
|
||||||
|
if (name === 'X-Execution-Data') return null
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Used MCP tools successfully',
|
||||||
|
model: 'gpt-4o',
|
||||||
|
tokens: { input: 20, output: 30, total: 50 },
|
||||||
|
toolCalls: [],
|
||||||
|
timing: { total: 200 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
mockTransformBlockTool.mockImplementation((tool: any) => ({
|
mockTransformBlockTool.mockImplementation((tool: any) => ({
|
||||||
@@ -1523,9 +1669,11 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
const result = await handler.execute(mockContext, mockBlock, inputs)
|
const result = await handler.execute(mockContext, mockBlock, inputs)
|
||||||
|
|
||||||
|
// Verify that the agent executed successfully with MCP tools
|
||||||
expect(result).toBeDefined()
|
expect(result).toBeDefined()
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
expect(mockFetch).toHaveBeenCalled()
|
||||||
|
|
||||||
|
// Verify the agent returns the expected response format
|
||||||
expect((result as any).content).toBe('Used MCP tools successfully')
|
expect((result as any).content).toBe('Used MCP tools successfully')
|
||||||
expect((result as any).model).toBe('gpt-4o')
|
expect((result as any).model).toBe('gpt-4o')
|
||||||
})
|
})
|
||||||
@@ -1543,12 +1691,21 @@ describe('AgentBlockHandler', () => {
|
|||||||
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
return Promise.resolve({ success: false, error: 'Unknown tool' })
|
||||||
})
|
})
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
mockFetch.mockImplementationOnce(() => {
|
||||||
content: 'Using MCP tool',
|
return Promise.resolve({
|
||||||
model: 'gpt-4o',
|
ok: true,
|
||||||
tokens: { input: 10, output: 10, total: 20 },
|
headers: {
|
||||||
toolCalls: [{ name: 'mcp-test-tool', arguments: {} }],
|
get: (name: string) => (name === 'Content-Type' ? 'application/json' : null),
|
||||||
timing: { total: 50 },
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Using MCP tool',
|
||||||
|
model: 'gpt-4o',
|
||||||
|
tokens: { input: 10, output: 10, total: 20 },
|
||||||
|
toolCalls: [{ name: 'mcp-test-tool', arguments: {} }],
|
||||||
|
timing: { total: 50 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
@@ -1658,26 +1815,37 @@ describe('AgentBlockHandler', () => {
|
|||||||
const discoveryCalls = fetchCalls.filter((c) => c.url.includes('/api/mcp/tools/discover'))
|
const discoveryCalls = fetchCalls.filter((c) => c.url.includes('/api/mcp/tools/discover'))
|
||||||
expect(discoveryCalls.length).toBe(0)
|
expect(discoveryCalls.length).toBe(0)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
const providerCalls = fetchCalls.filter((c) => c.url.includes('/api/providers'))
|
||||||
|
expect(providerCalls.length).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should pass toolSchema to execution endpoint when using cached schema', async () => {
|
it('should pass toolSchema to execution endpoint when using cached schema', async () => {
|
||||||
let executionCall: any = null
|
let executionCall: any = null
|
||||||
|
|
||||||
mockExecuteProviderRequest.mockResolvedValueOnce({
|
|
||||||
content: 'Tool executed',
|
|
||||||
model: 'gpt-4o',
|
|
||||||
tokens: { input: 10, output: 10, total: 20 },
|
|
||||||
toolCalls: [
|
|
||||||
{
|
|
||||||
name: 'search_files',
|
|
||||||
arguments: JSON.stringify({ query: 'test' }),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
timing: { total: 50 },
|
|
||||||
})
|
|
||||||
|
|
||||||
mockFetch.mockImplementation((url: string, options: any) => {
|
mockFetch.mockImplementation((url: string, options: any) => {
|
||||||
|
if (url.includes('/api/providers')) {
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => (name === 'Content-Type' ? 'application/json' : null),
|
||||||
|
},
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
content: 'Tool executed',
|
||||||
|
model: 'gpt-4o',
|
||||||
|
tokens: { input: 10, output: 10, total: 20 },
|
||||||
|
toolCalls: [
|
||||||
|
{
|
||||||
|
name: 'search_files',
|
||||||
|
arguments: { query: 'test' },
|
||||||
|
result: { success: true, output: {} },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timing: { total: 50 },
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (url.includes('/api/mcp/tools/execute')) {
|
if (url.includes('/api/mcp/tools/execute')) {
|
||||||
executionCall = { url, body: JSON.parse(options.body) }
|
executionCall = { url, body: JSON.parse(options.body) }
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -1730,11 +1898,13 @@ describe('AgentBlockHandler', () => {
|
|||||||
|
|
||||||
await handler.execute(contextWithWorkspace, mockBlock, inputs)
|
await handler.execute(contextWithWorkspace, mockBlock, inputs)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
const providerCalls = mockFetch.mock.calls.filter((c: any) => c[0].includes('/api/providers'))
|
||||||
const providerCallArgs = mockExecuteProviderRequest.mock.calls[0]
|
expect(providerCalls.length).toBe(1)
|
||||||
expect(providerCallArgs[1].tools).toBeDefined()
|
|
||||||
expect(providerCallArgs[1].tools.length).toBe(1)
|
const providerRequestBody = JSON.parse(providerCalls[0][1].body)
|
||||||
expect(providerCallArgs[1].tools[0].name).toBe('search_files')
|
expect(providerRequestBody.tools).toBeDefined()
|
||||||
|
expect(providerRequestBody.tools.length).toBe(1)
|
||||||
|
expect(providerRequestBody.tools[0].name).toBe('search_files')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle multiple MCP tools from the same server efficiently', async () => {
|
it('should handle multiple MCP tools from the same server efficiently', async () => {
|
||||||
@@ -1817,12 +1987,14 @@ describe('AgentBlockHandler', () => {
|
|||||||
const discoveryCalls = fetchCalls.filter((c) => c.url.includes('/api/mcp/tools/discover'))
|
const discoveryCalls = fetchCalls.filter((c) => c.url.includes('/api/mcp/tools/discover'))
|
||||||
expect(discoveryCalls.length).toBe(0)
|
expect(discoveryCalls.length).toBe(0)
|
||||||
|
|
||||||
expect(mockExecuteProviderRequest).toHaveBeenCalled()
|
const providerCalls = fetchCalls.filter((c) => c.url.includes('/api/providers'))
|
||||||
const providerCallArgs = mockExecuteProviderRequest.mock.calls[0]
|
expect(providerCalls.length).toBe(1)
|
||||||
expect(providerCallArgs[1].tools.length).toBe(3)
|
|
||||||
|
const providerRequestBody = JSON.parse(providerCalls[0].options.body)
|
||||||
|
expect(providerRequestBody.tools.length).toBe(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fallback to discovery for MCP tools without cached schema', async () => {
|
it('should should fallback to discovery for MCP tools without cached schema', async () => {
|
||||||
const fetchCalls: any[] = []
|
const fetchCalls: any[] = []
|
||||||
|
|
||||||
mockFetch.mockImplementation((url: string, options: any) => {
|
mockFetch.mockImplementation((url: string, options: any) => {
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ import { createMcpToolId } from '@/lib/mcp/utils'
|
|||||||
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||||
import { getAllBlocks } from '@/blocks'
|
import { getAllBlocks } from '@/blocks'
|
||||||
import type { BlockOutput } from '@/blocks/types'
|
import type { BlockOutput } from '@/blocks/types'
|
||||||
import { AGENT, BlockType, DEFAULTS, REFERENCE, stripCustomToolPrefix } from '@/executor/constants'
|
import {
|
||||||
|
AGENT,
|
||||||
|
BlockType,
|
||||||
|
DEFAULTS,
|
||||||
|
HTTP,
|
||||||
|
REFERENCE,
|
||||||
|
stripCustomToolPrefix,
|
||||||
|
} from '@/executor/constants'
|
||||||
import { memoryService } from '@/executor/handlers/agent/memory'
|
import { memoryService } from '@/executor/handlers/agent/memory'
|
||||||
import type {
|
import type {
|
||||||
AgentInputs,
|
AgentInputs,
|
||||||
@@ -16,7 +23,7 @@ import type {
|
|||||||
} from '@/executor/handlers/agent/types'
|
} from '@/executor/handlers/agent/types'
|
||||||
import type { BlockHandler, ExecutionContext, StreamingExecution } from '@/executor/types'
|
import type { BlockHandler, ExecutionContext, StreamingExecution } from '@/executor/types'
|
||||||
import { collectBlockData } from '@/executor/utils/block-data'
|
import { collectBlockData } from '@/executor/utils/block-data'
|
||||||
import { buildAPIUrl, buildAuthHeaders } from '@/executor/utils/http'
|
import { buildAPIUrl, buildAuthHeaders, extractAPIErrorMessage } from '@/executor/utils/http'
|
||||||
import { stringifyJSON } from '@/executor/utils/json'
|
import { stringifyJSON } from '@/executor/utils/json'
|
||||||
import {
|
import {
|
||||||
validateBlockType,
|
validateBlockType,
|
||||||
@@ -45,9 +52,11 @@ export class AgentBlockHandler implements BlockHandler {
|
|||||||
block: SerializedBlock,
|
block: SerializedBlock,
|
||||||
inputs: AgentInputs
|
inputs: AgentInputs
|
||||||
): Promise<BlockOutput | StreamingExecution> {
|
): Promise<BlockOutput | StreamingExecution> {
|
||||||
|
// Filter out unavailable MCP tools early so they don't appear in logs/inputs
|
||||||
const filteredTools = await this.filterUnavailableMcpTools(ctx, inputs.tools || [])
|
const filteredTools = await this.filterUnavailableMcpTools(ctx, inputs.tools || [])
|
||||||
const filteredInputs = { ...inputs, tools: filteredTools }
|
const filteredInputs = { ...inputs, tools: filteredTools }
|
||||||
|
|
||||||
|
// Validate tool permissions before processing
|
||||||
await this.validateToolPermissions(ctx, filteredInputs.tools || [])
|
await this.validateToolPermissions(ctx, filteredInputs.tools || [])
|
||||||
|
|
||||||
const responseFormat = this.parseResponseFormat(filteredInputs.responseFormat)
|
const responseFormat = this.parseResponseFormat(filteredInputs.responseFormat)
|
||||||
@@ -71,7 +80,13 @@ export class AgentBlockHandler implements BlockHandler {
|
|||||||
streaming: streamingConfig.shouldUseStreaming ?? false,
|
streaming: streamingConfig.shouldUseStreaming ?? false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await this.executeProviderRequest(ctx, providerRequest, block, responseFormat)
|
const result = await this.executeProviderRequest(
|
||||||
|
ctx,
|
||||||
|
providerRequest,
|
||||||
|
block,
|
||||||
|
responseFormat,
|
||||||
|
filteredInputs
|
||||||
|
)
|
||||||
|
|
||||||
if (this.isStreamingExecution(result)) {
|
if (this.isStreamingExecution(result)) {
|
||||||
if (filteredInputs.memoryType && filteredInputs.memoryType !== 'none') {
|
if (filteredInputs.memoryType && filteredInputs.memoryType !== 'none') {
|
||||||
@@ -981,60 +996,157 @@ export class AgentBlockHandler implements BlockHandler {
|
|||||||
ctx: ExecutionContext,
|
ctx: ExecutionContext,
|
||||||
providerRequest: any,
|
providerRequest: any,
|
||||||
block: SerializedBlock,
|
block: SerializedBlock,
|
||||||
responseFormat: any
|
responseFormat: any,
|
||||||
|
inputs: AgentInputs
|
||||||
): Promise<BlockOutput | StreamingExecution> {
|
): Promise<BlockOutput | StreamingExecution> {
|
||||||
const providerId = providerRequest.provider
|
const providerId = providerRequest.provider
|
||||||
const model = providerRequest.model
|
const model = providerRequest.model
|
||||||
const providerStartTime = Date.now()
|
const providerStartTime = Date.now()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let finalApiKey: string | undefined = providerRequest.apiKey
|
const isBrowser = typeof window !== 'undefined'
|
||||||
|
|
||||||
if (providerId === 'vertex' && providerRequest.vertexCredential) {
|
if (!isBrowser) {
|
||||||
finalApiKey = await this.resolveVertexCredential(
|
return this.executeServerSide(
|
||||||
providerRequest.vertexCredential,
|
ctx,
|
||||||
ctx.workflowId
|
providerRequest,
|
||||||
|
providerId,
|
||||||
|
model,
|
||||||
|
block,
|
||||||
|
responseFormat,
|
||||||
|
providerStartTime
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return this.executeBrowserSide(
|
||||||
const { blockData, blockNameMapping } = collectBlockData(ctx)
|
ctx,
|
||||||
|
providerRequest,
|
||||||
const response = await executeProviderRequest(providerId, {
|
block,
|
||||||
model,
|
responseFormat,
|
||||||
systemPrompt: 'systemPrompt' in providerRequest ? providerRequest.systemPrompt : undefined,
|
providerStartTime,
|
||||||
context: 'context' in providerRequest ? providerRequest.context : undefined,
|
inputs
|
||||||
tools: providerRequest.tools,
|
)
|
||||||
temperature: providerRequest.temperature,
|
|
||||||
maxTokens: providerRequest.maxTokens,
|
|
||||||
apiKey: finalApiKey,
|
|
||||||
azureEndpoint: providerRequest.azureEndpoint,
|
|
||||||
azureApiVersion: providerRequest.azureApiVersion,
|
|
||||||
vertexProject: providerRequest.vertexProject,
|
|
||||||
vertexLocation: providerRequest.vertexLocation,
|
|
||||||
bedrockAccessKeyId: providerRequest.bedrockAccessKeyId,
|
|
||||||
bedrockSecretKey: providerRequest.bedrockSecretKey,
|
|
||||||
bedrockRegion: providerRequest.bedrockRegion,
|
|
||||||
responseFormat: providerRequest.responseFormat,
|
|
||||||
workflowId: providerRequest.workflowId,
|
|
||||||
workspaceId: ctx.workspaceId,
|
|
||||||
stream: providerRequest.stream,
|
|
||||||
messages: 'messages' in providerRequest ? providerRequest.messages : undefined,
|
|
||||||
environmentVariables: ctx.environmentVariables || {},
|
|
||||||
workflowVariables: ctx.workflowVariables || {},
|
|
||||||
blockData,
|
|
||||||
blockNameMapping,
|
|
||||||
isDeployedContext: ctx.isDeployedContext,
|
|
||||||
reasoningEffort: providerRequest.reasoningEffort,
|
|
||||||
verbosity: providerRequest.verbosity,
|
|
||||||
})
|
|
||||||
|
|
||||||
return this.processProviderResponse(response, block, responseFormat)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleExecutionError(error, providerStartTime, providerId, model, ctx, block)
|
this.handleExecutionError(error, providerStartTime, providerId, model, ctx, block)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async executeServerSide(
|
||||||
|
ctx: ExecutionContext,
|
||||||
|
providerRequest: any,
|
||||||
|
providerId: string,
|
||||||
|
model: string,
|
||||||
|
block: SerializedBlock,
|
||||||
|
responseFormat: any,
|
||||||
|
providerStartTime: number
|
||||||
|
) {
|
||||||
|
let finalApiKey: string | undefined = providerRequest.apiKey
|
||||||
|
|
||||||
|
if (providerId === 'vertex' && providerRequest.vertexCredential) {
|
||||||
|
finalApiKey = await this.resolveVertexCredential(
|
||||||
|
providerRequest.vertexCredential,
|
||||||
|
ctx.workflowId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { blockData, blockNameMapping } = collectBlockData(ctx)
|
||||||
|
|
||||||
|
const response = await executeProviderRequest(providerId, {
|
||||||
|
model,
|
||||||
|
systemPrompt: 'systemPrompt' in providerRequest ? providerRequest.systemPrompt : undefined,
|
||||||
|
context: 'context' in providerRequest ? providerRequest.context : undefined,
|
||||||
|
tools: providerRequest.tools,
|
||||||
|
temperature: providerRequest.temperature,
|
||||||
|
maxTokens: providerRequest.maxTokens,
|
||||||
|
apiKey: finalApiKey,
|
||||||
|
azureEndpoint: providerRequest.azureEndpoint,
|
||||||
|
azureApiVersion: providerRequest.azureApiVersion,
|
||||||
|
vertexProject: providerRequest.vertexProject,
|
||||||
|
vertexLocation: providerRequest.vertexLocation,
|
||||||
|
bedrockAccessKeyId: providerRequest.bedrockAccessKeyId,
|
||||||
|
bedrockSecretKey: providerRequest.bedrockSecretKey,
|
||||||
|
bedrockRegion: providerRequest.bedrockRegion,
|
||||||
|
responseFormat: providerRequest.responseFormat,
|
||||||
|
workflowId: providerRequest.workflowId,
|
||||||
|
workspaceId: ctx.workspaceId,
|
||||||
|
stream: providerRequest.stream,
|
||||||
|
messages: 'messages' in providerRequest ? providerRequest.messages : undefined,
|
||||||
|
environmentVariables: ctx.environmentVariables || {},
|
||||||
|
workflowVariables: ctx.workflowVariables || {},
|
||||||
|
blockData,
|
||||||
|
blockNameMapping,
|
||||||
|
isDeployedContext: ctx.isDeployedContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.processProviderResponse(response, block, responseFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeBrowserSide(
|
||||||
|
ctx: ExecutionContext,
|
||||||
|
providerRequest: any,
|
||||||
|
block: SerializedBlock,
|
||||||
|
responseFormat: any,
|
||||||
|
providerStartTime: number,
|
||||||
|
inputs: AgentInputs
|
||||||
|
) {
|
||||||
|
const url = buildAPIUrl('/api/providers')
|
||||||
|
const response = await fetch(url.toString(), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': HTTP.CONTENT_TYPE.JSON },
|
||||||
|
body: stringifyJSON(providerRequest),
|
||||||
|
signal: AbortSignal.timeout(AGENT.REQUEST_TIMEOUT),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorMessage = await extractAPIErrorMessage(response)
|
||||||
|
throw new Error(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers.get('Content-Type')
|
||||||
|
if (contentType?.includes(HTTP.CONTENT_TYPE.EVENT_STREAM)) {
|
||||||
|
return this.handleStreamingResponse(response, block, ctx, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
return this.processProviderResponse(result, block, responseFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleStreamingResponse(
|
||||||
|
response: Response,
|
||||||
|
block: SerializedBlock,
|
||||||
|
_ctx?: ExecutionContext,
|
||||||
|
_inputs?: AgentInputs
|
||||||
|
): Promise<StreamingExecution> {
|
||||||
|
const executionDataHeader = response.headers.get('X-Execution-Data')
|
||||||
|
|
||||||
|
if (executionDataHeader) {
|
||||||
|
try {
|
||||||
|
const executionData = JSON.parse(executionDataHeader)
|
||||||
|
return {
|
||||||
|
stream: response.body!,
|
||||||
|
execution: {
|
||||||
|
success: executionData.success,
|
||||||
|
output: executionData.output || {},
|
||||||
|
error: executionData.error,
|
||||||
|
logs: [],
|
||||||
|
metadata: executionData.metadata || {
|
||||||
|
duration: DEFAULTS.EXECUTION_TIME,
|
||||||
|
startTime: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
isStreaming: true,
|
||||||
|
blockId: block.id,
|
||||||
|
blockName: block.metadata?.name,
|
||||||
|
blockType: block.metadata?.id,
|
||||||
|
} as any,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to parse execution data from header:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.createMinimalStreamingExecution(response.body!)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a Vertex AI OAuth credential to an access token
|
* Resolves a Vertex AI OAuth credential to an access token
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -85,11 +85,7 @@ export class ConditionBlockHandler implements BlockHandler {
|
|||||||
|
|
||||||
const sourceBlockId = ctx.workflow?.connections.find((conn) => conn.target === block.id)?.source
|
const sourceBlockId = ctx.workflow?.connections.find((conn) => conn.target === block.id)?.source
|
||||||
const evalContext = this.buildEvaluationContext(ctx, sourceBlockId)
|
const evalContext = this.buildEvaluationContext(ctx, sourceBlockId)
|
||||||
const rawSourceOutput = sourceBlockId ? ctx.blockStates.get(sourceBlockId)?.output : null
|
const sourceOutput = sourceBlockId ? ctx.blockStates.get(sourceBlockId)?.output : null
|
||||||
|
|
||||||
// Filter out _pauseMetadata from source output to prevent the engine from
|
|
||||||
// thinking this block is pausing (it was already resumed by the HITL block)
|
|
||||||
const sourceOutput = this.filterPauseMetadata(rawSourceOutput)
|
|
||||||
|
|
||||||
const outgoingConnections = ctx.workflow?.connections.filter((conn) => conn.source === block.id)
|
const outgoingConnections = ctx.workflow?.connections.filter((conn) => conn.source === block.id)
|
||||||
|
|
||||||
@@ -129,14 +125,6 @@ export class ConditionBlockHandler implements BlockHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private filterPauseMetadata(output: any): any {
|
|
||||||
if (!output || typeof output !== 'object') {
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
const { _pauseMetadata, ...rest } = output
|
|
||||||
return rest
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseConditions(input: any): Array<{ id: string; title: string; value: string }> {
|
private parseConditions(input: any): Array<{ id: string; title: string; value: string }> {
|
||||||
try {
|
try {
|
||||||
const conditions = Array.isArray(input) ? input : JSON.parse(input || '[]')
|
const conditions = Array.isArray(input) ? input : JSON.parse(input || '[]')
|
||||||
|
|||||||
@@ -16,168 +16,6 @@ export interface AuthResult {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves userId from a verified internal JWT token.
|
|
||||||
* Extracts workflowId/userId from URL params or POST body, then looks up userId if needed.
|
|
||||||
*/
|
|
||||||
async function resolveUserFromJwt(
|
|
||||||
request: NextRequest,
|
|
||||||
verificationUserId: string | null,
|
|
||||||
options: { requireWorkflowId?: boolean }
|
|
||||||
): Promise<AuthResult> {
|
|
||||||
let workflowId: string | null = null
|
|
||||||
let userId: string | null = verificationUserId
|
|
||||||
|
|
||||||
const { searchParams } = new URL(request.url)
|
|
||||||
workflowId = searchParams.get('workflowId')
|
|
||||||
if (!userId) {
|
|
||||||
userId = searchParams.get('userId')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!workflowId && !userId && request.method === 'POST') {
|
|
||||||
try {
|
|
||||||
const clonedRequest = request.clone()
|
|
||||||
const bodyText = await clonedRequest.text()
|
|
||||||
if (bodyText) {
|
|
||||||
const body = JSON.parse(bodyText)
|
|
||||||
workflowId = body.workflowId || body._context?.workflowId
|
|
||||||
userId = userId || body.userId || body._context?.userId
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Ignore JSON parse errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userId) {
|
|
||||||
return { success: true, userId, authType: 'internal_jwt' }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workflowId) {
|
|
||||||
const [workflowData] = await db
|
|
||||||
.select({ userId: workflow.userId })
|
|
||||||
.from(workflow)
|
|
||||||
.where(eq(workflow.id, workflowId))
|
|
||||||
.limit(1)
|
|
||||||
|
|
||||||
if (!workflowData) {
|
|
||||||
return { success: false, error: 'Workflow not found' }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, userId: workflowData.userId, authType: 'internal_jwt' }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.requireWorkflowId !== false) {
|
|
||||||
return { success: false, error: 'workflowId or userId required for internal JWT calls' }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, authType: 'internal_jwt' }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for internal JWT authentication only.
|
|
||||||
* Use this for routes that should ONLY be accessible by the executor (server-to-server).
|
|
||||||
* Rejects session and API key authentication.
|
|
||||||
*
|
|
||||||
* @param request - The incoming request
|
|
||||||
* @param options - Optional configuration
|
|
||||||
* @param options.requireWorkflowId - Whether workflowId/userId is required (default: true)
|
|
||||||
*/
|
|
||||||
export async function checkInternalAuth(
|
|
||||||
request: NextRequest,
|
|
||||||
options: { requireWorkflowId?: boolean } = {}
|
|
||||||
): Promise<AuthResult> {
|
|
||||||
try {
|
|
||||||
const authHeader = request.headers.get('authorization')
|
|
||||||
|
|
||||||
const apiKeyHeader = request.headers.get('x-api-key')
|
|
||||||
if (apiKeyHeader) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'API key access not allowed for this endpoint. Use workflow execution instead.',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authHeader?.startsWith('Bearer ')) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'Internal authentication required',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = authHeader.split(' ')[1]
|
|
||||||
const verification = await verifyInternalToken(token)
|
|
||||||
|
|
||||||
if (!verification.valid) {
|
|
||||||
return { success: false, error: 'Invalid internal token' }
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolveUserFromJwt(request, verification.userId || null, options)
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error in internal authentication:', error)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'Authentication error',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for session or internal JWT authentication.
|
|
||||||
* Use this for routes that should be accessible by the UI and executor,
|
|
||||||
* but NOT by external API keys.
|
|
||||||
*
|
|
||||||
* @param request - The incoming request
|
|
||||||
* @param options - Optional configuration
|
|
||||||
* @param options.requireWorkflowId - Whether workflowId/userId is required for JWT (default: true)
|
|
||||||
*/
|
|
||||||
export async function checkSessionOrInternalAuth(
|
|
||||||
request: NextRequest,
|
|
||||||
options: { requireWorkflowId?: boolean } = {}
|
|
||||||
): Promise<AuthResult> {
|
|
||||||
try {
|
|
||||||
// 1. Reject API keys first
|
|
||||||
const apiKeyHeader = request.headers.get('x-api-key')
|
|
||||||
if (apiKeyHeader) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'API key access not allowed for this endpoint',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Check for internal JWT token
|
|
||||||
const authHeader = request.headers.get('authorization')
|
|
||||||
if (authHeader?.startsWith('Bearer ')) {
|
|
||||||
const token = authHeader.split(' ')[1]
|
|
||||||
const verification = await verifyInternalToken(token)
|
|
||||||
|
|
||||||
if (verification.valid) {
|
|
||||||
return resolveUserFromJwt(request, verification.userId || null, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Try session auth (for web UI)
|
|
||||||
const session = await getSession()
|
|
||||||
if (session?.user?.id) {
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
userId: session.user.id,
|
|
||||||
authType: 'session',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'Authentication required - provide session or internal JWT',
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error in session/internal authentication:', error)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'Authentication error',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for authentication using any of the 3 supported methods:
|
* Check for authentication using any of the 3 supported methods:
|
||||||
* 1. Session authentication (cookies)
|
* 1. Session authentication (cookies)
|
||||||
@@ -198,7 +36,70 @@ export async function checkHybridAuth(
|
|||||||
const verification = await verifyInternalToken(token)
|
const verification = await verifyInternalToken(token)
|
||||||
|
|
||||||
if (verification.valid) {
|
if (verification.valid) {
|
||||||
return resolveUserFromJwt(request, verification.userId || null, options)
|
let workflowId: string | null = null
|
||||||
|
let userId: string | null = verification.userId || null
|
||||||
|
|
||||||
|
const { searchParams } = new URL(request.url)
|
||||||
|
workflowId = searchParams.get('workflowId')
|
||||||
|
if (!userId) {
|
||||||
|
userId = searchParams.get('userId')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!workflowId && !userId && request.method === 'POST') {
|
||||||
|
try {
|
||||||
|
// Clone the request to avoid consuming the original body
|
||||||
|
const clonedRequest = request.clone()
|
||||||
|
const bodyText = await clonedRequest.text()
|
||||||
|
if (bodyText) {
|
||||||
|
const body = JSON.parse(bodyText)
|
||||||
|
workflowId = body.workflowId || body._context?.workflowId
|
||||||
|
userId = userId || body.userId || body._context?.userId
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore JSON parse errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
userId,
|
||||||
|
authType: 'internal_jwt',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workflowId) {
|
||||||
|
const [workflowData] = await db
|
||||||
|
.select({ userId: workflow.userId })
|
||||||
|
.from(workflow)
|
||||||
|
.where(eq(workflow.id, workflowId))
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
if (!workflowData) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Workflow not found',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
userId: workflowData.userId,
|
||||||
|
authType: 'internal_jwt',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.requireWorkflowId !== false) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'workflowId or userId required for internal JWT calls',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
authType: 'internal_jwt',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,3 @@ export type { AnonymousSession } from './anonymous'
|
|||||||
export { createAnonymousSession, ensureAnonymousUserExists } from './anonymous'
|
export { createAnonymousSession, ensureAnonymousUserExists } from './anonymous'
|
||||||
export { auth, getSession, signIn, signUp } from './auth'
|
export { auth, getSession, signIn, signUp } from './auth'
|
||||||
export { ANONYMOUS_USER, ANONYMOUS_USER_ID } from './constants'
|
export { ANONYMOUS_USER, ANONYMOUS_USER_ID } from './constants'
|
||||||
export type { AuthResult } from './hybrid'
|
|
||||||
export { checkHybridAuth, checkInternalAuth, checkSessionOrInternalAuth } from './hybrid'
|
|
||||||
|
|||||||
@@ -567,32 +567,6 @@ export class PauseResumeManager {
|
|||||||
|
|
||||||
stateCopy.blockStates[stateBlockKey] = pauseBlockState
|
stateCopy.blockStates[stateBlockKey] = pauseBlockState
|
||||||
|
|
||||||
// Update the block log entry with the merged output so logs show the submission data
|
|
||||||
if (Array.isArray(stateCopy.blockLogs)) {
|
|
||||||
const blockLogIndex = stateCopy.blockLogs.findIndex(
|
|
||||||
(log: { blockId: string }) =>
|
|
||||||
log.blockId === stateBlockKey ||
|
|
||||||
log.blockId === pauseBlockId ||
|
|
||||||
log.blockId === contextId
|
|
||||||
)
|
|
||||||
if (blockLogIndex !== -1) {
|
|
||||||
// Filter output for logging (exclude internal fields and response)
|
|
||||||
const filteredOutput: Record<string, unknown> = {}
|
|
||||||
for (const [key, value] of Object.entries(mergedOutput)) {
|
|
||||||
if (key.startsWith('_')) continue
|
|
||||||
if (key === 'response') continue
|
|
||||||
filteredOutput[key] = value
|
|
||||||
}
|
|
||||||
stateCopy.blockLogs[blockLogIndex] = {
|
|
||||||
...stateCopy.blockLogs[blockLogIndex],
|
|
||||||
blockId: stateBlockKey,
|
|
||||||
output: filteredOutput,
|
|
||||||
durationMs: pauseDurationMs,
|
|
||||||
endedAt: new Date().toISOString(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(stateCopy.executedBlocks)) {
|
if (Array.isArray(stateCopy.executedBlocks)) {
|
||||||
const filtered = stateCopy.executedBlocks.filter(
|
const filtered = stateCopy.executedBlocks.filter(
|
||||||
(id: string) => id !== pauseBlockId && id !== contextId
|
(id: string) => id !== pauseBlockId && id !== contextId
|
||||||
|
|||||||
@@ -1125,17 +1125,6 @@ const sseHandlers: Record<string, SSEHandler> = {
|
|||||||
await get().handleNewChatCreation(context.newChatId)
|
await get().handleNewChatCreation(context.newChatId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title_updated: (_data, _context, get, set) => {
|
|
||||||
const title = _data.title
|
|
||||||
if (!title) return
|
|
||||||
const { currentChat, chats } = get()
|
|
||||||
if (currentChat) {
|
|
||||||
set({
|
|
||||||
currentChat: { ...currentChat, title },
|
|
||||||
chats: chats.map((c) => (c.id === currentChat.id ? { ...c, title } : c)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tool_result: (data, context, get, set) => {
|
tool_result: (data, context, get, set) => {
|
||||||
try {
|
try {
|
||||||
const toolCallId: string | undefined = data?.toolCallId || data?.data?.id
|
const toolCallId: string | undefined = data?.toolCallId || data?.data?.id
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user