Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Visit [http://localhost:3000](http://localhost:3000) to get started.
- **AI Gateway**: Generate Text, Generate Image
- **Firecrawl**: Scrape URL, Search Web
- **Linear**: Create Ticket, Find Issues
- **Olostep**: Scrape URL, Search Web, Map Website, AI Answer
- **Resend**: Send Email
- **Slack**: Send Slack Message
- **v0**: Create Chat, Send Message
Expand Down
46 changes: 46 additions & 0 deletions app/api/integrations/[integrationId]/test/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export async function POST(
integration.config.firecrawlApiKey
);
break;
case "olostep":
result = await testOlostepConnection(integration.config.olostepApiKey);
break;
default:
return NextResponse.json(
{ error: "Invalid integration type" },
Expand Down Expand Up @@ -281,3 +284,46 @@ async function testFirecrawlConnection(
};
}
}

async function testOlostepConnection(
apiKey?: string
): Promise<TestConnectionResult> {
try {
if (!apiKey) {
return {
status: "error",
message: "Olostep API Key is not configured",
};
}

const response = await fetch("https://api.olostep.com/v1/scrapes", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
url_to_scrape: "https://example.com",
formats: ["markdown"],
}),
});

if (!response.ok) {
const errorText = await response.text();
return {
status: "error",
message: `Connection failed: ${errorText}`,
};
}

return {
status: "success",
message: "Connected successfully",
};
} catch (error) {
return {
status: "error",
message: error instanceof Error ? error.message : "Connection failed",
};
}
}
30 changes: 25 additions & 5 deletions lib/step-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* This registry enables dynamic step imports that are statically analyzable
* by the bundler. Each action type maps to its step importer function.
*
* Generated entries: 10
* Generated entries: 14
*/

import "server-only";
Expand Down Expand Up @@ -45,15 +45,15 @@ export const PLUGIN_STEP_IMPORTERS: Record<string, StepImporter> = {
importer: () => import("@/plugins/firecrawl/steps/scrape"),
stepFunction: "firecrawlScrapeStep",
},
Scrape: {
"Scrape": {
importer: () => import("@/plugins/firecrawl/steps/scrape"),
stepFunction: "firecrawlScrapeStep",
},
"firecrawl/search": {
importer: () => import("@/plugins/firecrawl/steps/search"),
stepFunction: "firecrawlSearchStep",
},
Search: {
"Search": {
importer: () => import("@/plugins/firecrawl/steps/search"),
stepFunction: "firecrawlSearchStep",
},
Expand All @@ -73,6 +73,22 @@ export const PLUGIN_STEP_IMPORTERS: Record<string, StepImporter> = {
importer: () => import("@/plugins/linear/steps/find-issues"),
stepFunction: "findIssuesStep",
},
"olostep/scrape": {
importer: () => import("@/plugins/olostep/steps/scrape"),
stepFunction: "olostepScrapeStep",
},
"olostep/search": {
importer: () => import("@/plugins/olostep/steps/search"),
stepFunction: "olostepSearchStep",
},
"olostep/map": {
importer: () => import("@/plugins/olostep/steps/map"),
stepFunction: "olostepMapStep",
},
"olostep/answer": {
importer: () => import("@/plugins/olostep/steps/answer"),
stepFunction: "olostepAnswerStep",
},
"resend/send-email": {
importer: () => import("@/plugins/resend/steps/send-email"),
stepFunction: "sendEmailStep",
Expand Down Expand Up @@ -118,12 +134,16 @@ export const ACTION_LABELS: Record<string, string> = {
"firecrawl/search": "Search Web",
"linear/create-ticket": "Create Ticket",
"linear/find-issues": "Find Issues",
"olostep/scrape": "Scrape URL",
"olostep/search": "Search Web",
"olostep/map": "Map Website",
"olostep/answer": "AI Answer",
"resend/send-email": "Send Email",
"slack/send-message": "Send Slack Message",
"v0/create-chat": "Create Chat",
"v0/send-message": "Send Message",
Scrape: "Scrape URL",
Search: "Search Web",
"Scrape": "Scrape URL",
"Search": "Search Web",
"Generate Text": "Generate Text",
"Generate Image": "Generate Image",
"Send Email": "Send Email",
Expand Down
3 changes: 2 additions & 1 deletion lib/types/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 2. Add a system integration to SYSTEM_INTEGRATION_TYPES in discover-plugins.ts
* 3. Run: pnpm discover-plugins
*
* Generated types: ai-gateway, database, firecrawl, linear, resend, slack, v0
* Generated types: ai-gateway, database, firecrawl, linear, olostep, resend, slack, v0
*/

// Integration type union - plugins + system integrations
Expand All @@ -18,6 +18,7 @@ export type IntegrationType =
| "database"
| "firecrawl"
| "linear"
| "olostep"
| "resend"
| "slack"
| "v0";
Expand Down
3 changes: 2 additions & 1 deletion plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
* 1. Delete the plugin directory
* 2. Run: pnpm discover-plugins (or it runs automatically on build)
*
* Discovered plugins: ai-gateway, firecrawl, linear, resend, slack, v0
* Discovered plugins: ai-gateway, firecrawl, linear, olostep, resend, slack, v0
*/

import "./ai-gateway";
import "./firecrawl";
import "./linear";
import "./olostep";
import "./resend";
import "./slack";
import "./v0";
Expand Down
50 changes: 50 additions & 0 deletions plugins/olostep/codegen/answer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Code generation template for Olostep Answer action
* This template is used when exporting workflows to standalone Next.js projects
* It uses environment variables instead of integrationId
*/
export const answerCodegenTemplate = `export async function olostepAnswerStep(input: {
question: string;
urls?: string[];
searchQuery?: string;
}) {
"use step";

const requestBody: Record<string, unknown> = {
question: input.question,
};

if (input.urls && input.urls.length > 0) {
requestBody.urls = input.urls;
}

if (input.searchQuery) {
requestBody.search_query = input.searchQuery;
}

const response = await fetch('https://api.olostep.com/v1/answer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': \`Bearer \${process.env.OLOSTEP_API_KEY}\`,
},
body: JSON.stringify(requestBody),
});

if (!response.ok) {
throw new Error(\`Olostep API error: \${await response.text()}\`);
}

const result = await response.json();

return {
answer: result.answer || result.response || '',
sources: (result.sources || result.references || []).map((source: any) => ({
url: source.url || source.link,
title: source.title,
})),
};
}`;



40 changes: 40 additions & 0 deletions plugins/olostep/codegen/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Code generation template for Olostep Map action
* This template is used when exporting workflows to standalone Next.js projects
* It uses environment variables instead of integrationId
*/
export const mapCodegenTemplate = `export async function olostepMapStep(input: {
url: string;
limit?: number;
includeSubdomains?: boolean;
}) {
"use step";

const response = await fetch('https://api.olostep.com/v1/map', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': \`Bearer \${process.env.OLOSTEP_API_KEY}\`,
},
body: JSON.stringify({
url: input.url,
limit: input.limit || 100,
include_subdomains: input.includeSubdomains || false,
}),
});

if (!response.ok) {
throw new Error(\`Olostep API error: \${await response.text()}\`);
}

const result = await response.json();
const urls = result.urls || result.links || [];

return {
urls: urls.slice(0, input.limit || 100),
totalUrls: urls.length,
};
}`;



46 changes: 46 additions & 0 deletions plugins/olostep/codegen/scrape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Code generation template for Olostep Scrape action
* This template is used when exporting workflows to standalone Next.js projects
* It uses environment variables instead of integrationId
*/
export const scrapeCodegenTemplate = `export async function olostepScrapeStep(input: {
url: string;
formats?: ('markdown' | 'html' | 'text')[];
waitForSelector?: string;
}) {
"use step";

const response = await fetch('https://api.olostep.com/v1/scrapes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': \`Bearer \${process.env.OLOSTEP_API_KEY}\`,
},
body: JSON.stringify({
url_to_scrape: input.url,
formats: input.formats || ['markdown'],
wait_for_selector: input.waitForSelector,
}),
});

if (!response.ok) {
throw new Error(\`Olostep API error: \${await response.text()}\`);
}

const result = await response.json();

return {
markdown: result.markdown_content || result.markdown,
html: result.html_content || result.html,
metadata: {
title: result.title,
url: result.url,
statusCode: result.status_code,
},
};
}`;





52 changes: 52 additions & 0 deletions plugins/olostep/codegen/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Code generation template for Olostep Search action
* This template is used when exporting workflows to standalone Next.js projects
* It uses environment variables instead of integrationId
*/
export const searchCodegenTemplate = `export async function olostepSearchStep(input: {
query: string;
limit?: number;
country?: string;
}) {
"use step";

const params = new URLSearchParams({
query: input.query,
limit: String(input.limit || 10),
});

if (input.country) {
params.append('country', input.country);
}

const response = await fetch(
\`https://api.olostep.com/v1/google-search?\${params.toString()}\`,
{
method: 'GET',
headers: {
'Authorization': \`Bearer \${process.env.OLOSTEP_API_KEY}\`,
},
}
);

if (!response.ok) {
throw new Error(\`Olostep API error: \${await response.text()}\`);
}

const result = await response.json();

return {
results: (result.results || result.items || []).slice(0, input.limit || 10).map((item: any) => ({
url: item.url || item.link,
title: item.title,
description: item.description || item.snippet,
markdown: item.markdown,
})),
totalResults: result.total_results,
};
}`;





24 changes: 24 additions & 0 deletions plugins/olostep/icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function OlostepIcon({ className }: { className?: string }) {
return (
<svg
aria-label="Olostep"
className={className}
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<title>Olostep</title>
<rect width="100" height="100" rx="20" fill="#6366F1" />
<text
x="50"
y="72"
textAnchor="middle"
fill="white"
fontSize="60"
fontWeight="bold"
fontFamily="system-ui, -apple-system, sans-serif"
>
O
</text>
</svg>
);
}
Loading