mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
fix(docs): resolve missing tool outputs for spread-inherited V2 tools (#4020)
* fix(docs): resolve missing tool outputs for spread-inherited V2 tools * fix(docs): add word boundary to baseToolRegex to prevent false matches * fix(docs): remove unnecessary case-insensitive flag from baseToolRegex
This commit is contained in:
@@ -34,6 +34,13 @@ Integrate Extend AI into the workflow. Parse and extract structured content from
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Unique identifier for the parser run |
|
||||
| `status` | string | Processing status |
|
||||
| `chunks` | json | Parsed document content chunks |
|
||||
| `blocks` | json | Block-level document elements with type and content |
|
||||
| `pageCount` | number | Number of pages processed |
|
||||
| `creditsUsed` | number | API credits consumed |
|
||||
|
||||
|
||||
|
||||
@@ -54,8 +54,27 @@ Integrate Mistral Parse into the workflow. Can extract text from uploaded PDF do
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pages` | array | Array of page objects from Mistral OCR |
|
||||
| `model` | string | Mistral OCR model identifier |
|
||||
| `usage_info` | json | Usage statistics from the API |
|
||||
| `document_annotation` | string | Structured annotation data |
|
||||
| ↳ `index` | number | Page index \(zero-based\) |
|
||||
| ↳ `markdown` | string | Extracted markdown content |
|
||||
| ↳ `images` | array | Images extracted from this page with bounding boxes |
|
||||
| ↳ `id` | string | Image identifier \(e.g., img-0.jpeg\) |
|
||||
| ↳ `top_left_x` | number | Top-left X coordinate in pixels |
|
||||
| ↳ `top_left_y` | number | Top-left Y coordinate in pixels |
|
||||
| ↳ `bottom_right_x` | number | Bottom-right X coordinate in pixels |
|
||||
| ↳ `bottom_right_y` | number | Bottom-right Y coordinate in pixels |
|
||||
| ↳ `image_base64` | string | Base64-encoded image data \(when include_image_base64=true\) |
|
||||
| ↳ `dimensions` | object | Page dimensions |
|
||||
| ↳ `dpi` | number | Dots per inch |
|
||||
| ↳ `height` | number | Page height in pixels |
|
||||
| ↳ `width` | number | Page width in pixels |
|
||||
| ↳ `tables` | array | Extracted tables as HTML/markdown \(when table_format is set\). Referenced via placeholders like \[tbl-0.html\] |
|
||||
| ↳ `hyperlinks` | array | Array of URL strings detected in the page \(e.g., \["https://...", "mailto:..."\]\) |
|
||||
| ↳ `header` | string | Page header content \(when extract_header=true\) |
|
||||
| ↳ `footer` | string | Page footer content \(when extract_footer=true\) |
|
||||
| `model` | string | Mistral OCR model identifier \(e.g., mistral-ocr-latest\) |
|
||||
| `usage_info` | object | Usage and processing statistics |
|
||||
| ↳ `pages_processed` | number | Total number of pages processed |
|
||||
| ↳ `doc_size_bytes` | number | Document file size in bytes |
|
||||
| `document_annotation` | string | Structured annotation data as JSON string \(when applicable\) |
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,16 @@ Integrate Pulse into the workflow. Extract text from PDF documents, images, and
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `markdown` | string | Extracted content in markdown format |
|
||||
| `page_count` | number | Number of pages in the document |
|
||||
| `job_id` | string | Unique job identifier |
|
||||
| `bounding_boxes` | json | Bounding box layout information |
|
||||
| `extraction_url` | string | URL for extraction results \(for large documents\) |
|
||||
| `html` | string | HTML content if requested |
|
||||
| `structured_output` | json | Structured output if schema was provided |
|
||||
| `chunks` | json | Chunked content if chunking was enabled |
|
||||
| `figures` | json | Extracted figures if figure extraction was enabled |
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,13 @@ Integrate Reducto Parse into the workflow. Can extract text from uploaded PDF do
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `job_id` | string | Unique identifier for the processing job |
|
||||
| `duration` | number | Processing time in seconds |
|
||||
| `usage` | json | Resource consumption data |
|
||||
| `result` | json | Parsed document content with chunks and blocks |
|
||||
| `pdf_url` | string | Storage URL of converted PDF |
|
||||
| `studio_link` | string | Link to Reducto studio interface |
|
||||
|
||||
|
||||
|
||||
@@ -69,7 +69,17 @@ Transcribe audio and video files to text using leading AI providers. Supports mu
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `transcript` | string | Full transcribed text |
|
||||
| `segments` | array | Timestamped segments |
|
||||
| ↳ `text` | string | Transcribed text for this segment |
|
||||
| ↳ `start` | number | Start time in seconds |
|
||||
| ↳ `end` | number | End time in seconds |
|
||||
| ↳ `speaker` | string | Speaker identifier \(if diarization enabled\) |
|
||||
| ↳ `confidence` | number | Confidence score \(0-1\) |
|
||||
| `language` | string | Detected or specified language |
|
||||
| `duration` | number | Audio duration in seconds |
|
||||
|
||||
### `stt_deepgram`
|
||||
|
||||
@@ -89,7 +99,18 @@ This tool does not produce any outputs.
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `transcript` | string | Full transcribed text |
|
||||
| `segments` | array | Timestamped segments with speaker labels |
|
||||
| ↳ `text` | string | Transcribed text for this segment |
|
||||
| ↳ `start` | number | Start time in seconds |
|
||||
| ↳ `end` | number | End time in seconds |
|
||||
| ↳ `speaker` | string | Speaker identifier \(if diarization enabled\) |
|
||||
| ↳ `confidence` | number | Confidence score \(0-1\) |
|
||||
| `language` | string | Detected or specified language |
|
||||
| `duration` | number | Audio duration in seconds |
|
||||
| `confidence` | number | Overall confidence score |
|
||||
|
||||
### `stt_elevenlabs`
|
||||
|
||||
@@ -108,7 +129,13 @@ This tool does not produce any outputs.
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `transcript` | string | Full transcribed text |
|
||||
| `segments` | array | Timestamped segments |
|
||||
| `language` | string | Detected or specified language |
|
||||
| `duration` | number | Audio duration in seconds |
|
||||
| `confidence` | number | Overall confidence score |
|
||||
|
||||
### `stt_assemblyai`
|
||||
|
||||
@@ -132,7 +159,30 @@ This tool does not produce any outputs.
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `transcript` | string | Full transcribed text |
|
||||
| `segments` | array | Timestamped segments with speaker labels |
|
||||
| ↳ `text` | string | Transcribed text for this segment |
|
||||
| ↳ `start` | number | Start time in seconds |
|
||||
| ↳ `end` | number | End time in seconds |
|
||||
| ↳ `speaker` | string | Speaker identifier \(if diarization enabled\) |
|
||||
| ↳ `confidence` | number | Confidence score \(0-1\) |
|
||||
| `language` | string | Detected or specified language |
|
||||
| `duration` | number | Audio duration in seconds |
|
||||
| `confidence` | number | Overall confidence score |
|
||||
| `sentiment` | array | Sentiment analysis results |
|
||||
| ↳ `text` | string | Text that was analyzed |
|
||||
| ↳ `sentiment` | string | Sentiment \(POSITIVE, NEGATIVE, NEUTRAL\) |
|
||||
| ↳ `confidence` | number | Confidence score |
|
||||
| ↳ `start` | number | Start time in milliseconds |
|
||||
| ↳ `end` | number | End time in milliseconds |
|
||||
| `entities` | array | Detected entities |
|
||||
| ↳ `entity_type` | string | Entity type \(e.g., person_name, location, organization\) |
|
||||
| ↳ `text` | string | Entity text |
|
||||
| ↳ `start` | number | Start time in milliseconds |
|
||||
| ↳ `end` | number | End time in milliseconds |
|
||||
| `summary` | string | Auto-generated summary |
|
||||
|
||||
### `stt_gemini`
|
||||
|
||||
@@ -151,6 +201,12 @@ This tool does not produce any outputs.
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `transcript` | string | Full transcribed text |
|
||||
| `segments` | array | Timestamped segments |
|
||||
| `language` | string | Detected or specified language |
|
||||
| `duration` | number | Audio duration in seconds |
|
||||
| `confidence` | number | Overall confidence score |
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,39 @@ Integrate AWS Textract into your workflow to extract text, tables, forms, and ke
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `blocks` | array | Array of Block objects containing detected text, tables, forms, and other elements |
|
||||
| ↳ `BlockType` | string | Type of block \(PAGE, LINE, WORD, TABLE, CELL, KEY_VALUE_SET, etc.\) |
|
||||
| ↳ `Id` | string | Unique identifier for the block |
|
||||
| ↳ `Text` | string | The text content \(for LINE and WORD blocks\) |
|
||||
| ↳ `TextType` | string | Type of text \(PRINTED or HANDWRITING\) |
|
||||
| ↳ `Confidence` | number | Confidence score \(0-100\) |
|
||||
| ↳ `Page` | number | Page number |
|
||||
| ↳ `Geometry` | object | Location and bounding box information |
|
||||
| ↳ `BoundingBox` | object | Height as ratio of document height |
|
||||
| ↳ `Height` | number | Height as ratio of document height |
|
||||
| ↳ `Left` | number | Left position as ratio of document width |
|
||||
| ↳ `Top` | number | Top position as ratio of document height |
|
||||
| ↳ `Width` | number | Width as ratio of document width |
|
||||
| ↳ `Polygon` | array | Polygon coordinates |
|
||||
| ↳ `X` | number | X coordinate |
|
||||
| ↳ `Y` | number | Y coordinate |
|
||||
| ↳ `Relationships` | array | Relationships to other blocks |
|
||||
| ↳ `Type` | string | Relationship type \(CHILD, VALUE, ANSWER, etc.\) |
|
||||
| ↳ `Ids` | array | IDs of related blocks |
|
||||
| ↳ `EntityTypes` | array | Entity types for KEY_VALUE_SET \(KEY or VALUE\) |
|
||||
| ↳ `SelectionStatus` | string | For checkboxes: SELECTED or NOT_SELECTED |
|
||||
| ↳ `RowIndex` | number | Row index for table cells |
|
||||
| ↳ `ColumnIndex` | number | Column index for table cells |
|
||||
| ↳ `RowSpan` | number | Row span for merged cells |
|
||||
| ↳ `ColumnSpan` | number | Column span for merged cells |
|
||||
| ↳ `Query` | object | Query information for QUERY blocks |
|
||||
| ↳ `Text` | string | Query text |
|
||||
| ↳ `Alias` | string | Query alias |
|
||||
| ↳ `Pages` | array | Pages to search |
|
||||
| `documentMetadata` | object | Metadata about the analyzed document |
|
||||
| ↳ `pages` | number | Number of pages in the document |
|
||||
| `modelVersion` | string | Version of the Textract model used for processing |
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,14 @@ Integrate Vision into the workflow. Can analyze images with vision models.
|
||||
|
||||
#### Output
|
||||
|
||||
This tool does not produce any outputs.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | The analyzed content and description of the image |
|
||||
| `model` | string | The vision model that was used for analysis |
|
||||
| `tokens` | number | Total tokens used for the analysis |
|
||||
| `usage` | object | Detailed token usage breakdown |
|
||||
| ↳ `input_tokens` | number | Tokens used for input processing |
|
||||
| ↳ `output_tokens` | number | Tokens used for response generation |
|
||||
| ↳ `total_tokens` | number | Total tokens consumed |
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,27 @@ interface BlockConfig {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the position after the matching close delimiter for an opening delimiter.
|
||||
* Assumes `content[openPos]` is the opening char (e.g. `{` or `[`).
|
||||
* Returns the index one past the matching close char, or -1 if unbalanced.
|
||||
*/
|
||||
function findMatchingClose(
|
||||
content: string,
|
||||
openPos: number,
|
||||
openChar = '{',
|
||||
closeChar = '}'
|
||||
): number {
|
||||
let count = 1
|
||||
let pos = openPos + 1
|
||||
while (pos < content.length && count > 0) {
|
||||
if (content[pos] === openChar) count++
|
||||
else if (content[pos] === closeChar) count--
|
||||
pos++
|
||||
}
|
||||
return count === 0 ? pos : -1
|
||||
}
|
||||
|
||||
interface TriggerInfo {
|
||||
id: string
|
||||
name: string
|
||||
@@ -134,16 +155,9 @@ async function generateIconMapping(): Promise<Record<string, string>> {
|
||||
const startIndex = match.index + match[0].length - 1
|
||||
|
||||
// Extract the block content
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(fileContent, startIndex)
|
||||
|
||||
while (endIndex < fileContent.length && braceCount > 0) {
|
||||
if (fileContent[endIndex] === '{') braceCount++
|
||||
else if (fileContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endIndex !== -1) {
|
||||
const blockContent = fileContent.substring(startIndex, endIndex)
|
||||
|
||||
// Check hideFromToolbar - skip hidden blocks for docs but NOT for icon mapping
|
||||
@@ -272,26 +286,16 @@ function extractOperationsFromContent(blockContent: string): { label: string; id
|
||||
|
||||
// Locate the opening '[' of the subBlocks array
|
||||
const arrayStart = subBlocksMatch.index + subBlocksMatch[0].length - 1
|
||||
let bracketCount = 1
|
||||
let pos = arrayStart + 1
|
||||
while (pos < blockContent.length && bracketCount > 0) {
|
||||
if (blockContent[pos] === '[') bracketCount++
|
||||
else if (blockContent[pos] === ']') bracketCount--
|
||||
pos++
|
||||
}
|
||||
const subBlocksContent = blockContent.substring(arrayStart + 1, pos - 1)
|
||||
const arrayEnd = findMatchingClose(blockContent, arrayStart, '[', ']')
|
||||
if (arrayEnd === -1) return []
|
||||
const subBlocksContent = blockContent.substring(arrayStart + 1, arrayEnd - 1)
|
||||
|
||||
// Iterate over top-level objects in the subBlocks array, looking for id: 'operation'
|
||||
let i = 0
|
||||
while (i < subBlocksContent.length) {
|
||||
if (subBlocksContent[i] === '{') {
|
||||
let braceCount = 1
|
||||
let j = i + 1
|
||||
while (j < subBlocksContent.length && braceCount > 0) {
|
||||
if (subBlocksContent[j] === '{') braceCount++
|
||||
else if (subBlocksContent[j] === '}') braceCount--
|
||||
j++
|
||||
}
|
||||
const j = findMatchingClose(subBlocksContent, i)
|
||||
if (j === -1) break
|
||||
const objContent = subBlocksContent.substring(i, j)
|
||||
|
||||
if (/\bid\s*:\s*['"]operation['"]/.test(objContent)) {
|
||||
@@ -299,14 +303,9 @@ function extractOperationsFromContent(blockContent: string): { label: string; id
|
||||
if (!optionsMatch) return []
|
||||
|
||||
const optArrayStart = optionsMatch.index + optionsMatch[0].length - 1
|
||||
let bc = 1
|
||||
let op = optArrayStart + 1
|
||||
while (op < objContent.length && bc > 0) {
|
||||
if (objContent[op] === '[') bc++
|
||||
else if (objContent[op] === ']') bc--
|
||||
op++
|
||||
}
|
||||
const optionsContent = objContent.substring(optArrayStart + 1, op - 1)
|
||||
const optArrayEnd = findMatchingClose(objContent, optArrayStart, '[', ']')
|
||||
if (optArrayEnd === -1) return []
|
||||
const optionsContent = objContent.substring(optArrayStart + 1, optArrayEnd - 1)
|
||||
|
||||
// Extract { label, id } pairs from each option object
|
||||
const pairs: { label: string; id: string }[] = []
|
||||
@@ -442,14 +441,9 @@ function extractTriggersAvailable(blockContent: string): string[] {
|
||||
if (!triggersMatch) return []
|
||||
|
||||
const start = triggersMatch.index + triggersMatch[0].length - 1
|
||||
let braceCount = 1
|
||||
let pos = start + 1
|
||||
while (pos < blockContent.length && braceCount > 0) {
|
||||
if (blockContent[pos] === '{') braceCount++
|
||||
else if (blockContent[pos] === '}') braceCount--
|
||||
pos++
|
||||
}
|
||||
const triggersContent = blockContent.substring(start, pos)
|
||||
const trigEnd = findMatchingClose(blockContent, start)
|
||||
if (trigEnd === -1) return []
|
||||
const triggersContent = blockContent.substring(start, trigEnd)
|
||||
|
||||
if (!/enabled\s*:\s*true/.test(triggersContent)) return []
|
||||
|
||||
@@ -457,14 +451,9 @@ function extractTriggersAvailable(blockContent: string): string[] {
|
||||
if (!availableMatch) return []
|
||||
|
||||
const arrayStart = availableMatch.index + availableMatch[0].length - 1
|
||||
let bracketCount = 1
|
||||
let ap = arrayStart + 1
|
||||
while (ap < triggersContent.length && bracketCount > 0) {
|
||||
if (triggersContent[ap] === '[') bracketCount++
|
||||
else if (triggersContent[ap] === ']') bracketCount--
|
||||
ap++
|
||||
}
|
||||
const arrayContent = triggersContent.substring(arrayStart + 1, ap - 1)
|
||||
const arrayEnd = findMatchingClose(triggersContent, arrayStart, '[', ']')
|
||||
if (arrayEnd === -1) return []
|
||||
const arrayContent = triggersContent.substring(arrayStart + 1, arrayEnd - 1)
|
||||
|
||||
const ids: string[] = []
|
||||
const idRegex = /['"]([^'"]+)['"]/g
|
||||
@@ -731,16 +720,9 @@ function extractAllBlockConfigs(fileContent: string): BlockConfig[] {
|
||||
const startIndex = match.index + match[0].length - 1 // Position of opening brace
|
||||
|
||||
// Extract the block content by matching braces
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(fileContent, startIndex)
|
||||
|
||||
while (endIndex < fileContent.length && braceCount > 0) {
|
||||
if (fileContent[endIndex] === '{') braceCount++
|
||||
else if (fileContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endIndex !== -1) {
|
||||
const blockContent = fileContent.substring(startIndex, endIndex)
|
||||
|
||||
// Check if this block has hideFromToolbar: true
|
||||
@@ -798,16 +780,9 @@ function extractBlockConfigFromContent(
|
||||
|
||||
if (baseMatch) {
|
||||
const startIndex = baseMatch.index + baseMatch[0].length - 1
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(fileContent, startIndex)
|
||||
|
||||
while (endIndex < fileContent.length && braceCount > 0) {
|
||||
if (fileContent[endIndex] === '{') braceCount++
|
||||
else if (fileContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endIndex !== -1) {
|
||||
const baseBlockContent = fileContent.substring(startIndex, endIndex)
|
||||
// Recursively extract base config (but don't pass fileContent to avoid infinite loops)
|
||||
baseConfig = extractBlockConfigFromContent(
|
||||
@@ -1015,62 +990,42 @@ function extractOutputsFromContent(content: string): Record<string, any> {
|
||||
const openBracePos = content.indexOf('{', outputsStart)
|
||||
if (openBracePos === -1) return {}
|
||||
|
||||
let braceCount = 1
|
||||
let pos = openBracePos + 1
|
||||
const pos = findMatchingClose(content, openBracePos)
|
||||
if (pos === -1) return {}
|
||||
|
||||
while (pos < content.length && braceCount > 0) {
|
||||
if (content[pos] === '{') braceCount++
|
||||
else if (content[pos] === '}') braceCount--
|
||||
pos++
|
||||
const outputsContent = content.substring(openBracePos + 1, pos - 1).trim()
|
||||
const outputs: Record<string, any> = {}
|
||||
|
||||
const fieldRegex = /(\w+)\s*:\s*{/g
|
||||
let match
|
||||
const fieldPositions: Array<{ name: string; start: number }> = []
|
||||
|
||||
while ((match = fieldRegex.exec(outputsContent)) !== null) {
|
||||
fieldPositions.push({
|
||||
name: match[1],
|
||||
start: match.index + match[0].length - 1,
|
||||
})
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
const outputsContent = content.substring(openBracePos + 1, pos - 1).trim()
|
||||
const outputs: Record<string, any> = {}
|
||||
fieldPositions.forEach((field) => {
|
||||
const endPos = findMatchingClose(outputsContent, field.start)
|
||||
|
||||
const fieldRegex = /(\w+)\s*:\s*{/g
|
||||
let match
|
||||
const fieldPositions: Array<{ name: string; start: number }> = []
|
||||
if (endPos !== -1) {
|
||||
const fieldContent = outputsContent.substring(field.start + 1, endPos - 1).trim()
|
||||
|
||||
while ((match = fieldRegex.exec(outputsContent)) !== null) {
|
||||
fieldPositions.push({
|
||||
name: match[1],
|
||||
start: match.index + match[0].length - 1,
|
||||
})
|
||||
}
|
||||
const typeMatch = fieldContent.match(/type\s*:\s*['"](.*?)['"]/)
|
||||
const description = extractDescription(fieldContent)
|
||||
|
||||
fieldPositions.forEach((field) => {
|
||||
const startPos = field.start
|
||||
let braceCount = 1
|
||||
let endPos = startPos + 1
|
||||
|
||||
while (endPos < outputsContent.length && braceCount > 0) {
|
||||
if (outputsContent[endPos] === '{') braceCount++
|
||||
else if (outputsContent[endPos] === '}') braceCount--
|
||||
endPos++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
const fieldContent = outputsContent.substring(startPos + 1, endPos - 1).trim()
|
||||
|
||||
const typeMatch = fieldContent.match(/type\s*:\s*['"](.*?)['"]/)
|
||||
const description = extractDescription(fieldContent)
|
||||
|
||||
if (typeMatch) {
|
||||
outputs[field.name] = {
|
||||
type: typeMatch[1],
|
||||
description: description || `${field.name} output from the block`,
|
||||
}
|
||||
if (typeMatch) {
|
||||
outputs[field.name] = {
|
||||
type: typeMatch[1],
|
||||
description: description || `${field.name} output from the block`,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (Object.keys(outputs).length > 0) {
|
||||
return outputs
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {}
|
||||
return outputs
|
||||
}
|
||||
|
||||
function extractToolsAccessFromContent(content: string): string[] {
|
||||
@@ -1148,16 +1103,9 @@ function resolveConstReference(
|
||||
|
||||
// Extract the const content
|
||||
const startIndex = constMatch.index + constMatch[0].length - 1
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(typesContent, startIndex)
|
||||
|
||||
while (endIndex < typesContent.length && braceCount > 0) {
|
||||
if (typesContent[endIndex] === '{') braceCount++
|
||||
else if (typesContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount !== 0) {
|
||||
if (endIndex === -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1242,14 +1190,8 @@ function parseConstProperties(
|
||||
if ((propName === 'properties' || propName === 'type') && !constRef) {
|
||||
// Peek at what's inside the braces
|
||||
const startPos = match.index + match[0].length - 1
|
||||
let braceCount = 1
|
||||
let endPos = startPos + 1
|
||||
while (endPos < content.length && braceCount > 0) {
|
||||
if (content[endPos] === '{') braceCount++
|
||||
else if (content[endPos] === '}') braceCount--
|
||||
endPos++
|
||||
}
|
||||
if (braceCount === 0) {
|
||||
const endPos = findMatchingClose(content, startPos)
|
||||
if (endPos !== -1) {
|
||||
const propContent = content.substring(startPos + 1, endPos - 1).trim()
|
||||
// If it starts with 'type:', it's an output field definition - process it
|
||||
if (propContent.match(/^\s*type\s*:/)) {
|
||||
@@ -1272,17 +1214,9 @@ function parseConstProperties(
|
||||
} else {
|
||||
// This property has inline definition
|
||||
const startPos = match.index + match[0].length - 1
|
||||
const endPos = findMatchingClose(content, startPos)
|
||||
|
||||
let braceCount = 1
|
||||
let endPos = startPos + 1
|
||||
|
||||
while (endPos < content.length && braceCount > 0) {
|
||||
if (content[endPos] === '{') braceCount++
|
||||
else if (content[endPos] === '}') braceCount--
|
||||
endPos++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endPos !== -1) {
|
||||
const propContent = content.substring(startPos + 1, endPos - 1).trim()
|
||||
const parsedProp = parseConstFieldContent(propContent, toolPrefix, typesContent, depth)
|
||||
if (parsedProp) {
|
||||
@@ -1324,16 +1258,9 @@ function resolveConstFromTypesContent(
|
||||
}
|
||||
|
||||
const startIndex = constMatch.index + constMatch[0].length - 1
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(typesContent, startIndex)
|
||||
|
||||
while (endIndex < typesContent.length && braceCount > 0) {
|
||||
if (typesContent[endIndex] === '{') braceCount++
|
||||
else if (typesContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount !== 0) return null
|
||||
if (endIndex === -1) return null
|
||||
|
||||
const constContent = typesContent.substring(startIndex + 1, endIndex - 1).trim()
|
||||
|
||||
@@ -1414,16 +1341,9 @@ function parseConstFieldContent(
|
||||
const propertiesStart = fieldContent.search(/properties\s*:\s*\{/)
|
||||
if (propertiesStart !== -1) {
|
||||
const braceStart = fieldContent.indexOf('{', propertiesStart)
|
||||
let braceCount = 1
|
||||
let braceEnd = braceStart + 1
|
||||
const braceEnd = findMatchingClose(fieldContent, braceStart)
|
||||
|
||||
while (braceEnd < fieldContent.length && braceCount > 0) {
|
||||
if (fieldContent[braceEnd] === '{') braceCount++
|
||||
else if (fieldContent[braceEnd] === '}') braceCount--
|
||||
braceEnd++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (braceEnd !== -1) {
|
||||
const propertiesContent = fieldContent.substring(braceStart + 1, braceEnd - 1).trim()
|
||||
result.properties = parseConstProperties(
|
||||
propertiesContent,
|
||||
@@ -1452,16 +1372,9 @@ function parseConstFieldContent(
|
||||
const itemsStart = fieldContent.search(/items\s*:\s*\{/)
|
||||
if (itemsStart !== -1) {
|
||||
const braceStart = fieldContent.indexOf('{', itemsStart)
|
||||
let braceCount = 1
|
||||
let braceEnd = braceStart + 1
|
||||
const braceEnd = findMatchingClose(fieldContent, braceStart)
|
||||
|
||||
while (braceEnd < fieldContent.length && braceCount > 0) {
|
||||
if (fieldContent[braceEnd] === '{') braceCount++
|
||||
else if (fieldContent[braceEnd] === '}') braceCount--
|
||||
braceEnd++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (braceEnd !== -1) {
|
||||
const itemsContent = fieldContent.substring(braceStart + 1, braceEnd - 1).trim()
|
||||
const itemsType = itemsContent.match(/type\s*:\s*['"]([^'"]+)['"]/)
|
||||
const itemsDesc = extractDescription(itemsContent)
|
||||
@@ -1516,6 +1429,40 @@ function parseConstFieldContent(
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract outputs from a tool content block by trying:
|
||||
* 1. Const reference (e.g., `outputs: GIT_REF_OUTPUT_PROPERTIES,`)
|
||||
* 2. Inline object (e.g., `outputs: { id: { type: 'string', ... } }`)
|
||||
*/
|
||||
function extractOutputsFromToolContent(
|
||||
content: string,
|
||||
toolPrefix: string
|
||||
): Record<string, any> {
|
||||
const constMatch = content.match(
|
||||
/(?<![a-zA-Z_])outputs\s*:\s*([A-Z][A-Z_0-9]+)\s*(?:,|\}|$)/
|
||||
)
|
||||
if (constMatch) {
|
||||
const resolved = resolveConstReference(constMatch[1], toolPrefix)
|
||||
if (resolved && typeof resolved === 'object') {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
const outputsStart = content.search(/(?<![a-zA-Z_])outputs\s*:\s*{/)
|
||||
if (outputsStart !== -1) {
|
||||
const openBracePos = content.indexOf('{', outputsStart)
|
||||
if (openBracePos !== -1) {
|
||||
const closePos = findMatchingClose(content, openBracePos)
|
||||
if (closePos !== -1) {
|
||||
const outputsContent = content.substring(openBracePos + 1, closePos - 1).trim()
|
||||
return parseToolOutputsField(outputsContent, toolPrefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
function extractToolInfo(
|
||||
toolName: string,
|
||||
fileContent: string
|
||||
@@ -1539,16 +1486,9 @@ function extractToolInfo(
|
||||
|
||||
if (exportMatch && exportMatch.index !== undefined) {
|
||||
const startIndex = exportMatch.index + exportMatch[0].length - 1
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(fileContent, startIndex)
|
||||
|
||||
while (endIndex < fileContent.length && braceCount > 0) {
|
||||
if (fileContent[endIndex] === '{') braceCount++
|
||||
else if (fileContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endIndex !== -1) {
|
||||
toolContent = fileContent.substring(startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
@@ -1650,19 +1590,9 @@ function extractToolInfo(
|
||||
continue
|
||||
}
|
||||
|
||||
let braceCount = 1
|
||||
let endPos = startPos + 1
|
||||
const endPos = findMatchingClose(paramsContent, startPos)
|
||||
|
||||
while (endPos < paramsContent.length && braceCount > 0) {
|
||||
if (paramsContent[endPos] === '{') {
|
||||
braceCount++
|
||||
} else if (paramsContent[endPos] === '}') {
|
||||
braceCount--
|
||||
}
|
||||
endPos++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endPos !== -1) {
|
||||
const paramBlock = paramsContent.substring(startPos + 1, endPos - 1).trim()
|
||||
paramPositions.push({ name: paramName, start: startPos, content: paramBlock })
|
||||
}
|
||||
@@ -1704,36 +1634,43 @@ function extractToolInfo(
|
||||
// Get the tool prefix for resolving const references
|
||||
const toolPrefix = getToolPrefixFromName(toolName)
|
||||
|
||||
let outputs: Record<string, any> = {}
|
||||
let outputs = extractOutputsFromToolContent(toolContent, toolPrefix)
|
||||
|
||||
// Pattern 1: outputs directly assigned to a const (e.g., "outputs: GIT_REF_OUTPUT_PROPERTIES,")
|
||||
const directConstMatch = toolContent.match(
|
||||
/(?<![a-zA-Z_])outputs\s*:\s*([A-Z][A-Z_0-9]+)\s*(?:,|\}|$)/
|
||||
)
|
||||
if (directConstMatch) {
|
||||
const constName = directConstMatch[1]
|
||||
const resolvedConst = resolveConstReference(constName, toolPrefix)
|
||||
if (resolvedConst && typeof resolvedConst === 'object') {
|
||||
outputs = resolvedConst
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern 2: outputs is an object with properties (e.g., "outputs: { ... }")
|
||||
// If no outputs found, check for spread inheritance (e.g., "...extendParserTool")
|
||||
// toolContent may be narrowed past the spread line, so reconstruct the full block
|
||||
if (Object.keys(outputs).length === 0) {
|
||||
const outputsStart = toolContent.search(/(?<![a-zA-Z_])outputs\s*:\s*{/)
|
||||
if (outputsStart !== -1) {
|
||||
const openBracePos = toolContent.indexOf('{', outputsStart)
|
||||
if (openBracePos !== -1) {
|
||||
let braceCount = 1
|
||||
let pos = openBracePos + 1
|
||||
while (pos < toolContent.length && braceCount > 0) {
|
||||
if (toolContent[pos] === '{') braceCount++
|
||||
else if (toolContent[pos] === '}') braceCount--
|
||||
pos++
|
||||
let fullToolBlock = toolContent
|
||||
if (toolIdMatch && toolIdMatch.index !== undefined) {
|
||||
const beforeId = fileContent.substring(0, toolIdMatch.index)
|
||||
const exportRegex = /export\s+const\s+\w+[^=]*=\s*\{/g
|
||||
let lastExportMatch: RegExpExecArray | null = null
|
||||
let m: RegExpExecArray | null = null
|
||||
while ((m = exportRegex.exec(beforeId)) !== null) {
|
||||
lastExportMatch = m
|
||||
}
|
||||
if (lastExportMatch && lastExportMatch.index !== undefined) {
|
||||
const bracePos =
|
||||
lastExportMatch.index + lastExportMatch[0].length - 1
|
||||
const ep = findMatchingClose(fileContent, bracePos)
|
||||
if (ep !== -1) {
|
||||
fullToolBlock = fileContent.substring(bracePos, ep)
|
||||
}
|
||||
if (braceCount === 0) {
|
||||
const outputsContent = toolContent.substring(openBracePos + 1, pos - 1).trim()
|
||||
outputs = parseToolOutputsField(outputsContent, toolPrefix)
|
||||
}
|
||||
}
|
||||
const spreadMatch = fullToolBlock.match(/\.\.\.(\w+(?:Tool|Base)\w*)/)
|
||||
if (spreadMatch) {
|
||||
const baseVarName = spreadMatch[1]
|
||||
const baseToolRegex = new RegExp(
|
||||
`export\\s+const\\s+${baseVarName}(?=[^a-zA-Z0-9_]|$)[^=]*=\\s*\\{`
|
||||
)
|
||||
const baseToolMatch = fileContent.match(baseToolRegex)
|
||||
if (baseToolMatch && baseToolMatch.index !== undefined) {
|
||||
const baseStart =
|
||||
baseToolMatch.index + baseToolMatch[0].length - 1
|
||||
const endIdx = findMatchingClose(fileContent, baseStart)
|
||||
if (endIdx !== -1) {
|
||||
const baseToolContent = fileContent.substring(baseStart, endIdx)
|
||||
outputs = extractOutputsFromToolContent(baseToolContent, toolPrefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1921,24 +1858,15 @@ function parseToolOutputsField(outputsContent: string, toolPrefix?: string): Rec
|
||||
|
||||
const openBrace = braces.find((b) => b.type === 'open' && b.pos === bracePos)
|
||||
if (openBrace) {
|
||||
let braceCount = 1
|
||||
let endPos = bracePos + 1
|
||||
|
||||
while (endPos < outputsContent.length && braceCount > 0) {
|
||||
if (outputsContent[endPos] === '{') {
|
||||
braceCount++
|
||||
} else if (outputsContent[endPos] === '}') {
|
||||
braceCount--
|
||||
}
|
||||
endPos++
|
||||
const endPos = findMatchingClose(outputsContent, bracePos)
|
||||
if (endPos !== -1) {
|
||||
fieldPositions.push({
|
||||
name: fieldName,
|
||||
start: bracePos,
|
||||
end: endPos,
|
||||
level: openBrace.level,
|
||||
})
|
||||
}
|
||||
|
||||
fieldPositions.push({
|
||||
name: fieldName,
|
||||
start: bracePos,
|
||||
end: endPos,
|
||||
level: openBrace.level,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2001,16 +1929,9 @@ function parseFieldContent(fieldContent: string, toolPrefix?: string): any {
|
||||
|
||||
if (propertiesStart !== -1) {
|
||||
const braceStart = fieldContent.indexOf('{', propertiesStart)
|
||||
let braceCount = 1
|
||||
let braceEnd = braceStart + 1
|
||||
const braceEnd = findMatchingClose(fieldContent, braceStart)
|
||||
|
||||
while (braceEnd < fieldContent.length && braceCount > 0) {
|
||||
if (fieldContent[braceEnd] === '{') braceCount++
|
||||
else if (fieldContent[braceEnd] === '}') braceCount--
|
||||
braceEnd++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (braceEnd !== -1) {
|
||||
const propertiesContent = fieldContent.substring(braceStart + 1, braceEnd - 1).trim()
|
||||
result.properties = parsePropertiesContent(propertiesContent, toolPrefix)
|
||||
}
|
||||
@@ -2031,16 +1952,9 @@ function parseFieldContent(fieldContent: string, toolPrefix?: string): any {
|
||||
|
||||
if (itemsStart !== -1) {
|
||||
const braceStart = fieldContent.indexOf('{', itemsStart)
|
||||
let braceCount = 1
|
||||
let braceEnd = braceStart + 1
|
||||
const braceEnd = findMatchingClose(fieldContent, braceStart)
|
||||
|
||||
while (braceEnd < fieldContent.length && braceCount > 0) {
|
||||
if (fieldContent[braceEnd] === '{') braceCount++
|
||||
else if (fieldContent[braceEnd] === '}') braceCount--
|
||||
braceEnd++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (braceEnd !== -1) {
|
||||
const itemsContent = fieldContent.substring(braceStart + 1, braceEnd - 1).trim()
|
||||
const itemsType = itemsContent.match(/type\s*:\s*['"]([^'"]+)['"]/)
|
||||
|
||||
@@ -2213,19 +2127,9 @@ function parsePropertiesContent(
|
||||
|
||||
const startPos = match.index + match[0].length - 1
|
||||
|
||||
let braceCount = 1
|
||||
let endPos = startPos + 1
|
||||
const endPos = findMatchingClose(propertiesContent, startPos)
|
||||
|
||||
while (endPos < propertiesContent.length && braceCount > 0) {
|
||||
if (propertiesContent[endPos] === '{') {
|
||||
braceCount++
|
||||
} else if (propertiesContent[endPos] === '}') {
|
||||
braceCount--
|
||||
}
|
||||
endPos++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endPos !== -1) {
|
||||
const propContent = propertiesContent.substring(startPos + 1, endPos - 1).trim()
|
||||
|
||||
const hasDescription = /description\s*:\s*/.test(propContent)
|
||||
@@ -2729,16 +2633,9 @@ async function getHiddenAndVisibleBlockTypes(): Promise<{
|
||||
const startIndex = match.index + match[0].length - 1
|
||||
|
||||
// Extract the block content
|
||||
let braceCount = 1
|
||||
let endIndex = startIndex + 1
|
||||
const endIndex = findMatchingClose(fileContent, startIndex)
|
||||
|
||||
while (endIndex < fileContent.length && braceCount > 0) {
|
||||
if (fileContent[endIndex] === '{') braceCount++
|
||||
else if (fileContent[endIndex] === '}') braceCount--
|
||||
endIndex++
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
if (endIndex !== -1) {
|
||||
const blockContent = fileContent.substring(startIndex, endIndex)
|
||||
const blockType = extractStringPropertyFromContent(blockContent, 'type', true)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user