@zerowidth/workbench-sdk 2.0.0-alpha.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/nodes/combine-document-chunks/combine-document-chunks.config.json +8 -0
  2. package/nodes/combine-document-chunks/combine-document-chunks.process.js +9 -2
  3. package/nodes/describe-tables/describe-tables.config.json +59 -0
  4. package/nodes/describe-tables/describe-tables.process.js +40 -0
  5. package/nodes/expand-chunk-context/expand-chunk-context.config.json +87 -0
  6. package/nodes/expand-chunk-context/expand-chunk-context.process.js +34 -0
  7. package/nodes/find-entity-path/find-entity-path.config.json +73 -0
  8. package/nodes/find-entity-path/find-entity-path.process.js +51 -0
  9. package/nodes/firecrawl-search/firecrawl-search.config.json +46 -0
  10. package/nodes/firecrawl-search/firecrawl-search.process.js +54 -0
  11. package/nodes/firecrawl-search/firecrawl-search.tests.json +9 -0
  12. package/nodes/get-chunk-by-index/get-chunk-by-index.config.json +8 -0
  13. package/nodes/get-chunk-by-index/get-chunk-by-index.process.js +9 -2
  14. package/nodes/get-entity-neighbors/get-entity-neighbors.config.json +81 -0
  15. package/nodes/get-entity-neighbors/get-entity-neighbors.process.js +51 -0
  16. package/nodes/keyword-search/keyword-search.config.json +67 -0
  17. package/nodes/keyword-search/keyword-search.process.js +45 -0
  18. package/nodes/knowledge-base/knowledge-base.config.json +32 -0
  19. package/nodes/knowledge-base/knowledge-base.process.js +15 -0
  20. package/nodes/list-documents/list-documents.config.json +60 -0
  21. package/nodes/list-documents/list-documents.process.js +25 -0
  22. package/nodes/list-entities/list-entities.config.json +74 -0
  23. package/nodes/list-entities/list-entities.process.js +25 -0
  24. package/nodes/query-knowledge-base/query-knowledge-base.config.json +14 -7
  25. package/nodes/query-knowledge-base/query-knowledge-base.process.js +25 -5
  26. package/nodes/read-chunks/read-chunks.config.json +79 -0
  27. package/nodes/read-chunks/read-chunks.process.js +49 -0
  28. package/nodes/remote-mcp-tool/remote-mcp-tool.config.json +1 -0
  29. package/nodes/search-internet/search-internet.config.json +7 -7
  30. package/nodes/semantic-search/semantic-search.config.json +9 -1
  31. package/nodes/semantic-search/semantic-search.process.js +41 -18
  32. package/nodes/tool/tool.config.json +1 -0
  33. package/package.json +12 -7
  34. package/src/index.js +81 -11
  35. package/src/integrations/firecrawl.js +71 -0
  36. package/src/integrations/knowledge-base-interface.js +49 -0
  37. package/src/integrations/sqlite.js +535 -83
  38. package/src/types/knowledge_base.json +9 -0
  39. package/src/utilities/loaders.js +67 -11
  40. package/src/utilities/typers.js +15 -17
  41. package/src/utilities/validators.js +16 -1
  42. package/types/knowledge_base.json +9 -0
@@ -0,0 +1,67 @@
1
+ {
2
+ "display_name": "Keyword Search",
3
+ "tagline": "Search by exact words",
4
+ "description": "Search a knowledge base for chunks containing a word or phrase (case-insensitive substring match). Free and instant — no embeddings. The exact-term complement to Semantic Search; wire a Knowledge Base node into its knowledge_base input the same way.",
5
+ "category": "knowledge",
6
+ "is_constant": false,
7
+ "is_plugin": true,
8
+ "is_output": false,
9
+ "inputs": [
10
+ {
11
+ "name": "query",
12
+ "display_name": "Search Text",
13
+ "type": "string",
14
+ "description": "Word or phrase to match within chunk content",
15
+ "required": true
16
+ },
17
+ {
18
+ "name": "knowledge_base",
19
+ "display_name": "Knowledge Base",
20
+ "type": "knowledge_base",
21
+ "description": "The knowledge base to search, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
22
+ "required": false,
23
+ "default": null
24
+ },
25
+ {
26
+ "name": "limit",
27
+ "display_name": "Result Limit",
28
+ "type": "number",
29
+ "description": "Maximum number of results to return",
30
+ "default": 10
31
+ },
32
+ {
33
+ "name": "document_id",
34
+ "display_name": "Document ID Filter",
35
+ "type": "string",
36
+ "description": "Optional: restrict results to a specific document uuid.",
37
+ "default": null
38
+ }
39
+ ],
40
+ "outputs": [
41
+ {
42
+ "name": "results",
43
+ "display_name": "Search Results",
44
+ "type": "array",
45
+ "description": "Matching chunks, most matches first"
46
+ },
47
+ {
48
+ "name": "count",
49
+ "display_name": "Result Count",
50
+ "type": "number",
51
+ "description": "Number of results returned"
52
+ },
53
+ {
54
+ "name": "success",
55
+ "display_name": "Success",
56
+ "type": "boolean",
57
+ "description": "Whether the search completed successfully"
58
+ },
59
+ {
60
+ "name": "error",
61
+ "display_name": "Error",
62
+ "type": "string",
63
+ "description": "Error message if search failed"
64
+ }
65
+ ],
66
+ "settings": []
67
+ }
@@ -0,0 +1,45 @@
1
+ export default async ({ inputs, config }) => {
2
+ try {
3
+ // Resolve which knowledge base to search — SAME wiring as semantic-search
4
+ // and query-knowledge-base: a `knowledge_base` handle wired in from a
5
+ // Knowledge Base node names a specific KB by uuid (resolved to a per-KB
6
+ // integration keyed knowledgeBase:<uuid>), falling back to the
7
+ // flow-global KB. See ADR 0023.
8
+ const kbRef = inputs.knowledge_base;
9
+ const knowledgeBase =
10
+ (kbRef && kbRef.uuid
11
+ ? config.integrations?.[`knowledgeBase:${kbRef.uuid}`]
12
+ : null) ||
13
+ config.integrations?.knowledgeBase ||
14
+ config.integrations?.sqlite;
15
+
16
+ if (!knowledgeBase) {
17
+ throw new Error("Knowledge base integration not found. Make sure a knowledge database is available.");
18
+ }
19
+
20
+ const { query, limit = 10, document_id = null } = inputs;
21
+
22
+ if (!query || typeof query !== 'string') {
23
+ throw new Error("Query is required and must be a string");
24
+ }
25
+
26
+ // No embeddings — a case-insensitive substring match over chunk content.
27
+ const results = await knowledgeBase.keywordSearch(query, { limit, document_id });
28
+
29
+ return {
30
+ results: results,
31
+ count: results.length,
32
+ success: true,
33
+ error: null
34
+ };
35
+
36
+ } catch (error) {
37
+ // Return error information instead of throwing to prevent engine crash
38
+ return {
39
+ results: [],
40
+ count: 0,
41
+ success: false,
42
+ error: error.message
43
+ };
44
+ }
45
+ };
@@ -0,0 +1,32 @@
1
+ {
2
+ "display_name": "Knowledge Base",
3
+ "tagline": "Reference a knowledge base",
4
+ "description": "References a workspace knowledge base by id and emits a handle to it. Wire this node's output into a Semantic Search (or Query Knowledge Base) node to search that KB. Attach one Knowledge Base node to several search nodes to reuse the same KB — like importing a subflow, the KB is one thing they all depend on.",
5
+ "icon": "database",
6
+ "category": "knowledge",
7
+ "is_constant": true,
8
+ "is_plugin": false,
9
+ "is_output": false,
10
+ "custom_render_height": 76,
11
+ "inputs": [],
12
+ "outputs": [
13
+ {
14
+ "name": "knowledge_base",
15
+ "primary": true,
16
+ "display_name": "Knowledge Base",
17
+ "type": "knowledge_base",
18
+ "description": "A reference (handle) to the selected knowledge base."
19
+ }
20
+ ],
21
+ "settings": [
22
+ {
23
+ "name": "uuid",
24
+ "display_name": "Knowledge Base",
25
+ "type": "knowledge_base_ref",
26
+ "description": "The knowledge base this node references.",
27
+ "default": null
28
+ }
29
+ ],
30
+ "timeout": 3000,
31
+ "retry_limit": 1
32
+ }
@@ -0,0 +1,15 @@
1
+ export default async ({ inputs, settings, config }) => {
2
+ // A pure reference/source node: it carries a KB id in its settings and emits
3
+ // it as a `knowledge_base` handle. The runtime KB itself (the `.db`) is
4
+ // resolved host-side (ZeroWidth's flow-runner pulls each referenced KB and
5
+ // hands the engine an integration keyed by this uuid) — this node only names
6
+ // which one. Consumers (semantic-search) read the uuid off the handle and
7
+ // look up config.integrations.knowledgeBases[uuid].
8
+ const uuid = settings?.uuid ?? null;
9
+ if (!uuid) {
10
+ return { knowledge_base: null };
11
+ }
12
+ const handle = { uuid };
13
+ if (settings?.name) handle.name = settings.name;
14
+ return { knowledge_base: handle };
15
+ };
@@ -0,0 +1,60 @@
1
+ {
2
+ "display_name": "List Documents",
3
+ "tagline": "List everything in a KB",
4
+ "description": "List the documents in a knowledge base (id, name, type, size, chunk count) — for browsing or letting an agent pick a document to read. Wire a Knowledge Base node into knowledge_base, same as the search nodes.",
5
+ "category": "knowledge",
6
+ "is_constant": false,
7
+ "is_plugin": true,
8
+ "is_output": false,
9
+ "inputs": [
10
+ {
11
+ "name": "knowledge_base",
12
+ "display_name": "Knowledge Base",
13
+ "type": "knowledge_base",
14
+ "description": "The knowledge base to list, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
15
+ "required": false,
16
+ "default": null
17
+ },
18
+ {
19
+ "name": "limit",
20
+ "display_name": "Limit",
21
+ "type": "number",
22
+ "description": "Maximum number of documents to return",
23
+ "default": 100
24
+ },
25
+ {
26
+ "name": "offset",
27
+ "display_name": "Offset",
28
+ "type": "number",
29
+ "description": "Number of documents to skip (pagination)",
30
+ "default": 0
31
+ }
32
+ ],
33
+ "outputs": [
34
+ {
35
+ "name": "documents",
36
+ "display_name": "Documents",
37
+ "type": "array",
38
+ "description": "Document rows (id, display_name, file_type, file_size, chunk_count, …)"
39
+ },
40
+ {
41
+ "name": "count",
42
+ "display_name": "Count",
43
+ "type": "number",
44
+ "description": "Number of documents returned"
45
+ },
46
+ {
47
+ "name": "success",
48
+ "display_name": "Success",
49
+ "type": "boolean",
50
+ "description": "Whether the listing completed successfully"
51
+ },
52
+ {
53
+ "name": "error",
54
+ "display_name": "Error",
55
+ "type": "string",
56
+ "description": "Error message if it failed"
57
+ }
58
+ ],
59
+ "settings": []
60
+ }
@@ -0,0 +1,25 @@
1
+ export default async ({ inputs, config }) => {
2
+ try {
3
+ // Shared KB wiring (see semantic-search / keyword-search): a
4
+ // knowledge_base handle names a KB by uuid, falling back to the
5
+ // flow-global KB. See ADR 0023.
6
+ const kbRef = inputs.knowledge_base;
7
+ const knowledgeBase =
8
+ (kbRef && kbRef.uuid
9
+ ? config.integrations?.[`knowledgeBase:${kbRef.uuid}`]
10
+ : null) ||
11
+ config.integrations?.knowledgeBase ||
12
+ config.integrations?.sqlite;
13
+
14
+ if (!knowledgeBase) {
15
+ throw new Error("Knowledge base integration not found. Make sure a knowledge database is available.");
16
+ }
17
+
18
+ const { limit = 100, offset = 0 } = inputs;
19
+ const documents = await knowledgeBase.listDocuments({ limit, offset });
20
+
21
+ return { documents, count: documents.length, success: true, error: null };
22
+ } catch (error) {
23
+ return { documents: [], count: 0, success: false, error: error.message };
24
+ }
25
+ };
@@ -0,0 +1,74 @@
1
+ {
2
+ "display_name": "List Entities",
3
+ "tagline": "Browse a knowledge graph",
4
+ "description": "List the entities in a Graph knowledge base, highest-degree (most-connected) first — for browsing the graph or letting an agent discover what it can traverse. Optionally filter by entity type or a name substring. Wire a Knowledge Base node into knowledge_base, same as the search nodes.",
5
+ "category": "knowledge",
6
+ "is_constant": false,
7
+ "is_plugin": true,
8
+ "is_output": false,
9
+ "inputs": [
10
+ {
11
+ "name": "knowledge_base",
12
+ "display_name": "Knowledge Base",
13
+ "type": "knowledge_base",
14
+ "description": "The knowledge base to list, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
15
+ "required": false,
16
+ "default": null
17
+ },
18
+ {
19
+ "name": "type",
20
+ "display_name": "Entity Type",
21
+ "type": "string",
22
+ "description": "Optional: only entities of this type (e.g. person, organization). Case-insensitive.",
23
+ "default": null
24
+ },
25
+ {
26
+ "name": "search",
27
+ "display_name": "Name Search",
28
+ "type": "string",
29
+ "description": "Optional: only entities whose name contains this text (case-insensitive).",
30
+ "default": null
31
+ },
32
+ {
33
+ "name": "limit",
34
+ "display_name": "Limit",
35
+ "type": "number",
36
+ "description": "Maximum number of entities to return",
37
+ "default": 100
38
+ },
39
+ {
40
+ "name": "offset",
41
+ "display_name": "Offset",
42
+ "type": "number",
43
+ "description": "Number of entities to skip (pagination)",
44
+ "default": 0
45
+ }
46
+ ],
47
+ "outputs": [
48
+ {
49
+ "name": "entities",
50
+ "display_name": "Entities",
51
+ "type": "array",
52
+ "description": "Entity rows (id, name, type, description, degree, …), most-connected first"
53
+ },
54
+ {
55
+ "name": "count",
56
+ "display_name": "Count",
57
+ "type": "number",
58
+ "description": "Number of entities returned"
59
+ },
60
+ {
61
+ "name": "success",
62
+ "display_name": "Success",
63
+ "type": "boolean",
64
+ "description": "Whether the listing completed successfully"
65
+ },
66
+ {
67
+ "name": "error",
68
+ "display_name": "Error",
69
+ "type": "string",
70
+ "description": "Error message if it failed"
71
+ }
72
+ ],
73
+ "settings": []
74
+ }
@@ -0,0 +1,25 @@
1
+ export default async ({ inputs, config }) => {
2
+ try {
3
+ // Shared KB wiring (see semantic-search / keyword-search): a
4
+ // knowledge_base handle names a KB by uuid, falling back to the
5
+ // flow-global KB. See ADR 0023.
6
+ const kbRef = inputs.knowledge_base;
7
+ const knowledgeBase =
8
+ (kbRef && kbRef.uuid
9
+ ? config.integrations?.[`knowledgeBase:${kbRef.uuid}`]
10
+ : null) ||
11
+ config.integrations?.knowledgeBase ||
12
+ config.integrations?.sqlite;
13
+
14
+ if (!knowledgeBase) {
15
+ throw new Error("Knowledge base integration not found. Make sure a knowledge database is available.");
16
+ }
17
+
18
+ const { limit = 100, offset = 0, type = null, search = null } = inputs;
19
+ const entities = await knowledgeBase.listEntities({ limit, offset, type, search });
20
+
21
+ return { entities, count: entities.length, success: true, error: null };
22
+ } catch (error) {
23
+ return { entities: [], count: 0, success: false, error: error.message };
24
+ }
25
+ };
@@ -14,6 +14,14 @@
14
14
  "description": "SQL query to execute against the knowledge database",
15
15
  "required": true
16
16
  },
17
+ {
18
+ "name": "knowledge_base",
19
+ "display_name": "Knowledge Base",
20
+ "type": "knowledge_base",
21
+ "description": "The knowledge base to query, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
22
+ "required": false,
23
+ "default": null
24
+ },
17
25
  {
18
26
  "name": "params",
19
27
  "display_name": "Parameters",
@@ -49,6 +57,12 @@
49
57
  "type": "number",
50
58
  "description": "Number of rows affected or returned"
51
59
  },
60
+ {
61
+ "name": "truncated",
62
+ "display_name": "Truncated",
63
+ "type": "boolean",
64
+ "description": "True when the returned data was capped (>1000 rows) — add a LIMIT to the query for the full set"
65
+ },
52
66
  {
53
67
  "name": "operation",
54
68
  "display_name": "Operation",
@@ -63,13 +77,6 @@
63
77
  }
64
78
  ],
65
79
  "settings": [
66
- {
67
- "name": "database_path",
68
- "display_name": "Database Path",
69
- "type": "string",
70
- "description": "Path to the knowledge database file (relative to orchestration)",
71
- "default": "knowledge.db"
72
- },
73
80
  {
74
81
  "name": "timeout",
75
82
  "display_name": "Query Timeout",
@@ -1,12 +1,20 @@
1
- export default async ({inputs, settings, config, nodeConfig}) => {
2
- // Get knowledge base integration from engine (supports multiple backends)
3
- const knowledgeBase = config.integrations?.knowledgeBase || config.integrations?.sqlite;
1
+ export default async ({inputs, config}) => {
2
+ // Resolve which knowledge base to query — SAME wiring as semantic-search
3
+ // and keyword-search: a `knowledge_base` handle wired in from a Knowledge
4
+ // Base node names a specific KB by uuid (resolved to a per-KB integration
5
+ // keyed knowledgeBase:<uuid>), falling back to the flow-global KB. See ADR 0023.
6
+ const kbRef = inputs.knowledge_base;
7
+ const knowledgeBase =
8
+ (kbRef && kbRef.uuid
9
+ ? config.integrations?.[`knowledgeBase:${kbRef.uuid}`]
10
+ : null) ||
11
+ config.integrations?.knowledgeBase ||
12
+ config.integrations?.sqlite;
4
13
  if (!knowledgeBase) {
5
14
  throw new Error("Knowledge base integration not found. Make sure a knowledge database is available.");
6
15
  }
7
16
 
8
17
  const { query, params = [], operation = 'SELECT' } = inputs;
9
- const { database_path = 'knowledge.db', timeout = 5000 } = settings;
10
18
 
11
19
  if (!query || typeof query !== 'string') {
12
20
  throw new Error("Query is required and must be a string");
@@ -25,11 +33,23 @@ export default async ({inputs, settings, config, nodeConfig}) => {
25
33
  // Execute the query
26
34
  const result = await knowledgeBase.query(query, params, operation);
27
35
 
36
+ // Cap the rows returned into the flow so a broad SELECT can't bloat the run
37
+ // payload. rowCount stays the true count; `truncated` flags the cut (never
38
+ // silently). Bump the SQL's own LIMIT for fewer/more rows.
39
+ const MAX_ROWS = 1000;
40
+ let data = result.data;
41
+ let truncated = false;
42
+ if (Array.isArray(data) && data.length > MAX_ROWS) {
43
+ data = data.slice(0, MAX_ROWS);
44
+ truncated = true;
45
+ }
46
+
28
47
  return {
29
- data: result.data,
48
+ data,
30
49
  success: result.success,
31
50
  rowCount: result.rowCount,
32
51
  operation: result.operation,
52
+ truncated,
33
53
  error: null
34
54
  };
35
55
  };
@@ -0,0 +1,79 @@
1
+ {
2
+ "display_name": "Read Chunks",
3
+ "tagline": "Paginate through a document",
4
+ "description": "Read a document's chunks in order starting at a chunk index — paginate through a document, or read what came AFTER a chunk you found via search (set start_index to that chunk's index + 1). Feed next_index back in to page forward.",
5
+ "category": "knowledge",
6
+ "is_constant": false,
7
+ "is_plugin": true,
8
+ "is_output": false,
9
+ "inputs": [
10
+ {
11
+ "name": "document_id",
12
+ "display_name": "Document ID",
13
+ "type": "string",
14
+ "description": "The document to read chunks from",
15
+ "required": true
16
+ },
17
+ {
18
+ "name": "knowledge_base",
19
+ "display_name": "Knowledge Base",
20
+ "type": "knowledge_base",
21
+ "description": "The knowledge base, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
22
+ "required": false,
23
+ "default": null
24
+ },
25
+ {
26
+ "name": "start_index",
27
+ "display_name": "Start Index",
28
+ "type": "number",
29
+ "description": "First chunk index to read (0-based). To continue after a found chunk, use its index + 1.",
30
+ "default": 0
31
+ },
32
+ {
33
+ "name": "limit",
34
+ "display_name": "Limit",
35
+ "type": "number",
36
+ "description": "Maximum number of chunks to return",
37
+ "default": 10
38
+ }
39
+ ],
40
+ "outputs": [
41
+ {
42
+ "name": "chunks",
43
+ "display_name": "Chunks",
44
+ "type": "array",
45
+ "description": "Chunks with index >= start_index, ascending"
46
+ },
47
+ {
48
+ "name": "count",
49
+ "display_name": "Count",
50
+ "type": "number",
51
+ "description": "Number of chunks returned"
52
+ },
53
+ {
54
+ "name": "next_index",
55
+ "display_name": "Next Index",
56
+ "type": "number",
57
+ "description": "start_index for the next page, or null if there are no more chunks"
58
+ },
59
+ {
60
+ "name": "has_more",
61
+ "display_name": "Has More",
62
+ "type": "boolean",
63
+ "description": "Whether more chunks remain after this page"
64
+ },
65
+ {
66
+ "name": "success",
67
+ "display_name": "Success",
68
+ "type": "boolean",
69
+ "description": "Whether the read completed successfully"
70
+ },
71
+ {
72
+ "name": "error",
73
+ "display_name": "Error",
74
+ "type": "string",
75
+ "description": "Error message if it failed"
76
+ }
77
+ ],
78
+ "settings": []
79
+ }
@@ -0,0 +1,49 @@
1
+ export default async ({ inputs, config }) => {
2
+ try {
3
+ // Shared KB wiring (see semantic-search / keyword-search). ADR 0023.
4
+ const kbRef = inputs.knowledge_base;
5
+ const knowledgeBase =
6
+ (kbRef && kbRef.uuid
7
+ ? config.integrations?.[`knowledgeBase:${kbRef.uuid}`]
8
+ : null) ||
9
+ config.integrations?.knowledgeBase ||
10
+ config.integrations?.sqlite;
11
+
12
+ if (!knowledgeBase) {
13
+ throw new Error("Knowledge base integration not found. Make sure a knowledge database is available.");
14
+ }
15
+
16
+ const { document_id, start_index = 0, limit = 10 } = inputs;
17
+ if (!document_id || typeof document_id !== 'string') {
18
+ throw new Error("document_id is required and must be a string");
19
+ }
20
+
21
+ // Fetch one extra to detect whether more chunks remain after this page.
22
+ const fetched = await knowledgeBase.getChunks(document_id, {
23
+ startIndex: start_index,
24
+ limit: limit + 1,
25
+ });
26
+ const hasMore = fetched.length > limit;
27
+ const chunks = hasMore ? fetched.slice(0, limit) : fetched;
28
+ const last = chunks[chunks.length - 1];
29
+ const nextIndex = hasMore && last ? last.chunk_index + 1 : null;
30
+
31
+ return {
32
+ chunks,
33
+ count: chunks.length,
34
+ next_index: nextIndex,
35
+ has_more: hasMore,
36
+ success: true,
37
+ error: null,
38
+ };
39
+ } catch (error) {
40
+ return {
41
+ chunks: [],
42
+ count: 0,
43
+ next_index: null,
44
+ has_more: false,
45
+ success: false,
46
+ error: error.message,
47
+ };
48
+ }
49
+ };
@@ -5,6 +5,7 @@
5
5
  "icon": "fa-plug",
6
6
  "category": "tools",
7
7
  "is_constant": true,
8
+ "is_plugin": true,
8
9
  "inputs": [],
9
10
  "outputs": [],
10
11
  "settings": [
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "display_name": "Search the Internet",
3
3
  "tagline": "Search the internet",
4
- "description": "Search the web using Google Custom Search - returns clean search results with titles, links, and snippets",
4
+ "description": "Search the web using Firecrawl Search - returns clean search results with titles, links, and snippets",
5
5
  "category": "third-party",
6
- "provider": "google",
7
- "needs_key_from": ["google_custom_search"],
6
+ "provider": "firecrawl",
7
+ "needs_key_from": ["firecrawl"],
8
8
  "is_macro": true,
9
9
  "is_plugin": true,
10
10
  "macro_flow": {
@@ -18,13 +18,13 @@
18
18
  },
19
19
  {
20
20
  "id": "search",
21
- "type": "google-custom-search"
21
+ "type": "firecrawl-search"
22
22
  },
23
23
  {
24
24
  "id": "num_results",
25
- "type": "string",
25
+ "type": "number",
26
26
  "settings": {
27
- "value": "10"
27
+ "value": 5
28
28
  }
29
29
  },
30
30
  {
@@ -42,7 +42,7 @@
42
42
  },
43
43
  {
44
44
  "from": { "node_id": "num_results", "port_name": "value" },
45
- "to": { "node_id": "search", "port_name": "num" }
45
+ "to": { "node_id": "search", "port_name": "limit" }
46
46
  },
47
47
  {
48
48
  "from": { "node_id": "search", "port_name": "items" },
@@ -6,7 +6,7 @@
6
6
  "is_constant": false,
7
7
  "is_plugin": true,
8
8
  "is_output": false,
9
- "needs_key_from": ["openai"],
9
+ "needs_key_from": ["openrouter"],
10
10
  "inputs": [
11
11
  {
12
12
  "name": "query",
@@ -15,6 +15,14 @@
15
15
  "description": "Text query to search for semantically similar content",
16
16
  "required": true
17
17
  },
18
+ {
19
+ "name": "knowledge_base",
20
+ "display_name": "Knowledge Base",
21
+ "type": "knowledge_base",
22
+ "description": "The knowledge base to search, from a Knowledge Base node. If omitted, falls back to the flow's default knowledge base.",
23
+ "required": false,
24
+ "default": null
25
+ },
18
26
  {
19
27
  "name": "limit",
20
28
  "display_name": "Result Limit",