mirror of
https://github.com/simstudioai/sim.git
synced 2026-08-30 17:05:18 +08:00
fix(docs): stop publishing unsettable params, fix comment blanking (#7169)
* fix(docs): stop publishing unsettable params, fix comment blanking
generate-docs never read visibility, so every tool param appeared in the
public Input table -- including params marked visibility:'hidden', which are
shown to neither the user nor the LLM. Several are credential-shaped
(idToken, instanceUrl, apiToken, cloudId), so the docs told integrators they
could set values they cannot reach.
A hidden param is now dropped only when the block declares no subBlock for
it, matched on id or canonicalParamId -- a param can be hidden on the tool
because the block injects it while the block still renders it as a required
field the user types.
blankStringsAndComments kept the first and last character of every match.
That is right for a quoted string, where both are delimiters, but for a '//'
comment the last character is arbitrary source text, so a commented-out
'// options: [' left an unbalanced bracket that derailed the subBlock
scan. Parsing now throws rather than silently reporting that a block
exposes nothing, since that fallback was the destructive one.
Also corrects the LinkedIn w_member_social consent-screen description, which
read 'Access LinkedIn profile' for a scope that posts on the user's behalf.
* fix(docs): keep hidden params the block mapper supplies
The carve-out only recognized an identity match between a subBlock id and a
tool param, so a block that renames or assembles the value in
tools.config.params was invisible to it -- and the row was dropped even
though the user types it.
Cal.com's attendee (required) is assembled from attendeeName/attendeeEmail/
attendeeTimeZone; JSM's workspaceId comes from assetWorkspaceId; Textract
writes parameters.file from a field whose canonicalParamId is 'document',
which left the Mistral PDF Parser documenting zero inputs.
Collects params written by any accumulator identifier, not just 'result',
since the two real mappers use different names. Object keys are collected
without proving they are top-level, so a nested key can produce a false
keep -- one hard-to-set row is better than hiding a required input.
* fix(tools): reject values that cannot be a path segment
toGuardedString coerced with String(value), so an object reached the wire as
%5Bobject%20Object%5D and a boolean as 'true' -- a doomed request instead of
a clean error, on 44 live call sites. Accepts string, bigint, and finite
non-exponential numbers; everything else throws a named error.
Rejects a number whose decimal text is a rewrite rather than the caller's
value: 1e21 stringifies to '1e+21', and an integer past 2^53 has already
lost digits. A snowflake cannot be repaired here at all -- JSON.parse
destroys it before this runs -- so the doc now says it must arrive as a
string, and cites Box folderId (root = 0) instead.
Corrects the claim that the parser removes only an exact '.' or '..'; the
spec defines 11 removable spellings. The guards are sufficient because
encodeURIComponent escapes '%', not because the others cannot occur.
* test(oauth): pin the LinkedIn write-scope description
Nothing guarded the consent-screen text: utils.test.ts covered only the
Bitbucket and Reddit overrides, and the modal test stubs
getScopeDescription to identity, so a regression to a read-only label for a
posting scope would pass silently.
* fix(docs): stop a comment hijacking the id scan, unhang a Firecrawl reference
The depth-1 walk copied from the raw source at indices where the blanked
copy was at depth 1, so 'id:' inside a string value or a comment landed in
the scanned text and won the first match. With the keep-bias that now means
a phantom id can retain a param the block never exposes. Matching runs on
the blanked text and reads the literal back through a source-index map. No
block in the repo trips this today -- verified across all 305 -- so this is
a latent fix.
Removing the unsettable scrapeOptions row left five Firecrawl Search output
descriptions referencing a name that no longer appears on the page. They
now describe the response condition instead. Pointing them at 'formats' was
not an option: that subBlock is conditioned on scrape/parse/batch_scrape and
the search tool declares no such param, so it would have swapped one
dangling reference for another.
* refactor(tools): drop the unused guard API from this PR
safeUrlPath, safeOpaqueUrlSegment and SafeUrlPathOptions had zero call
sites -- 469 lines of unused API in a docs-generator change, including an
allowEmptySegments flag whose own TSDoc documents a host-takeover footgun
('//evil.com' under new URL(relative, base)). They belong with the ~693
traversal call sites that use them, where they can be reviewed against real
usage.
What stays is the part with 44 live consumers: safeUrlPathSegment now
accepts number and bigint. Staging already rejected every non-string, so
this only widens acceptance -- a differential over 87 real call-site values
shows 87 identical, 0 differing.
Also: encodeURIComponent throws an unnamed URIError on a lone surrogate,
which JSON.parse accepts, so a truncated emoji lost the param name the file
claims as its invariant. And the exponential rejection told tiny values like
1e-7 they were 'too large' when they round-trip exactly; the rejection is
right -- a path segment should not rewrite 0.0000001 into other text -- but
the stated ground was not.
* fix(docs): abort before writing on a parse failure, unhang three descriptions
The guard's own TSDoc said to fail rather than guess, because the fallback
strips every hidden param from a page. The caller did the opposite: it
caught, recorded, and continued with an empty id set, and the non-zero exit
came after every page was already written. A developer who reran, saw red,
and missed the scrollback could commit a stripped page. Parse failures are
now detected in a dry pass before anything is emitted.
The zero-id guard also only fired when the array held a literal '{', so a
subBlocks built by a helper call was silently empty, while a subBlocks whose
literals only spread ({ ...sb, required: true }) hard-failed the build. It
now fires on the destructive case alone -- reporting an unreadable array
while leaving legitimate opaque spreads, which 23 blocks rely on, untouched.
Three descriptions referenced things the reader can no longer see: Dataverse
mandated base64 after its base64 row was removed, Vanta's mimeType described
itself as useful only on that removed path, and five Drive actions told the
reader to fetch a next page with no input left to accept the token.
* chore(tinyfish): use a white block background
Matches the dominant convention (97 blocks use #FFFFFF). Regenerates the
docs page, the tool metadata, and the deployment catalog, which each carried
the previous value.
* fix(docs): correct Drive/Firecrawl/Vanta output and param descriptions
Google Drive nextPageToken: the discovery doc says the field is *absent*
at the end of the list, not empty. Say absent, and name the resource
(files/comments/permissions/revisions) per tool.
Firecrawl search outputs: restore the per-format gate the v2 OpenAPI
states ("HTML content if requested in formats"), and add that Search
exposes no input for those formats.
Vanta mimeType: the route resolves a content type from storage on every
path, so the param is never read. Say so instead of describing it as a
fallback.
Dataverse: describe the request as sending the bytes as the raw body.
* fix(docs): correct the Vanta mimeType and Firecrawl reachability wording
Vanta: the base64 branch (route.ts:100) reads params.mimeType as its only
content-type source, so "not currently applied" was wrong. Say it applies
there and is a fallback on the File branch.
Firecrawl: scrapeOptions is declared in the block's inputs map with no
subBlock, so it is reachable by a direct tool call. "Exposes no visible
input" rather than "exposes no input".
* fix(docs): never abort the generator on an unreadable subBlocks array
An unreadable `subBlocks` value used to throw, and with no spread base to
fall back on the failure was fatal: the pre-scan recorded it and
`generateAllBlockDocs` returned false, so `main` exited 1 and nothing was
written at all. Nine shipped blocks already use the non-literal form and
are saved only because they happen to spread a base — the first block
authored as `subBlocks: myFields` without one would brick `generate-docs`
and `docs:check` for the whole repository.
The author's reason for aborting was sound: an empty `userSettableParamIds`
is indistinguishable from "nothing is settable", which strips every hidden
param and publishes a wrong page. So the fix is not to treat the failure as
empty — it is to represent UNKNOWN distinctly. `extractBlockSuppliedParamIds`
now returns `{ ids, mapperIds, parseError }` with `ids: null` for UNKNOWN,
that `null` flows through `BlockConfig.userSettableParamIds`, `getToolInfo`
and `extractToolInfo`, and the filter site skips filtering entirely when it
sees it — restoring the pre-filter behaviour for that one block instead of
killing the run. `getToolInfo`'s default is `null` for the same reason: `[]`
as a default silently meant "strip everything".
The mapper scan now runs before the subBlocks scan, so a spread-inheriting
block keeps its mapper's renames when only the subBlocks scan fails. With
nothing left that can record a fatal, the dry pre-scan and its reporting
are removed.
Also fixes a silent blind spot in the mapper scan: both key regexes require
a literal `:`, so a mapper returning a shorthand property (`{ file }`) or
writing a computed key (`result['file'] = …`) dropped a real user input from
the docs with no warning. Shorthand names are read from the depth-1 comma
segments of brace-matched regions, which keeps call argument lists from
contributing names.
Verified byte-identical output: `scripts/generate-docs.ts` and
`tool-metadata:generate` reproduce all 302 generated files unchanged, the
credential-shaped hidden params stay stripped, and `check:audits` passes.
* fix(docs): name the input that gates Firecrawl search scrape output
The previous wording ended each description with "for which the Search
operation exposes no visible input", a relative clause that attaches
ambiguously and never tells the reader what controls the field. Name
scrapeOptions and note that it is hidden.
* fix(docs): say the Vanta mimeType is ignored for File-input uploads
Every return path of downloadServableFileFromStorage yields a non-empty
contentType (a literal, getMimeTypeFromExtension's GENERIC_MIME_TYPE
fallback, or resolveServableDocBytes' constants/getContentType), so
resolved.contentType always wins at route.ts:79-81 and params.mimeType is
unreachable on that branch. It is not a fallback; it is ignored.
* fix(docs): note the hidden inputs that gate Pulse html and figures output
extractFigure and returnHtml are visibility: 'hidden' with no subBlock, and
parser.ts:135-144 only forwards them when defined, so neither output can be
produced today. Say so on the output rows rather than deleting them, since
removing an output field would break saved block references.
chunks is left alone: chunking/chunkSize are user-only with real subBlocks.
* fix(docs): report a spread-only subBlocks array as unknown, not empty
extractUserSettableParamIds answered [] for a subBlocks array whose every
element spreads a fields array it cannot follow (NotionV2Block's
`[...NotionBlock.subBlocks, ...getTrigger(x).subBlocks]`). [] asserts the
block supplies nothing, so the hidden-param filter stripped every hidden
param from every tool the block owns - silently, with no parseError and so
no warning. That is the exact false-drop the null UNKNOWN state exists to
prevent.
Return null in that case and propagate it: extractBlockSuppliedParamIds no
longer folds it into [], and the block pass no longer collapses it with
`supplied.ids ?? []`. A config-level spread base still narrows the filter to
its readable fields plus the mapper's renames; with no base the filter is
switched off. An array with at least one inline id, a genuinely empty array,
and the existing throw/warn paths are unchanged - all 8 warned blocks warn
identically and every generated page is byte-identical.
Also pin the hidden-param filter on extractToolInfo's source-parsing path,
which had no coverage at all: deleting it outright left the suite green.
* fix(daytona): stop the lifecycle tools crashing on a non-string sandboxId
start/stop/delete echo sandboxId back as the output id when the API returns
no body, via params.sandboxId.trim() inside transformResponse - after the
request has already gone out. sandboxId is declared type: 'string' but
arrives unvalidated, and now that safeUrlPathSegment accepts a numeric id a
number builds a URL, sends the DELETE/START/STOP, and only then throws an
unnamed TypeError. Both the old and new behaviour fail, so this is not a
regression of a working workflow, but for delete_sandbox the side effect is
irreversible and the caller cannot tell what happened.
Fixed with a shared resolveSandboxId in utils.ts rather than a coercion at
each of the three sites: utils.ts already owns every sandbox-id helper, the
three tools already import from it, and the reasoning belongs in one place.
The encoded value cannot be reused - it is percent-encoded and would be
wrong as an output id. Behaviour for a string is unchanged.
* docs(url-path): drop the false claim that widening restores prior behaviour
The module TSDoc said the number/bigint widening fixed 'a regression for the
call sites whose pre-guard form was a bare ${params.id} template that
stringified a number fine'. It did not. Every pre-guard form in a42299066d
used .trim() (`/v13/deployments/${params.deploymentId.trim()}`,
`sandboxId?.trim()`), so a numeric id threw there too - no importer has ever
accepted one. The cited examples were also wrong: only Vercel and Daytona
import this module, and neither Box nor X does.
Replaced with the real motivation - params are declared type: 'string' but
nothing enforces it before the guard, and the old coercion-to-'' turned a
supplied numeric id into a misleading 'is required'. Two test comments made
the same claim ('still stringifies', 'replaced bare ${params.id} templates')
and are corrected; no assertion is weakened.
* fix(docs): correct output rows that cite inputs Sim does not send
mistral_parse: the PR removed the includeImageBase64 input row but left the
image_base64 output citing include_image_base64=true, so the page referenced
an input it no longer documents. includeImageBase64 is visibility: 'hidden'
with no subBlock, mapper or canonicalParamId, so it is annotated the same way
Pulse's html and figures were.
The sibling rows are a stronger defect: table_format, extract_header and
extract_footer appear nowhere in the repo - not as tool params, not in the
request body parser.ts builds - so tables/header/footer cited options Sim
never sends. Worded accordingly rather than as hidden inputs.
pulse structured_output cited 'if schema was provided', but there is no
schema or structuredOutput param in the tool, in pulseParseInputSchema, or in
the outgoing body, so the field is always null.
No output field is deleted - removing one changes the block's output schema
and could break saved workflow references.
* style: wrap long description literals to satisfy biome
Formatting only — regenerating both artifacts produces a byte-identical
tree, so no description text changed.
* fix(docs): cite the real Mistral options behind tables, header and footer
The previous wording was self-contradictory on tables: it described
placeholder-referenced table objects and then asserted the list is empty.
Mistral's OCR API does expose table_format, extract_header and
extract_footer. table_format defaults to inline markdown, so the separate
tables list stays empty; extract_header and extract_footer default to
false, so neither field is returned. Sim sets none of the three.
Name the option and its default in each description instead of asserting
an outcome the request body alone does not establish.
This commit is contained in:
@@ -52,7 +52,6 @@ Upload a file to a Box folder
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `parentFolderId` | string | Yes | The ID of the folder to upload the file to \(use "0" for root\) |
|
||||
| `file` | file | No | The file to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Legacy: base64 encoded file content |
|
||||
| `fileName` | string | No | Optional filename override |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -216,7 +216,6 @@ Upload a file to a Daytona sandbox
|
||||
| `sandboxId` | string | Yes | ID of the sandbox to upload the file to |
|
||||
| `destinationPath` | string | Yes | Destination path in the sandbox \(a trailing slash uploads into that directory using the file name\) |
|
||||
| `file` | file | No | The file to upload |
|
||||
| `fileContent` | string | No | Legacy: base64 encoded file content |
|
||||
| `fileName` | string | No | Optional file name override |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -44,7 +44,6 @@ Upload a file to Dropbox
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path in Dropbox where the file should be saved \(e.g., /folder/document.pdf\) |
|
||||
| `file` | file | No | The file to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Legacy: base64 encoded file content |
|
||||
| `fileName` | string | No | Optional filename \(used if path is a folder\) |
|
||||
| `mode` | string | No | Write mode: add \(default\) or overwrite |
|
||||
| `autorename` | boolean | No | If true, rename the file if there is a conflict |
|
||||
|
||||
@@ -52,7 +52,6 @@ Extract structured content from web pages with comprehensive metadata support. C
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `url` | string | Yes | The URL to scrape content from \(e.g., "https://example.com/page"\) |
|
||||
| `formats` | json | No | Output formats supplied by existing Firecrawl block configurations |
|
||||
| `scrapeOptions` | json | No | Options for content scraping |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
@@ -90,7 +89,6 @@ Scrape multiple URLs in a single batch job and retrieve structured content from
|
||||
| `onlyMainContent` | boolean | No | Extract only main content from pages |
|
||||
| `maxConcurrency` | number | No | Maximum number of concurrent scrapes |
|
||||
| `ignoreInvalidURLs` | boolean | No | Skip invalid URLs instead of failing the batch \(default: true\) |
|
||||
| `scrapeOptions` | json | No | Advanced scraping configuration options |
|
||||
| `zeroDataRetention` | boolean | No | Enable zero data retention |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
@@ -159,7 +157,6 @@ Search for information on the web using Firecrawl
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | The search query to use |
|
||||
| `scrapeOptions` | json | No | Advanced scrape options supplied by existing configurations |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
@@ -170,11 +167,11 @@ Search for information on the web using Firecrawl
|
||||
| ↳ `title` | string | Search result title from search engine |
|
||||
| ↳ `description` | string | Search result description/snippet from search engine |
|
||||
| ↳ `url` | string | URL of the search result |
|
||||
| ↳ `markdown` | string | Page content in markdown \(when scrapeOptions.formats includes "markdown"\) |
|
||||
| ↳ `html` | string | Processed HTML content \(when scrapeOptions.formats includes "html"\) |
|
||||
| ↳ `rawHtml` | string | Unprocessed raw HTML \(when scrapeOptions.formats includes "rawHtml"\) |
|
||||
| ↳ `links` | array | Links found on the page \(when scrapeOptions.formats includes "links"\) |
|
||||
| ↳ `screenshot` | string | Screenshot URL \(expires after 24 hours, when scrapeOptions.formats includes "screenshot"\) |
|
||||
| ↳ `markdown` | string | Page content in markdown; returned only when scraping was requested via the hidden scrapeOptions input |
|
||||
| ↳ `html` | string | Processed HTML content; returned only when "html" is among the scrape formats requested via the hidden scrapeOptions input |
|
||||
| ↳ `rawHtml` | string | Unprocessed raw HTML; returned only when "rawHtml" is among the scrape formats requested via the hidden scrapeOptions input |
|
||||
| ↳ `links` | array | Links found on the page; returned only when "links" is among the scrape formats requested via the hidden scrapeOptions input |
|
||||
| ↳ `screenshot` | string | Screenshot URL \(expires after 24 hours\); returned only when "screenshot" is among the scrape formats requested via the hidden scrapeOptions input |
|
||||
| ↳ `metadata` | object | Metadata about the search result page |
|
||||
| ↳ `title` | string | Page title |
|
||||
| ↳ `description` | string | Page meta description |
|
||||
@@ -195,7 +192,6 @@ Crawl entire websites and extract structured content from all accessible pages
|
||||
| `maxDepth` | number | No | Maximum depth to crawl from the starting URL \(e.g., 1, 2, 3\). Controls how many levels deep to follow links |
|
||||
| `formats` | json | No | Output formats for scraped content \(e.g., \["markdown"\], \["markdown", "html"\], \["markdown", "links"\]\) |
|
||||
| `prompt` | string | No | Natural-language crawl guidance supplied by existing configurations |
|
||||
| `scrapeOptions` | json | No | Advanced scrape options supplied by existing configurations |
|
||||
| `excludePaths` | json | No | URL paths to exclude from crawling \(e.g., \["/blog/*", "/admin/*", "/*.pdf"\]\) |
|
||||
| `includePaths` | json | No | URL paths to include in crawling \(e.g., \["/docs/*", "/api/*"\]\). Only these paths will be crawled |
|
||||
| `onlyMainContent` | boolean | No | Extract only main content from pages |
|
||||
@@ -287,7 +283,6 @@ Get a complete list of URLs from any website quickly and reliably. Useful for di
|
||||
| `ignoreQueryParameters` | boolean | No | Exclude URLs containing query strings \(default: true\) |
|
||||
| `limit` | number | No | Maximum number of links to return \(e.g., 100, 1000, 5000\). Max: 100,000, default: 5,000 |
|
||||
| `timeout` | number | No | Request timeout in milliseconds |
|
||||
| `location` | json | No | Geographic context for proxying \(country, languages\) |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
@@ -313,7 +308,6 @@ Extract structured data from entire webpages using natural language prompts and
|
||||
| `includeSubdomains` | boolean | No | Extend scanning to subdomains \(default: true\) |
|
||||
| `showSources` | boolean | No | Return data sources in the response \(default: false\) |
|
||||
| `ignoreInvalidURLs` | boolean | No | Skip invalid URLs in the array \(default: true\) |
|
||||
| `scrapeOptions` | json | No | Advanced scraping configuration options |
|
||||
| `apiKey` | string | Yes | Firecrawl API key |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -120,11 +120,8 @@ Create comments on GitHub PRs
|
||||
| `body` | string | Yes | Comment content |
|
||||
| `pullNumber` | number | Yes | Pull request number |
|
||||
| `path` | string | No | File path for review comment |
|
||||
| `position` | number | No | Line number for review comment |
|
||||
| `commentType` | string | No | Type of comment \(pr_comment or file_comment\) |
|
||||
| `line` | number | No | Line number for review comment |
|
||||
| `side` | string | No | Side of the diff \(LEFT or RIGHT\) |
|
||||
| `commitId` | string | No | The SHA of the commit to comment on |
|
||||
| `apiKey` | string | Yes | GitHub API token |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -89,7 +89,6 @@ List events from Google Calendar. Returns API-aligned fields only.
|
||||
| `maxResults` | number | No | Maximum number of events to return \(max 2500\) |
|
||||
| `pageToken` | string | No | Token for retrieving the next page of results |
|
||||
| `orderBy` | string | No | Order of events: startTime \(chronological, the default\) or updated \(last-modified\). startTime is always valid here because singleEvents is set. |
|
||||
| `showDeleted` | boolean | No | Include deleted events |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -227,7 +226,6 @@ Get instances of a recurring event from Google Calendar. Returns API-aligned fie
|
||||
| `timeMax` | string | No | Upper bound for instances \(RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z\) |
|
||||
| `maxResults` | number | No | Maximum number of instances to return \(default 250, max 2500\) |
|
||||
| `pageToken` | string | No | Token for retrieving subsequent pages of results |
|
||||
| `showDeleted` | boolean | No | Include deleted instances |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -248,8 +246,6 @@ List all calendars in the user's calendar list. Returns API-aligned fields only.
|
||||
| `minAccessRole` | string | No | Minimum access role for returned calendars: freeBusyReader, reader, writer, or owner |
|
||||
| `maxResults` | number | No | Maximum number of calendars to return \(default 100, max 250\) |
|
||||
| `pageToken` | string | No | Token for retrieving subsequent pages of results |
|
||||
| `showDeleted` | boolean | No | Include deleted calendars |
|
||||
| `showHidden` | boolean | No | Include hidden calendars |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ List files and folders in Google Drive with complete metadata
|
||||
| `folderId` | string | No | The ID of the folder to list files from \(internal use\) |
|
||||
| `query` | string | No | Search term to filter files by name \(e.g. "budget" finds files with "budget" in the name\). Do NOT use Google Drive query syntax here - just provide a plain search term. |
|
||||
| `pageSize` | number | No | The maximum number of files to return \(default: 100\) |
|
||||
| `pageToken` | string | No | The page token to use for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -105,7 +104,7 @@ List files and folders in Google Drive with complete metadata
|
||||
| ↳ `isAppAuthorized` | boolean | Whether created by requesting app |
|
||||
| ↳ `contentRestrictions` | json | Content restrictions |
|
||||
| ↳ `linkShareMetadata` | json | Link share metadata |
|
||||
| `nextPageToken` | string | Token for fetching the next page of results |
|
||||
| `nextPageToken` | string | Page token for the next page of files; absent from the response when the end of the files list has been reached |
|
||||
|
||||
### Get Google Drive File
|
||||
|
||||
@@ -485,7 +484,6 @@ Search for files in Google Drive using advanced query syntax (e.g., fullText con
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Google Drive query string using advanced search syntax \(e.g., "fullText contains 'budget'", "mimeType = 'application/pdf'", "modifiedTime > '2024-01-01'"\) |
|
||||
| `pageSize` | number | No | Maximum number of files to return \(default: 100\) |
|
||||
| `pageToken` | string | No | Token for fetching the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -518,7 +516,7 @@ Search for files in Google Drive using advanced query syntax (e.g., fullText con
|
||||
| ↳ `driveId` | string | Shared drive ID |
|
||||
| ↳ `capabilities` | json | User capabilities on file |
|
||||
| ↳ `version` | string | Version number |
|
||||
| `nextPageToken` | string | Token for fetching the next page of results |
|
||||
| `nextPageToken` | string | Page token for the next page of files; absent from the response when the end of the files list has been reached |
|
||||
|
||||
### Update Google Drive File
|
||||
|
||||
@@ -673,7 +671,6 @@ List all permissions (who has access) for a file in Google Drive
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to list permissions for |
|
||||
| `pageToken` | string | No | The page token to use for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -692,7 +689,7 @@ List all permissions (who has access) for a file in Google Drive
|
||||
| ↳ `allowFileDiscovery` | boolean | Whether file is discoverable by grantee |
|
||||
| ↳ `pendingOwner` | boolean | Whether ownership transfer is pending |
|
||||
| ↳ `permissionDetails` | json | Details about inherited permissions |
|
||||
| `nextPageToken` | string | Token for fetching the next page of permissions |
|
||||
| `nextPageToken` | string | Page token for the next page of permissions; absent from the response when the end of the permissions list has been reached |
|
||||
|
||||
### Export Google Drive File
|
||||
|
||||
@@ -723,7 +720,6 @@ List the revision history of a file in Google Drive
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileId` | string | Yes | The ID of the file to list revisions for |
|
||||
| `pageSize` | number | No | Maximum number of revisions to return \(1-1000, default 200\) |
|
||||
| `pageToken` | string | No | The page token to use for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -741,7 +737,7 @@ List the revision history of a file in Google Drive
|
||||
| ↳ `md5Checksum` | string | MD5 checksum for binary revisions |
|
||||
| ↳ `size` | string | Size of the revision in bytes |
|
||||
| ↳ `exportLinks` | json | Export format links for the revision |
|
||||
| `nextPageToken` | string | Token for fetching the next page of revisions |
|
||||
| `nextPageToken` | string | Page token for the next page of revisions; absent from the response when the end of the revisions list has been reached |
|
||||
|
||||
### Get Google Drive Revision
|
||||
|
||||
@@ -783,7 +779,6 @@ List comments on a file in Google Drive
|
||||
| `includeDeleted` | boolean | No | Whether to include deleted comments \(their content is stripped\) |
|
||||
| `pageSize` | number | No | Maximum number of comments to return \(1-100, default 20\) |
|
||||
| `startModifiedTime` | string | No | Only return comments modified after this RFC 3339 timestamp |
|
||||
| `pageToken` | string | No | The page token to use for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -801,7 +796,7 @@ List comments on a file in Google Drive
|
||||
| ↳ `anchor` | string | Region of the document the comment refers to |
|
||||
| ↳ `quotedFileContent` | json | The file content the comment quotes |
|
||||
| ↳ `replies` | json | Threaded replies to the comment |
|
||||
| `nextPageToken` | string | Token for fetching the next page of comments |
|
||||
| `nextPageToken` | string | Page token for the next page of comments; absent from the response when the end of the comments list has been reached |
|
||||
|
||||
### Create Google Drive Comment
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ Write data to a specific sheet in a Google Sheets spreadsheet
|
||||
| `cellRange` | string | No | The cell range to write to \(e.g. "A1:D10", "A1"\). Defaults to "A1" if not specified. |
|
||||
| `values` | array | Yes | The data to write as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
|
||||
| `valueInputOption` | string | No | The format of the data to write |
|
||||
| `includeValuesInResponse` | boolean | No | Whether to include the written values in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -102,7 +101,6 @@ Update data in a specific sheet in a Google Sheets spreadsheet
|
||||
| `cellRange` | string | No | The cell range to update \(e.g. "A1:D10", "A1"\). Defaults to "A1" if not specified. |
|
||||
| `values` | array | Yes | The data to update as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\]\]\) or array of objects. |
|
||||
| `valueInputOption` | string | No | The format of the data to update |
|
||||
| `includeValuesInResponse` | boolean | No | Whether to include the updated values in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -129,7 +127,6 @@ Append data to the end of a specific sheet in a Google Sheets spreadsheet
|
||||
| `values` | array | Yes | The data to append as a 2D array \(e.g. \[\["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
|
||||
| `valueInputOption` | string | No | The format of the data to append |
|
||||
| `insertDataOption` | string | No | How to insert the data \(OVERWRITE or INSERT_ROWS\) |
|
||||
| `includeValuesInResponse` | boolean | No | Whether to include the appended values in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ Retrieve detailed information about a specific Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to retrieve \(e.g., PROJ-123\) |
|
||||
| `includeAttachments` | boolean | No | Download attachment file contents and include them as files in the output |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -262,7 +261,6 @@ Update a Jira issue
|
||||
| `customFieldId` | string | No | Custom field ID to update \(e.g., customfield_10001\) |
|
||||
| `customFieldValue` | string | No | Value for the custom field |
|
||||
| `notifyUsers` | boolean | No | Whether to send email notifications about this update \(default: true\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -287,7 +285,6 @@ Create a new Jira issue
|
||||
| `description` | string | No | Description for the issue. Accepts plain text \(auto-wrapped in ADF\) or a raw ADF document object |
|
||||
| `priority` | string | No | Priority ID or name for the issue \(e.g., "10000" or "High"\) |
|
||||
| `assignee` | string | No | Assignee account ID for the issue |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story, Bug, Epic, Sub-task\) |
|
||||
| `parent` | json | No | Parent issue key for creating subtasks \(e.g., \{ "key": "PROJ-123" \}\) |
|
||||
| `labels` | array | No | Labels for the issue \(array of label names\) |
|
||||
@@ -322,7 +319,6 @@ Retrieve multiple Jira issues from a project in bulk
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | Yes | Jira project key \(e.g., PROJ\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -364,7 +360,6 @@ Delete a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to delete \(e.g., PROJ-123\) |
|
||||
| `deleteSubtasks` | boolean | No | Whether to delete subtasks. If false, parent issues with subtasks cannot be deleted. |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -385,7 +380,6 @@ Assign a Jira issue to a user
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to assign \(e.g., PROJ-123\) |
|
||||
| `accountId` | string | Yes | Account ID of the user to assign the issue to. Use "-1" for automatic assignment, or leave empty / pass "null" to unassign. |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -409,7 +403,6 @@ Move a Jira issue between workflow statuses (e.g., To Do -> In Progress)
|
||||
| `transitionId` | string | Yes | ID of the transition to execute \(e.g., "11" for "To Do", "21" for "In Progress"\) |
|
||||
| `comment` | string | No | Optional comment to add when transitioning the issue |
|
||||
| `resolution` | string | No | Resolution name to set during transition \(e.g., "Fixed", "Won't Fix"\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -437,7 +430,6 @@ Search for Jira issues using JQL (Jira Query Language)
|
||||
| `nextPageToken` | string | No | Cursor token for the next page of results. Omit for the first page. |
|
||||
| `maxResults` | number | No | Maximum number of results to return per page \(default: 50\) |
|
||||
| `fields` | array | No | Array of field names to return \(default: all fields\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -520,7 +512,6 @@ Add a comment to a Jira issue
|
||||
| `issueKey` | string | Yes | Jira issue key to add comment to \(e.g., PROJ-123\) |
|
||||
| `body` | string | Yes | Comment body text |
|
||||
| `visibility` | json | No | Restrict comment visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -555,7 +546,6 @@ Get all comments from a Jira issue
|
||||
| `startAt` | number | No | Index of the first comment to return \(default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of comments to return \(default: 50\) |
|
||||
| `orderBy` | string | No | Sort order for comments: "-created" for newest first, "created" for oldest first |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -605,7 +595,6 @@ Update an existing comment on a Jira issue
|
||||
| `commentId` | string | Yes | ID of the comment to update |
|
||||
| `body` | string | Yes | Updated comment text |
|
||||
| `visibility` | json | No | Restrict comment visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -638,7 +627,6 @@ Delete a comment from a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key containing the comment \(e.g., PROJ-123\) |
|
||||
| `commentId` | string | Yes | ID of the comment to delete |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -660,7 +648,6 @@ Get all attachments from a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to get attachments from \(e.g., PROJ-123\) |
|
||||
| `includeAttachments` | boolean | No | Download attachment file contents and include them as files in the output |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -698,7 +685,6 @@ Add attachments to a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to add attachments to \(e.g., PROJ-123\) |
|
||||
| `files` | file[] | Yes | Files to attach to the Jira issue |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -725,7 +711,6 @@ Delete an attachment from a Jira issue
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `attachmentId` | string | Yes | ID of the attachment to delete |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -749,7 +734,6 @@ Add a time tracking worklog entry to a Jira issue
|
||||
| `comment` | string | No | Optional comment for the worklog entry |
|
||||
| `started` | string | No | Optional start time in ISO format \(defaults to current time\) |
|
||||
| `visibility` | json | No | Restrict worklog visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -784,7 +768,6 @@ Get all worklog entries from a Jira issue
|
||||
| `issueKey` | string | Yes | Jira issue key to get worklogs from \(e.g., PROJ-123\) |
|
||||
| `startAt` | number | No | Index of the first worklog to return \(default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of worklogs to return \(default: 50\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -836,7 +819,6 @@ Update an existing worklog entry on a Jira issue
|
||||
| `comment` | string | No | Optional comment for the worklog entry |
|
||||
| `started` | string | No | Optional start time in ISO format |
|
||||
| `visibility` | json | No | Restrict worklog visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -880,7 +862,6 @@ Delete a worklog entry from a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key containing the worklog \(e.g., PROJ-123\) |
|
||||
| `worklogId` | string | Yes | ID of the worklog entry to delete |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -904,7 +885,6 @@ Create a link relationship between two Jira issues
|
||||
| `outwardIssueKey` | string | Yes | Jira issue key for the outward issue \(e.g., PROJ-456\) |
|
||||
| `linkType` | string | Yes | The type of link relationship \(e.g., "Blocks", "Relates to", "Duplicates"\) |
|
||||
| `comment` | string | No | Optional comment to add to the issue link |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -927,7 +907,6 @@ Delete a link between two Jira issues
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `linkId` | string | Yes | ID of the issue link to delete |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -948,7 +927,6 @@ Add a watcher to a Jira issue to receive notifications about updates
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to add watcher to \(e.g., PROJ-123\) |
|
||||
| `accountId` | string | Yes | Account ID of the user to add as watcher |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -970,7 +948,6 @@ Remove a watcher from a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to remove watcher from \(e.g., PROJ-123\) |
|
||||
| `accountId` | string | Yes | Account ID of the user to remove as watcher |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -993,7 +970,6 @@ Get Jira users. If an account ID is provided, returns a single user. Otherwise,
|
||||
| `accountId` | string | No | Optional account ID to get a specific user. If not provided, returns all users. |
|
||||
| `startAt` | number | No | The index of the first user to return \(for pagination, default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of users to return \(default: 50\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1026,7 +1002,6 @@ Search for Jira users by email address or display name. Returns matching users w
|
||||
| `query` | string | Yes | A query string to search for users. Can be an email address, display name, or partial match. |
|
||||
| `maxResults` | number | No | Maximum number of users to return \(default: 50, max: 1000\) |
|
||||
| `startAt` | number | No | The index of the first user to return \(for pagination, default: 0\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1058,7 +1033,6 @@ List Jira projects visible to the user, with optional name/key filtering and pag
|
||||
| `query` | string | No | Filter projects by partial name or key match |
|
||||
| `startAt` | number | No | The index of the first project to return \(for pagination, default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of projects to return \(default: 50, max: 100\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1091,7 +1065,6 @@ Get the details of a single Jira project by its ID or key, including its type, l
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | Yes | The project ID or key \(e.g., "PROJ" or "10000"\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1124,7 +1097,6 @@ Get the workflow transitions available for an issue in its current status. Use t
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | The issue key or ID \(e.g., PROJ-123\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1151,7 +1123,6 @@ List all issue types visible to the user across projects (e.g., Task, Bug, Story
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1177,7 +1148,6 @@ Get all system and custom fields defined in the Jira instance. Useful for discov
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ Get all service desks from Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `expand` | string | No | Comma-separated fields to expand in the response |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -75,7 +74,6 @@ Get request types for a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `searchQuery` | string | No | Filter request types by name |
|
||||
| `groupId` | string | No | Filter by request type group ID |
|
||||
@@ -110,7 +108,6 @@ Create a new service request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `requestTypeId` | string | Yes | Request Type ID \(e.g., "10", "15"\) |
|
||||
| `summary` | string | No | Summary/title for the service request \(required unless using Form Answers\) |
|
||||
@@ -145,7 +142,6 @@ Get a single service request from Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `expand` | string | No | Comma-separated fields to expand: participant, status, sla, requestType, serviceDesk, attachment, comment, action |
|
||||
|
||||
@@ -185,7 +181,6 @@ Get multiple service requests from Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | No | Filter by service desk ID \(e.g., "1", "2"\) |
|
||||
| `requestOwnership` | string | No | Filter by ownership: OWNED_REQUESTS, PARTICIPATED_REQUESTS, APPROVER, ALL_REQUESTS |
|
||||
| `requestStatus` | string | No | Filter by status: OPEN_REQUESTS, CLOSED_REQUESTS, ALL_REQUESTS |
|
||||
@@ -232,7 +227,6 @@ Add a comment (public or internal) to a service request in Jira Service Manageme
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `body` | string | Yes | Comment body text |
|
||||
| `isPublic` | boolean | Yes | Whether the comment is public \(visible to customer\) or internal \(true/false\) |
|
||||
@@ -263,7 +257,6 @@ Get comments for a service request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `isPublic` | boolean | No | Filter to only public comments \(true/false\) |
|
||||
| `internal` | boolean | No | Filter to only internal comments \(true/false\) |
|
||||
@@ -300,7 +293,6 @@ Get customers for a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `query` | string | No | Search query to filter customers \(e.g., "john", "acme"\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
@@ -329,7 +321,6 @@ Add customers to a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `accountIds` | string | Yes | Comma-separated Atlassian account IDs to add as customers |
|
||||
|
||||
@@ -350,7 +341,6 @@ Get organizations for a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -375,7 +365,6 @@ Create a new organization in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `name` | string | Yes | Name of the organization to create |
|
||||
|
||||
#### Output
|
||||
@@ -396,7 +385,6 @@ Add an organization to a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `organizationId` | string | Yes | Organization ID to add to the service desk |
|
||||
|
||||
@@ -418,7 +406,6 @@ Get queues for a service desk in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `includeCount` | boolean | No | Include issue count for each queue \(true/false\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
@@ -447,7 +434,6 @@ Get SLA information for a service request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -475,7 +461,6 @@ Get available transitions for a service request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -501,7 +486,6 @@ Transition a service request to a new status in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `transitionId` | string | Yes | Transition ID to apply |
|
||||
| `comment` | string | No | Optional comment to add during transition |
|
||||
@@ -524,7 +508,6 @@ Get participants for a request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -552,7 +535,6 @@ Add participants to a request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `accountIds` | string | Yes | Comma-separated account IDs to add as participants |
|
||||
|
||||
@@ -578,7 +560,6 @@ Get approvals for a request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
@@ -615,7 +596,6 @@ Approve or decline an approval request in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `approvalId` | string | Yes | Approval ID to answer |
|
||||
| `decision` | string | Yes | Decision: "approve" or "decline" |
|
||||
@@ -653,7 +633,6 @@ Get the fields required to create a request of a specific type in Jira Service M
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `requestTypeId` | string | Yes | Request Type ID \(e.g., "10", "15"\) |
|
||||
|
||||
@@ -686,7 +665,6 @@ List forms (ProForma/JSM Forms) in a Jira project to discover form IDs for reque
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `projectIdOrKey` | string | Yes | Jira project ID or key \(e.g., "10001" or "SD"\) |
|
||||
|
||||
#### Output
|
||||
@@ -714,7 +692,6 @@ Get the full structure of a ProForma/JSM form including all questions, field typ
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `projectIdOrKey` | string | Yes | Jira project ID or key \(e.g., "10001" or "SD"\) |
|
||||
| `formId` | string | Yes | Form ID \(UUID from Get Form Templates\) |
|
||||
|
||||
@@ -738,7 +715,6 @@ List forms (ProForma/JSM Forms) attached to a Jira issue with metadata (name, su
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123", "10001"\) |
|
||||
|
||||
#### Output
|
||||
@@ -766,7 +742,6 @@ Attach a form template to an existing Jira issue or JSM request
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key to attach the form to \(e.g., "SD-123"\) |
|
||||
| `formTemplateId` | string | Yes | Form template UUID \(from Get Form Templates\) |
|
||||
|
||||
@@ -793,7 +768,6 @@ Save answers to a form attached to a Jira issue or JSM request
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID \(from Attach Form or Get Issue Forms\) |
|
||||
| `answers` | json | Yes | Form answers using numeric question IDs as keys \(e.g., \{"1": \{"text": "Title"\}, "4": \{"choices": \["5"\]\}\}\) |
|
||||
@@ -817,7 +791,6 @@ Submit a form on a Jira issue or JSM request, locking it from further edits
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID \(from Attach Form or Get Issue Forms\) |
|
||||
|
||||
@@ -839,7 +812,6 @@ Get a single form with full design, state, and answers from a Jira issue
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID \(from Attach Form or Get Issue Forms\) |
|
||||
|
||||
@@ -863,7 +835,6 @@ Get simplified answers from a form attached to a Jira issue or JSM request
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID \(from Attach Form or Get Issue Forms\) |
|
||||
|
||||
@@ -885,7 +856,6 @@ Reopen a submitted form on a Jira issue or JSM request, allowing further edits
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID \(from Get Issue Forms\) |
|
||||
|
||||
@@ -907,7 +877,6 @@ Remove a form from a Jira issue or JSM request
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID to delete |
|
||||
|
||||
@@ -929,7 +898,6 @@ Make a form visible to customers on a Jira issue or JSM request
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID |
|
||||
|
||||
@@ -951,7 +919,6 @@ Make a form internal only (not visible to customers) on a Jira issue or JSM requ
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., "SD-123"\) |
|
||||
| `formId` | string | Yes | Form instance UUID |
|
||||
|
||||
@@ -973,7 +940,6 @@ Copy forms from one Jira issue to another
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `sourceIssueIdOrKey` | string | Yes | Source issue ID or key to copy forms from \(e.g., "SD-123"\) |
|
||||
| `targetIssueIdOrKey` | string | Yes | Target issue ID or key to copy forms to \(e.g., "SD-456"\) |
|
||||
| `formIds` | json | No | Optional JSON array of form UUIDs to copy \(e.g., \["uuid1", "uuid2"\]\). If omitted, copies all forms. |
|
||||
@@ -997,7 +963,6 @@ List Assets (Insight/CMDB) object schemas in Jira Service Management
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `startAt` | number | No | Pagination start index \(e.g., 0, 50\) |
|
||||
| `maxResults` | number | No | Maximum schemas to return \(e.g., 25, 50\) |
|
||||
@@ -1028,7 +993,6 @@ Get a single Assets (Insight/CMDB) object schema by ID
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `schemaId` | string | Yes | The Assets object schema ID |
|
||||
|
||||
@@ -1055,7 +1019,6 @@ List object types within an Assets (Insight/CMDB) object schema
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `schemaId` | string | Yes | The Assets object schema ID to list object types for |
|
||||
| `excludeAbstract` | boolean | No | Exclude abstract object types from the result |
|
||||
@@ -1084,7 +1047,6 @@ Get the attribute definitions for an Assets (Insight/CMDB) object type. Use the
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `objectTypeId` | string | Yes | The Assets object type ID |
|
||||
| `onlyValueEditable` | boolean | No | Return only attributes whose values can be edited |
|
||||
@@ -1116,7 +1078,6 @@ Search Assets (Insight/CMDB) objects using AQL (Assets Query Language), e.g. obj
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `qlQuery` | string | Yes | AQL query string \(e.g., objectType = "Host" AND "Operating System" = "Ubuntu"\) |
|
||||
| `page` | number | No | Page number \(1-based, defaults to 1\) |
|
||||
@@ -1149,7 +1110,6 @@ Get a single Assets (Insight/CMDB) object by ID, including its attribute values
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `objectId` | string | Yes | The Assets object ID |
|
||||
|
||||
@@ -1179,7 +1139,6 @@ Create an Assets (Insight/CMDB) object of a given object type. Attributes use ob
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `objectTypeId` | string | Yes | The object type ID to create the object under |
|
||||
| `attributes` | json | Yes | Array of attributes: \[\{ objectTypeAttributeId, objectAttributeValues: \[\{ value \}\] \}\] |
|
||||
@@ -1210,7 +1169,6 @@ Update an existing Assets (Insight/CMDB) object. Provide the attributes to chang
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `objectId` | string | Yes | The Assets object ID to update |
|
||||
| `attributes` | json | Yes | Array of attributes to set: \[\{ objectTypeAttributeId, objectAttributeValues: \[\{ value \}\] \}\] |
|
||||
@@ -1242,7 +1200,6 @@ Delete an Assets (Insight/CMDB) object by ID
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `workspaceId` | string | No | Assets workspace ID \(resolved automatically when omitted\) |
|
||||
| `objectId` | string | Yes | The Assets object ID to delete |
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ Upload a file to a Jupyter server
|
||||
| `token` | string | Yes | Jupyter server authentication token |
|
||||
| `directory` | string | No | Destination directory, relative to the server root. Leave blank to upload to the root directory. |
|
||||
| `file` | file | No | The file to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Legacy: base64 encoded file content |
|
||||
| `fileName` | string | No | Optional filename override |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -108,7 +108,6 @@ Upload a file to Linq as a reusable attachment (max 100MB) and get an attachment
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Linq API key |
|
||||
| `file` | file | No | File to upload \(a UserFile from a file-upload field or a previous block\) |
|
||||
| `fileContent` | string | No | Legacy base64-encoded file content fallback |
|
||||
| `filename` | string | No | Override the file name \(defaults to the uploaded file name\) |
|
||||
| `contentType` | string | No | Override the MIME type \(defaults to the uploaded file type\) |
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ Update an existing record in a Microsoft Dataverse table. Only send the columns
|
||||
|
||||
### Upload File to Microsoft Dataverse
|
||||
|
||||
Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. The file content must be provided as a base64-encoded string.
|
||||
Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. Provide the file through the File input; its bytes are read from storage and sent as the raw request body.
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -399,7 +399,6 @@ Upload a file to a file or image column on a Dataverse record. Supports single-r
|
||||
| `fileColumn` | string | Yes | File or image column logical name \(e.g., entityimage, cr_document\) |
|
||||
| `fileName` | string | Yes | Name of the file being uploaded \(e.g., document.pdf\) |
|
||||
| `file` | file | No | File to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Base64-encoded file content \(legacy\) |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ Write data to a specific sheet in a Microsoft Excel spreadsheet
|
||||
| `cellRange` | string | No | The cell range to write to \(e.g., "A1:D10", "A1"\). Defaults to "A1" if not specified. |
|
||||
| `values` | array | Yes | The data to write as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
|
||||
| `valueInputOption` | string | No | The format of the data to write |
|
||||
| `includeValuesInResponse` | boolean | No | Whether to include the written values in the response |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -43,10 +43,7 @@ Parse PDF documents using Mistral OCR API
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `file` | file | Yes | Normalized UserFile from file upload or file reference |
|
||||
| `resultType` | string | No | Type of parsed result \(markdown, text, or json\). Defaults to markdown. |
|
||||
| `includeImageBase64` | boolean | No | Include base64-encoded images in the response |
|
||||
| `pages` | array | No | Specific pages to process \(array of page numbers, starting from 0\) |
|
||||
| `imageLimit` | number | No | Maximum number of images to extract from the PDF |
|
||||
| `imageMinSize` | number | No | Minimum height and width of images to extract from the PDF |
|
||||
| `apiKey` | string | Yes | Mistral API key \(MISTRAL_API_KEY\) |
|
||||
|
||||
#### Output
|
||||
@@ -62,15 +59,15 @@ Parse PDF documents using Mistral OCR API
|
||||
| ↳ `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\) |
|
||||
| ↳ `image_base64` | string | Base64-encoded image data; returned only when the hidden includeImageBase64 input is enabled |
|
||||
| ↳ `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\] |
|
||||
| ↳ `tables` | array | Separate table objects, referenced from the markdown via placeholders like \[tbl-0.html\]. Mistral populates these only when table_format is "markdown" or "html"; Sim never sets it, so tables stay inline in the markdown and this list is empty |
|
||||
| ↳ `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\) |
|
||||
| ↳ `header` | string | Page header content. Mistral returns it only when extract_header is true \(it defaults to false\); Sim never sets it, so this is not returned |
|
||||
| ↳ `footer` | string | Page footer content. Mistral returns it only when extract_footer is true \(it defaults to false\); Sim never sets it, so this is not returned |
|
||||
| `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 |
|
||||
|
||||
@@ -56,7 +56,6 @@ List one page of a NetSuite record collection, optionally filtered with a q expr
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `q` | string | No | NetSuite record collection filter expression |
|
||||
| `limit` | number | No | Results to return in this page \(1-1000; default 100\) |
|
||||
@@ -90,7 +89,6 @@ Retrieve one NetSuite record by internal or external ID.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `fields` | string | No | Comma-separated record fields to return |
|
||||
@@ -113,7 +111,6 @@ Create a NetSuite record using the account-specific record metadata schema.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `body` | json | Yes | Record fields matching the account-specific NetSuite metadata schema |
|
||||
| `replace` | string | No | Comma-separated sublists whose default lines should be replaced |
|
||||
@@ -135,7 +132,6 @@ Update fields on an existing NetSuite record with PATCH.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `body` | json | Yes | Record fields matching the account-specific NetSuite metadata schema |
|
||||
@@ -158,7 +154,6 @@ Create or update a NetSuite record by external ID with PUT.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `externalId` | string | Yes | External ID without the eid: prefix |
|
||||
| `body` | json | Yes | Record fields matching the account-specific NetSuite metadata schema |
|
||||
@@ -180,7 +175,6 @@ Delete one NetSuite record by internal or external ID.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
|
||||
@@ -200,7 +194,6 @@ Retrieve a record sublist, subrecord, referenced record, or nested subresource.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `subresourcePath` | string | Yes | Slash-separated subresource path, such as item or item/1/inventoryDetail |
|
||||
@@ -221,7 +214,6 @@ Return a prepopulated create form, or an edit form when a record ID is supplied.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | No | Existing record ID; omit to request a create form |
|
||||
| `body` | json | No | Record fields matching the account-specific NetSuite metadata schema |
|
||||
@@ -245,7 +237,6 @@ Retrieve valid select values for one or more fields on a new or existing record.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | No | Existing record ID; omit to evaluate options for a new record |
|
||||
| `fields` | string | Yes | Comma-separated select field IDs |
|
||||
@@ -273,7 +264,6 @@ Attach a contact or file to another NetSuite record.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `relatedType` | string | Yes | Related resource type: contact or file |
|
||||
@@ -297,7 +287,6 @@ Detach a contact or file from another NetSuite record.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `relatedType` | string | Yes | Related resource type: contact or file |
|
||||
@@ -319,7 +308,6 @@ Execute a supported NetSuite record action such as approve, reject, or confirm.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `action` | string | Yes | NetSuite record action ID without the @ prefix |
|
||||
@@ -342,7 +330,6 @@ Transform a supported source record into another NetSuite record type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `recordId` | string | Yes | NetSuite internal ID or an external-ID reference beginning with eid: |
|
||||
| `targetRecordType` | string | Yes | Target record type supported by the source record metadata |
|
||||
@@ -365,7 +352,6 @@ Submit an asynchronous request to retrieve up to 100 records of one type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `ids` | string | Yes | Up to 100 comma-separated internal IDs or eid: external-ID references |
|
||||
| `fields` | string | No | Comma-separated record fields to return |
|
||||
@@ -391,7 +377,6 @@ Submit an asynchronous batch that creates up to 100 records of one type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `items` | array | Yes | Array of 1-100 records matching the account-specific metadata schema |
|
||||
| `idempotencyKey` | string | No | Optional unique idempotency key for retrying the batch |
|
||||
@@ -414,7 +399,6 @@ Submit an asynchronous batch that updates up to 100 records of one type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `items` | array | Yes | Array of 1-100 records; every item must include an internal or external ID |
|
||||
| `idempotencyKey` | string | No | Optional unique idempotency key for retrying the batch |
|
||||
@@ -437,7 +421,6 @@ Submit an asynchronous batch that creates or updates up to 100 records by extern
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `items` | array | Yes | Array of 1-100 records; every item must include externalId |
|
||||
| `idempotencyKey` | string | No | Optional unique idempotency key for retrying the batch |
|
||||
@@ -460,7 +443,6 @@ Submit an asynchronous request to delete up to 100 records of one type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `ids` | string | Yes | Up to 100 comma-separated internal IDs or eid: external-ID references |
|
||||
| `idempotencyKey` | string | No | Optional unique idempotency key for retrying the batch |
|
||||
@@ -483,7 +465,6 @@ Execute one page of a SuiteQL query through SuiteTalk REST web services.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `query` | string | Yes | SuiteQL SELECT query; use a complete unique ORDER BY when retrieving multiple pages |
|
||||
| `limit` | number | No | Results to return in this page \(1-1000; default 100\) |
|
||||
| `offset` | number | No | Zero-based result offset; must be divisible by limit and stay within the first 100,000 results and 1,000 pages |
|
||||
@@ -512,7 +493,6 @@ List one page of SuiteAnalytics Workbook datasets available to the authenticated
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `limit` | number | No | Results to return in this page \(1-1000; default 100\) |
|
||||
| `offset` | number | No | Zero-based result offset; must be divisible by limit and stay within the first 100,000 results and 1,000 pages |
|
||||
|
||||
@@ -540,7 +520,6 @@ Execute one page of a standard or custom SuiteAnalytics Workbook dataset.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `datasetId` | string | Yes | SuiteAnalytics dataset script ID |
|
||||
| `limit` | number | No | Results to return in this page \(1-1000; default 100\) |
|
||||
| `offset` | number | No | Zero-based result offset; must be divisible by limit and stay within the first 100,000 results and 1,000 pages |
|
||||
@@ -569,7 +548,6 @@ List record types exposed to the authenticated role by the REST metadata catalog
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -596,7 +574,6 @@ Retrieve account-specific metadata for one NetSuite record type.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `recordType` | string | Yes | NetSuite REST record type script ID, such as customer or salesOrder |
|
||||
| `format` | string | No | Metadata representation: default, openapi, or json_schema |
|
||||
|
||||
@@ -616,7 +593,6 @@ Retrieve job status, list job tasks, or retrieve one task status.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `jobId` | string | Yes | Asynchronous job ID |
|
||||
| `view` | string | No | Retrieve job status, list tasks for the job, or retrieve one task status |
|
||||
| `taskId` | string | No | Task ID; required when view is task |
|
||||
@@ -654,7 +630,6 @@ Retrieve the provider response for one task within a completed asynchronous job.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
| `jobId` | string | Yes | Asynchronous job ID |
|
||||
| `taskId` | string | Yes | Task ID within the asynchronous job |
|
||||
|
||||
@@ -674,7 +649,6 @@ Retrieve the current UTC time from the NetSuite server.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -693,7 +667,6 @@ Retrieve REST web-services concurrency limits for the NetSuite account and integ
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | NetSuite OAuth 2.0 client-credentials service account |
|
||||
| `instanceUrl` | string | No | SuiteTalk account origin injected by the executor from the selected credential |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ Retrieve all deals from Pipedrive with optional filters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `status` | string | No | Only fetch deals with a specific status. Values: open, won, lost. If omitted, all not deleted deals are returned |
|
||||
| `person_id` | string | No | If supplied, only deals linked to the specified person are returned \(e.g., "456"\) |
|
||||
| `org_id` | string | No | If supplied, only deals linked to the specified organization are returned \(e.g., "789"\) |
|
||||
@@ -88,7 +87,6 @@ Retrieve detailed information about a specific deal
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `deal_id` | string | Yes | The ID of the deal to retrieve \(e.g., "123"\) |
|
||||
|
||||
#### Output
|
||||
@@ -106,7 +104,6 @@ Create a new deal in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `title` | string | Yes | The title of the deal \(e.g., "Enterprise Software License"\) |
|
||||
| `value` | string | No | The monetary value of the deal \(e.g., "5000"\) |
|
||||
| `currency` | string | No | Currency code \(e.g., "USD", "EUR", "GBP"\) |
|
||||
@@ -132,7 +129,6 @@ Update an existing deal in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `deal_id` | string | Yes | The ID of the deal to update \(e.g., "123"\) |
|
||||
| `title` | string | No | New title for the deal \(e.g., "Updated Enterprise License"\) |
|
||||
| `value` | string | No | New monetary value for the deal \(e.g., "7500"\) |
|
||||
@@ -155,7 +151,6 @@ Retrieve files from Pipedrive with optional filters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `sort` | string | No | Sort files by field \(supported: "id", "update_time"\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 100\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
@@ -190,7 +185,6 @@ Retrieve mail threads from Pipedrive mailbox
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `folder` | string | No | Filter by folder: inbox, drafts, sent, archive \(default: inbox\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "25", default: 50\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
@@ -213,7 +207,6 @@ Retrieve all messages from a specific mail thread
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `thread_id` | string | Yes | The ID of the mail thread \(e.g., "12345"\) |
|
||||
|
||||
#### Output
|
||||
@@ -232,7 +225,6 @@ Retrieve all pipelines from Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `sort_by` | string | No | Field to sort by: id, update_time, add_time \(default: id\) |
|
||||
| `sort_direction` | string | No | Sorting direction: asc, desc \(default: asc\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
@@ -264,7 +256,6 @@ Retrieve all deals in a specific pipeline
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `pipeline_id` | string | Yes | The ID of the pipeline \(e.g., "1"\) |
|
||||
| `stage_id` | string | No | Filter by specific stage within the pipeline \(e.g., "2"\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
@@ -286,7 +277,6 @@ Retrieve all projects or a specific project from Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `project_id` | string | No | Optional: ID of a specific project to retrieve \(e.g., "123"\) |
|
||||
| `status` | string | No | Filter by project status: open, completed, deleted \(only for listing all\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500, only for listing all\) |
|
||||
@@ -311,7 +301,6 @@ Create a new project in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `title` | string | Yes | The title of the project \(e.g., "Q2 Marketing Campaign"\) |
|
||||
| `description` | string | No | Description of the project |
|
||||
| `start_date` | string | No | Project start date in YYYY-MM-DD format \(e.g., "2025-04-01"\) |
|
||||
@@ -332,7 +321,6 @@ Retrieve activities (tasks) from Pipedrive with optional filters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `user_id` | string | No | Filter activities by user ID \(e.g., "123"\) |
|
||||
| `type` | string | No | Filter by activity type \(call, meeting, task, deadline, email, lunch\) |
|
||||
| `done` | string | No | Filter by completion status: 0 for not done, 1 for done |
|
||||
@@ -370,7 +358,6 @@ Create a new activity (task) in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `subject` | string | Yes | The subject/title of the activity \(e.g., "Follow up call with John"\) |
|
||||
| `type` | string | Yes | Activity type: call, meeting, task, deadline, email, lunch |
|
||||
| `due_date` | string | Yes | Due date in YYYY-MM-DD format \(e.g., "2025-03-15"\) |
|
||||
@@ -396,7 +383,6 @@ Update an existing activity (task) in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `activity_id` | string | Yes | The ID of the activity to update \(e.g., "12345"\) |
|
||||
| `subject` | string | No | New subject/title for the activity \(e.g., "Updated meeting with client"\) |
|
||||
| `due_date` | string | No | New due date in YYYY-MM-DD format \(e.g., "2025-03-20"\) |
|
||||
@@ -420,7 +406,6 @@ Retrieve all leads or a specific lead from Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `lead_id` | string | No | Optional: ID of a specific lead to retrieve \(e.g., "abc123-def456-ghi789"\) |
|
||||
| `archived` | string | No | Get archived leads instead of active ones \(e.g., "true" or "false"\) |
|
||||
| `owner_id` | string | No | Filter by owner user ID \(e.g., "123"\) |
|
||||
@@ -474,7 +459,6 @@ Create a new lead in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `title` | string | Yes | The name of the lead \(e.g., "Acme Corp - Website Redesign"\) |
|
||||
| `person_id` | string | No | ID of the person \(REQUIRED unless organization_id is provided\) \(e.g., "456"\) |
|
||||
| `organization_id` | string | No | ID of the organization \(REQUIRED unless person_id is provided\) \(e.g., "789"\) |
|
||||
@@ -499,7 +483,6 @@ Update an existing lead in Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `lead_id` | string | Yes | The ID of the lead to update \(e.g., "abc123-def456-ghi789"\) |
|
||||
| `title` | string | No | New name for the lead \(e.g., "Updated Lead - Premium Package"\) |
|
||||
| `person_id` | string | No | New person ID \(e.g., "456"\) |
|
||||
@@ -525,7 +508,6 @@ Delete a specific lead from Pipedrive
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authStyle` | string | No | Auth scheme for the token; set by the credential resolver for API-token service accounts |
|
||||
| `lead_id` | string | Yes | The ID of the lead to delete \(e.g., "abc123-def456-ghi789"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -47,9 +47,6 @@ Parse documents (PDF, images, Office docs) using Pulse OCR API
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `file` | file | Yes | Document to be processed |
|
||||
| `pages` | string | No | Page range to process \(1-indexed, e.g., "1-2,5"\) |
|
||||
| `extractFigure` | boolean | No | Enable figure extraction from the document |
|
||||
| `figureDescription` | boolean | No | Generate descriptions/captions for extracted figures |
|
||||
| `returnHtml` | boolean | No | Include HTML in the response |
|
||||
| `chunking` | string | No | Chunking strategies \(comma-separated: semantic, header, page, recursive\) |
|
||||
| `chunkSize` | number | No | Maximum characters per chunk when chunking is enabled |
|
||||
| `apiKey` | string | Yes | Pulse API key |
|
||||
@@ -63,9 +60,9 @@ Parse documents (PDF, images, Office docs) using Pulse OCR API
|
||||
| `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 |
|
||||
| `html` | string | HTML content; returned only when the hidden returnHtml input is enabled |
|
||||
| `structured_output` | json | Structured output; Sim exposes no input for supplying a schema, so this is always null |
|
||||
| `chunks` | json | Chunked content if chunking was enabled |
|
||||
| `figures` | json | Extracted figures if figure extraction was enabled |
|
||||
| `figures` | json | Extracted figures; returned only when the hidden extractFigure input is enabled |
|
||||
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ Retrieve accounts from Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | The ID token from Salesforce OAuth \(contains instance URL\) |
|
||||
| `instanceUrl` | string | No | The Salesforce instance URL |
|
||||
| `limit` | string | No | Maximum number of results \(default: 100, max: 2000\) |
|
||||
| `fields` | string | No | Comma-separated field API names \(e.g., "Id,Name,Industry,Phone"\) |
|
||||
| `orderBy` | string | No | Field and direction for sorting \(e.g., "Name ASC" or "CreatedDate DESC"\) |
|
||||
@@ -71,8 +69,6 @@ Create a new account in Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `name` | string | Yes | Account name \(required\) |
|
||||
| `type` | string | No | Account type \(e.g., Customer, Partner, Prospect\) |
|
||||
| `industry` | string | No | Industry \(e.g., Technology, Healthcare, Finance\) |
|
||||
@@ -105,8 +101,6 @@ Update an existing account in Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `accountId` | string | Yes | Salesforce Account ID to update \(18-character string starting with 001\) |
|
||||
| `name` | string | No | Account name |
|
||||
| `type` | string | No | Account type \(e.g., Customer, Partner, Prospect\) |
|
||||
@@ -139,8 +133,6 @@ Delete an account from Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `accountId` | string | Yes | Salesforce Account ID to delete \(18-character string starting with 001\) |
|
||||
|
||||
#### Output
|
||||
@@ -160,8 +152,6 @@ Get contact(s) from Salesforce - single contact if ID provided, or list if not
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `contactId` | string | No | Salesforce Contact ID \(18-character string starting with 003\) to get a single contact |
|
||||
| `limit` | string | No | Maximum number of results \(default: 100, max: 2000\). Only for list query. |
|
||||
| `fields` | string | No | Comma-separated field API names \(e.g., "Id,FirstName,LastName,Email,Phone"\) |
|
||||
@@ -193,8 +183,6 @@ Create a new contact in Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `lastName` | string | Yes | Last name \(required\) |
|
||||
| `firstName` | string | No | First name |
|
||||
| `email` | string | No | Email address |
|
||||
@@ -227,8 +215,6 @@ Update an existing contact in Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `contactId` | string | Yes | Salesforce Contact ID to update \(18-character string starting with 003\) |
|
||||
| `lastName` | string | No | Last name |
|
||||
| `firstName` | string | No | First name |
|
||||
@@ -261,8 +247,6 @@ Delete a contact from Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `contactId` | string | Yes | Salesforce Contact ID to delete \(18-character string starting with 003\) |
|
||||
|
||||
#### Output
|
||||
@@ -282,8 +266,6 @@ Retrieve lead(s) from Salesforce CRM
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `leadId` | string | No | Salesforce Lead ID \(18-character string starting with 00Q\) to get a single lead |
|
||||
| `limit` | string | No | Maximum number of results to return \(default: 100\) |
|
||||
| `fields` | string | No | Comma-separated list of field API names to return |
|
||||
@@ -315,8 +297,6 @@ Create a new lead
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `lastName` | string | Yes | Last name \(required\) |
|
||||
| `company` | string | Yes | Company name \(required\) |
|
||||
| `firstName` | string | No | First name |
|
||||
@@ -345,8 +325,6 @@ Update an existing lead
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `leadId` | string | Yes | Salesforce Lead ID to update \(18-character string starting with 00Q\) |
|
||||
| `lastName` | string | No | Last name |
|
||||
| `company` | string | No | Company name |
|
||||
@@ -375,8 +353,6 @@ Delete a lead
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `leadId` | string | Yes | Salesforce Lead ID to delete \(18-character string starting with 00Q\) |
|
||||
|
||||
#### Output
|
||||
@@ -396,8 +372,6 @@ Get opportunity(ies) from Salesforce
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `opportunityId` | string | No | Salesforce Opportunity ID \(18-character string starting with 006\) to get a single opportunity |
|
||||
| `limit` | string | No | Maximum number of results to return \(default: 100\) |
|
||||
| `fields` | string | No | Comma-separated list of field API names to return |
|
||||
@@ -428,8 +402,6 @@ Create a new opportunity
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `name` | string | Yes | Opportunity name \(required\) |
|
||||
| `stageName` | string | Yes | Stage name \(required, e.g., Prospecting, Qualification, Closed Won\) |
|
||||
| `closeDate` | string | Yes | Close date in YYYY-MM-DD format \(required\) |
|
||||
@@ -456,8 +428,6 @@ Update an existing opportunity
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `opportunityId` | string | Yes | Salesforce Opportunity ID to update \(18-character string starting with 006\) |
|
||||
| `name` | string | No | Opportunity name |
|
||||
| `stageName` | string | No | Stage name \(e.g., Prospecting, Qualification, Closed Won\) |
|
||||
@@ -484,8 +454,6 @@ Delete an opportunity
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `opportunityId` | string | Yes | Salesforce Opportunity ID to delete \(18-character string starting with 006\) |
|
||||
|
||||
#### Output
|
||||
@@ -505,8 +473,6 @@ Get case(s) from Salesforce
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `caseId` | string | No | Salesforce Case ID \(18-character string starting with 500\) to get a single case |
|
||||
| `limit` | string | No | Maximum number of results to return \(default: 100\) |
|
||||
| `fields` | string | No | Comma-separated list of field API names to return |
|
||||
@@ -537,8 +503,6 @@ Create a new case
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `subject` | string | Yes | Case subject \(required\) |
|
||||
| `status` | string | No | Status \(e.g., New, Working, Escalated\) |
|
||||
| `priority` | string | No | Priority \(e.g., Low, Medium, High\) |
|
||||
@@ -565,8 +529,6 @@ Update an existing case
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `caseId` | string | Yes | Salesforce Case ID to update \(18-character string starting with 500\) |
|
||||
| `subject` | string | No | Case subject |
|
||||
| `status` | string | No | Status \(e.g., New, Working, Escalated, Closed\) |
|
||||
@@ -593,8 +555,6 @@ Delete a case
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `caseId` | string | Yes | Salesforce Case ID to delete \(18-character string starting with 500\) |
|
||||
|
||||
#### Output
|
||||
@@ -614,8 +574,6 @@ Get task(s) from Salesforce
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `taskId` | string | No | Salesforce Task ID \(18-character string starting with 00T\) to get a single task |
|
||||
| `limit` | string | No | Maximum number of results to return \(default: 100\) |
|
||||
| `fields` | string | No | Comma-separated list of field API names to return |
|
||||
@@ -646,8 +604,6 @@ Create a new task
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `subject` | string | Yes | Task subject \(required\) |
|
||||
| `status` | string | No | Status \(e.g., Not Started, In Progress, Completed\) |
|
||||
| `priority` | string | No | Priority \(e.g., Low, Normal, High\) |
|
||||
@@ -674,8 +630,6 @@ Update an existing task
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `taskId` | string | Yes | Salesforce Task ID to update \(18-character string starting with 00T\) |
|
||||
| `subject` | string | No | Task subject |
|
||||
| `status` | string | No | Status \(e.g., Not Started, In Progress, Completed\) |
|
||||
@@ -702,8 +656,6 @@ Delete a task
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `taskId` | string | Yes | Salesforce Task ID to delete \(18-character string starting with 00T\) |
|
||||
|
||||
#### Output
|
||||
@@ -723,8 +675,6 @@ Get a list of up to 200 recently viewed reports for the current user
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `searchTerm` | string | No | Filter reports by name \(case-insensitive partial match\) |
|
||||
|
||||
#### Output
|
||||
@@ -745,8 +695,6 @@ Get the describe (definition and metadata) for a specific report
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `reportId` | string | Yes | Salesforce Report ID \(18-character string starting with 00O\) |
|
||||
|
||||
#### Output
|
||||
@@ -767,8 +715,6 @@ Execute a report and retrieve the results
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `reportId` | string | Yes | Salesforce Report ID \(18-character string starting with 00O\) |
|
||||
| `includeDetails` | string | No | Include detail rows \(true/false, default: true\) |
|
||||
| `filters` | string | No | JSON array of report filter objects to apply |
|
||||
@@ -799,8 +745,6 @@ Get a list of available report types
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -820,8 +764,6 @@ Get a list of recently used dashboards for the current user
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -841,8 +783,6 @@ Get details and results for a specific dashboard
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `dashboardId` | string | Yes | Salesforce Dashboard ID \(18-character string starting with 01Z\) |
|
||||
|
||||
#### Output
|
||||
@@ -867,8 +807,6 @@ Refresh a dashboard to get the latest data
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `dashboardId` | string | Yes | Salesforce Dashboard ID \(18-character string starting with 01Z\) |
|
||||
|
||||
#### Output
|
||||
@@ -894,8 +832,6 @@ Execute a custom SOQL query to retrieve data from Salesforce
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `query` | string | Yes | SOQL query to execute \(e.g., SELECT Id, Name FROM Account LIMIT 10\) |
|
||||
|
||||
#### Output
|
||||
@@ -919,8 +855,6 @@ Retrieve additional query results using the nextRecordsUrl from a previous query
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `nextRecordsUrl` | string | Yes | The nextRecordsUrl value from a previous query response \(e.g., /services/data/v59.0/query/01g...\) |
|
||||
|
||||
#### Output
|
||||
@@ -943,8 +877,6 @@ Get metadata and field information for a Salesforce object
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `objectName` | string | Yes | Salesforce object API name \(e.g., Account, Contact, Lead, Custom_Object__c\) |
|
||||
|
||||
#### Output
|
||||
@@ -998,8 +930,6 @@ Get a list of all available Salesforce objects
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1037,8 +967,6 @@ Create a custom field on a Salesforce object (e.g., Account) using the Tooling A
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `objectName` | string | Yes | API name of the object to add the field to \(e.g., Account, Contact, Lead, MyObject__c\) |
|
||||
| `fieldName` | string | Yes | API name of the new field; the __c suffix is added automatically \(e.g., Region\) |
|
||||
| `label` | string | No | Display label shown in the UI \(defaults to the field name when omitted\) |
|
||||
@@ -1074,8 +1002,6 @@ Update an existing custom field on a Salesforce object using the Tooling API
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `fieldId` | string | Yes | Tooling API Id of the custom field to update \(find it via the Tooling Query tool\) |
|
||||
| `label` | string | No | Display label shown in the UI |
|
||||
| `length` | number | No | Maximum length for Text, LongTextArea, Html, or MultiselectPicklist fields |
|
||||
@@ -1107,8 +1033,6 @@ Delete a custom field from a Salesforce object using the Tooling API
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `fieldId` | string | Yes | Tooling API Id of the custom field to delete \(find it via the Tooling Query tool\) |
|
||||
|
||||
#### Output
|
||||
@@ -1128,8 +1052,6 @@ Create a custom object in Salesforce using the Tooling API
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `objectName` | string | Yes | API name of the new object; the __c suffix is added automatically \(e.g., Project\) |
|
||||
| `label` | string | Yes | Singular display label for the object \(e.g., Project\) |
|
||||
| `pluralLabel` | string | Yes | Plural display label for the object \(e.g., Projects\) |
|
||||
@@ -1156,8 +1078,6 @@ Execute a SOQL query against the Tooling API to inspect metadata objects
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `idToken` | string | No | No description |
|
||||
| `instanceUrl` | string | No | No description |
|
||||
| `query` | string | Yes | Tooling SOQL query \(e.g., SELECT Id, DeveloperName FROM CustomField WHERE TableEnumOrId = 'Account'\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -163,7 +163,6 @@ Create and share Slack canvases in channels. Canvases are collaborative document
|
||||
| `channel` | string | Yes | Slack channel ID \(e.g., C1234567890\) |
|
||||
| `title` | string | Yes | Title of the canvas |
|
||||
| `content` | string | Yes | Canvas content in markdown format |
|
||||
| `document_content` | object | No | Structured canvas document content |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ Execute one parameterized SQL statement through the Snowflake SQL API.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -93,7 +92,6 @@ Check a running or completed statement and retrieve exactly one result partition
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `statementHandle` | string | Yes | Statement handle returned by Snowflake |
|
||||
| `partition` | number | No | Zero-based result partition to retrieve; defaults to 0 |
|
||||
| `partitionCount` | number | No | Total number of result partitions, taken from the partitionCount of the first partition. Snowflake omits metadata from every later partition response, so supply this when fetching partition 1 or higher to keep truncated and nextPartition accurate |
|
||||
@@ -135,7 +133,6 @@ Cancel a running Snowflake SQL API statement.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `statementHandle` | string | Yes | Statement handle returned by Snowflake |
|
||||
|
||||
#### Output
|
||||
@@ -175,7 +172,6 @@ Insert structured JSON rows using bound values.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -221,7 +217,6 @@ Update matching rows with a bound MERGE statement without inserting new rows.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -268,7 +263,6 @@ Update matching rows and insert unmatched rows with a bound MERGE statement.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -315,7 +309,6 @@ Delete rows matching a required set of bound column filters.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -361,7 +354,6 @@ Load files from an existing Snowflake stage with COPY INTO.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -414,7 +406,6 @@ Export a Snowflake table to files in a stage with COPY INTO.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -466,7 +457,6 @@ List the databases the credential can access.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `nameLike` | string | No | Optional SQL LIKE pattern for object names |
|
||||
@@ -509,7 +499,6 @@ List the schemas in a Snowflake database.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -553,7 +542,6 @@ List the tables in a Snowflake schema.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -598,7 +586,6 @@ List warehouses visible to the active Snowflake role.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `maxRows` | number | No | Maximum result rows; defaults to 1000 with a Sim safety limit of 10000 |
|
||||
@@ -641,7 +628,6 @@ Get the full details for a Snowflake virtual warehouse.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouseName` | string | Yes | Warehouse name |
|
||||
@@ -683,7 +669,6 @@ Resume a Snowflake virtual warehouse if it is suspended.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouseName` | string | Yes | Warehouse name |
|
||||
@@ -725,7 +710,6 @@ Suspend a Snowflake virtual warehouse.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouseName` | string | Yes | Warehouse name |
|
||||
@@ -767,7 +751,6 @@ Resize a Snowflake warehouse or change its auto-suspend and auto-resume settings
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouseName` | string | Yes | Warehouse name |
|
||||
@@ -812,7 +795,6 @@ List tasks in a Snowflake schema.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -857,7 +839,6 @@ Describe a Snowflake task.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -901,7 +882,6 @@ Run a Snowflake task immediately, optionally retrying its last failed graph.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -946,7 +926,6 @@ Resume a suspended Snowflake task so its schedule runs again.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -990,7 +969,6 @@ Suspend a Snowflake task so its schedule stops triggering runs.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `database` | string | Yes | Database name |
|
||||
@@ -1034,7 +1012,6 @@ Query up to seven days of Snowflake task history, capped at 10000 rows.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1081,7 +1058,6 @@ Find one task history record by query ID within Snowflake’s seven-day window a
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1127,7 +1103,6 @@ Cancel one running task query by query ID with SYSTEM$CANCEL_QUERY. Task runs al
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1170,7 +1145,6 @@ Read a task query result with RESULT_SCAN during Snowflake’s 24-hour retention
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1214,7 +1188,6 @@ List queries that completed in the last seven days, optionally filtered.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1262,7 +1235,6 @@ List staged-file load results for a table over the last fourteen days.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1310,7 +1282,6 @@ Inspect table and column metadata through Snowflake INFORMATION_SCHEMA views.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
@@ -1357,7 +1328,6 @@ Call a stored procedure with explicitly typed Snowflake bindings.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `oauthCredential` | string | Yes | Snowflake credential \(account host and programmatic access token\) |
|
||||
| `domain` | string | No | Snowflake account host injected by the executor from the selected credential |
|
||||
| `role` | string | No | Snowflake role to use for this statement |
|
||||
| `statementTimeoutSeconds` | number | No | Statement timeout in seconds; 0 uses Snowflake maximum of 604800 seconds |
|
||||
| `warehouse` | string | No | Warehouse to use for this statement; defaults to the PAT user setting |
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="tinyfish"
|
||||
color="#FF6700"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
|
||||
@@ -330,9 +330,8 @@ Upload an evidence file to a Vanta document. Requires credentials with the vanta
|
||||
| `region` | string | No | Vanta API region: "us" \(api.vanta.com, default\) or "gov" \(api.vanta-gov.com\) |
|
||||
| `documentId` | string | Yes | Unique ID of the document to attach the file to |
|
||||
| `file` | file | No | The evidence file to upload |
|
||||
| `fileContent` | string | No | Base64-encoded file content \(alternative to file\) |
|
||||
| `fileName` | string | No | Optional file name override |
|
||||
| `mimeType` | string | No | MIME type of the file \(e.g., application/pdf\); used when uploading base64 content, since uploaded files already carry their own type |
|
||||
| `mimeType` | string | No | MIME type of the file \(e.g., application/pdf\). Used only for base64 uploads; ignored for a file from the File input, whose content type is always resolved from storage. |
|
||||
| `description` | string | No | Description of the uploaded evidence \(e.g., "Q3 access review evidence"\) |
|
||||
| `effectiveAtDate` | string | No | ISO 8601 date indicating when the document is effective from |
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ List tickets from a Zoho Desk organization with optional filters. Returns a list
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `from` | number | No | Pagination start index \(0-based\) |
|
||||
| `limit` | number | No | Number of tickets to return \(1-100\) |
|
||||
@@ -111,7 +110,6 @@ Retrieve a single Zoho Desk ticket by ID.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID to retrieve |
|
||||
| `include` | string | No | Comma-separated related data to embed. Allowed: contacts, products, assignee, departments, contract, isRead, team, skills |
|
||||
@@ -162,7 +160,6 @@ Update fields on an existing Zoho Desk ticket.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID to update |
|
||||
| `subject` | string | No | Ticket subject |
|
||||
@@ -224,7 +221,6 @@ List comments on a Zoho Desk ticket.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID |
|
||||
| `from` | number | No | Pagination start index \(0-based\) |
|
||||
@@ -267,7 +263,6 @@ Add a comment to a Zoho Desk ticket.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID |
|
||||
| `content` | string | Yes | Comment content |
|
||||
@@ -309,7 +304,6 @@ List conversation threads on a Zoho Desk ticket, newest first (Zoho sorts by sen
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID |
|
||||
| `from` | number | No | Pagination start index \(0-based\) |
|
||||
@@ -367,7 +361,6 @@ Retrieve the full content of a single Zoho Desk ticket thread.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `ticketId` | string | Yes | Ticket ID |
|
||||
| `threadId` | string | Yes | Thread ID |
|
||||
@@ -423,7 +416,6 @@ Retrieve a Zoho Desk contact by ID.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `contactId` | string | Yes | Contact ID to retrieve |
|
||||
| `include` | string | No | Comma-separated related data to embed. Allowed: accounts, owner |
|
||||
@@ -460,7 +452,6 @@ Download a Zoho Desk ticket attachment (from its href) as a file.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
| `orgId` | string | Yes | Zoho Desk organization ID |
|
||||
| `href` | string | Yes | Attachment download href \(from a thread or comment attachment\) |
|
||||
| `fileName` | string | No | Optional file name for the downloaded file |
|
||||
@@ -479,7 +470,6 @@ List the Zoho Desk organizations (portals) the connected account can access.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiDomain` | string | No | Zoho Desk data-center REST base URL |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -141,7 +141,11 @@ export const PulseBlock: BlockConfig<PulseParserOutput> = {
|
||||
bounding_boxes: { type: 'json', description: 'Bounding box layout information' },
|
||||
extraction_url: { type: 'string', description: 'URL for extraction results (large documents)' },
|
||||
html: { type: 'string', description: 'HTML content if requested' },
|
||||
structured_output: { type: 'json', description: 'Structured output if schema was provided' },
|
||||
structured_output: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Structured output; Sim exposes no input for supplying a schema, so this is always null',
|
||||
},
|
||||
chunks: { type: 'json', description: 'Chunked content if chunking was enabled' },
|
||||
figures: { type: 'json', description: 'Extracted figures if figure extraction was enabled' },
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ export const TinyFishBlock: BlockConfig<TinyFishRunResponse> = {
|
||||
docsLink: 'https://docs.sim.ai/integrations/tinyfish',
|
||||
category: 'tools',
|
||||
integrationType: IntegrationType.AI,
|
||||
bgColor: '#FF6700',
|
||||
bgColor: '#FFFFFF',
|
||||
icon: TinyFishIcon,
|
||||
canvasPresentation: {
|
||||
defaultTitle: 'TinyFish',
|
||||
|
||||
@@ -398,6 +398,28 @@ describe('getScopeDescription', () => {
|
||||
expect(getScopeDescription('account', 'reddit')).toBe('Update account preferences and settings')
|
||||
expect(getScopeDescription('account')).toBe('Update account preferences and settings')
|
||||
})
|
||||
|
||||
/**
|
||||
* The consent screen is where a user decides what to grant, so a write scope
|
||||
* has to read as one. `w_member_social` previously said 'Access LinkedIn
|
||||
* profile', describing a posting grant as a profile read.
|
||||
*
|
||||
* The wording tracks LinkedIn's own: "Post, comment, and like posts on behalf
|
||||
* of an authenticated member." It names all three verbs even though Sim only
|
||||
* posts -- the label describes the grant the token carries, not Sim's current
|
||||
* use of it, and LinkedIn's scopes cannot be sub-selected.
|
||||
*/
|
||||
it.concurrent('describes w_member_social as the write grant it is', () => {
|
||||
const description = getScopeDescription('w_member_social', 'linkedin')
|
||||
|
||||
expect(description).toBe('Post, comment, and like posts on your behalf')
|
||||
expect(description).not.toMatch(/access .*profile/i)
|
||||
})
|
||||
|
||||
it.concurrent('leaves the read-only LinkedIn scopes read-only', () => {
|
||||
expect(getScopeDescription('profile', 'linkedin')).toBe('Access profile information')
|
||||
expect(getScopeDescription('email', 'linkedin')).toBe('Access email address')
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseProvider', () => {
|
||||
|
||||
@@ -385,7 +385,7 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'webhooks:full': 'Full access to manage Pipedrive webhooks',
|
||||
|
||||
// LinkedIn scopes
|
||||
w_member_social: 'Access LinkedIn profile',
|
||||
w_member_social: 'Post, comment, and like posts on your behalf',
|
||||
|
||||
// Instagram scopes (Business Login for Instagram)
|
||||
instagram_business_basic: 'Access Instagram professional profile and media',
|
||||
|
||||
@@ -17,7 +17,6 @@ function runShell(source: string) {
|
||||
.replace(/^<!DOCTYPE html>\n<html lang="en">/, '')
|
||||
.replace(/<\/html>$/, '')
|
||||
const script = SIM_ARTIFACT_SHELL.replace(/^<script>/, '').replace(/<\/script>$/, '')
|
||||
// biome-ignore lint: test harness executes the shell inline
|
||||
new Function(script)()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
extractDaytonaError,
|
||||
mapDaytonaSandbox,
|
||||
parseDaytonaJson,
|
||||
resolveSandboxId,
|
||||
} from '@/tools/daytona/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -48,7 +49,7 @@ export const daytonaDeleteSandboxTool: ToolConfig<
|
||||
const data = await parseDaytonaJson(response)
|
||||
const sandbox = mapDaytonaSandbox(data)
|
||||
if (!sandbox.id && params) {
|
||||
sandbox.id = params.sandboxId.trim()
|
||||
sandbox.id = resolveSandboxId(params.sandboxId)
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -180,3 +180,45 @@ describe('encodeSandboxId', () => {
|
||||
expect(encodeSandboxId(sandboxId)).toBe(sandboxId)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* The lifecycle tools echo `sandboxId` back as the output id when the API
|
||||
* returns no body. That runs in `transformResponse`, so the sandbox has already
|
||||
* been started, stopped, or irreversibly deleted by the time it executes — a
|
||||
* `.trim()` on a non-string id would surface the successful call as an unnamed
|
||||
* `TypeError`. `sandboxId` is declared `type: 'string'` but arrives unvalidated,
|
||||
* and `safeUrlPathSegment` now accepts a numeric id, so a number reaches here.
|
||||
*/
|
||||
describe('the sandbox id echoed back by the lifecycle tools', () => {
|
||||
const lifecycleTools = [
|
||||
daytonaTools.daytonaStartSandboxTool,
|
||||
daytonaTools.daytonaStopSandboxTool,
|
||||
daytonaTools.daytonaDeleteSandboxTool,
|
||||
] as AnyTool[]
|
||||
|
||||
const emptyBody = () => new Response('', { status: 200 })
|
||||
|
||||
it.each(lifecycleTools.map((tool) => [tool.id, tool] as const))(
|
||||
'%s reports a numeric sandboxId instead of throwing after the request went out',
|
||||
async (_id, tool) => {
|
||||
const result = await tool.transformResponse!(emptyBody(), {
|
||||
apiKey: 'k',
|
||||
sandboxId: 12345 as unknown as string,
|
||||
})
|
||||
|
||||
expect(result.output.sandbox.id).toBe('12345')
|
||||
}
|
||||
)
|
||||
|
||||
it.each(lifecycleTools.map((tool) => [tool.id, tool] as const))(
|
||||
'%s still trims a string sandboxId',
|
||||
async (_id, tool) => {
|
||||
const result = await tool.transformResponse!(emptyBody(), {
|
||||
apiKey: 'k',
|
||||
sandboxId: ' sbx_abc123 ',
|
||||
})
|
||||
|
||||
expect(result.output.sandbox.id).toBe('sbx_abc123')
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
extractDaytonaError,
|
||||
mapDaytonaSandbox,
|
||||
parseDaytonaJson,
|
||||
resolveSandboxId,
|
||||
} from '@/tools/daytona/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -48,7 +49,7 @@ export const daytonaStartSandboxTool: ToolConfig<
|
||||
const data = await parseDaytonaJson(response)
|
||||
const sandbox = mapDaytonaSandbox(data)
|
||||
if (!sandbox.id && params) {
|
||||
sandbox.id = params.sandboxId.trim()
|
||||
sandbox.id = resolveSandboxId(params.sandboxId)
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
extractDaytonaError,
|
||||
mapDaytonaSandbox,
|
||||
parseDaytonaJson,
|
||||
resolveSandboxId,
|
||||
} from '@/tools/daytona/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -46,7 +47,7 @@ export const daytonaStopSandboxTool: ToolConfig<DaytonaStopSandboxParams, Dayton
|
||||
const data = await parseDaytonaJson(response)
|
||||
const sandbox = mapDaytonaSandbox(data)
|
||||
if (!sandbox.id && params) {
|
||||
sandbox.id = params.sandboxId.trim()
|
||||
sandbox.id = resolveSandboxId(params.sandboxId)
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -18,6 +18,25 @@ export function encodeSandboxId(sandboxId: string): string {
|
||||
return safeUrlPathSegment(sandboxId, 'Sandbox ID')
|
||||
}
|
||||
|
||||
/**
|
||||
* The sandbox identifier as it should read back in a tool's output, for the
|
||||
* lifecycle responses that return no body of their own.
|
||||
*
|
||||
* `sandboxId` is declared `type: 'string'`, but the value arrives unvalidated
|
||||
* from an LLM tool call or stored workflow state and a numeric-looking id can
|
||||
* land as a JSON number — the same widening {@link safeUrlPathSegment} accepts.
|
||||
* This runs inside `transformResponse`, so the request has *already* been sent:
|
||||
* a bare `.trim()` here would throw an unnamed `TypeError` after the sandbox was
|
||||
* started, stopped, or irreversibly deleted. Stringifying keeps the call's
|
||||
* result reportable.
|
||||
*
|
||||
* Only reached for a value {@link encodeSandboxId} already accepted, so the
|
||||
* rejection of unusable shapes stays where it belongs — before the request.
|
||||
*/
|
||||
export function resolveSandboxId(sandboxId: string | number | bigint): string {
|
||||
return String(sandboxId).trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a toolbox API URL for a sandbox-scoped endpoint.
|
||||
*/
|
||||
|
||||
@@ -135,29 +135,33 @@ export const SEARCH_RESULT_OUTPUT_PROPERTIES = {
|
||||
url: { type: 'string', description: 'URL of the search result' },
|
||||
markdown: {
|
||||
type: 'string',
|
||||
description: 'Page content in markdown (when scrapeOptions.formats includes "markdown")',
|
||||
description:
|
||||
'Page content in markdown; returned only when scraping was requested via the hidden scrapeOptions input',
|
||||
optional: true,
|
||||
},
|
||||
html: {
|
||||
type: 'string',
|
||||
description: 'Processed HTML content (when scrapeOptions.formats includes "html")',
|
||||
description:
|
||||
'Processed HTML content; returned only when "html" is among the scrape formats requested via the hidden scrapeOptions input',
|
||||
optional: true,
|
||||
},
|
||||
rawHtml: {
|
||||
type: 'string',
|
||||
description: 'Unprocessed raw HTML (when scrapeOptions.formats includes "rawHtml")',
|
||||
description:
|
||||
'Unprocessed raw HTML; returned only when "rawHtml" is among the scrape formats requested via the hidden scrapeOptions input',
|
||||
optional: true,
|
||||
},
|
||||
links: {
|
||||
type: 'array',
|
||||
description: 'Links found on the page (when scrapeOptions.formats includes "links")',
|
||||
description:
|
||||
'Links found on the page; returned only when "links" is among the scrape formats requested via the hidden scrapeOptions input',
|
||||
optional: true,
|
||||
items: { type: 'string', description: 'URL found on the page' },
|
||||
},
|
||||
screenshot: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Screenshot URL (expires after 24 hours, when scrapeOptions.formats includes "screenshot")',
|
||||
'Screenshot URL (expires after 24 hours); returned only when "screenshot" is among the scrape formats requested via the hidden scrapeOptions input',
|
||||
optional: true,
|
||||
},
|
||||
metadata: SEARCH_METADATA_OUTPUT,
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -198,7 +198,8 @@ export const listTool: ToolConfig<GoogleDriveToolParams, GoogleDriveListResponse
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
description:
|
||||
'Page token for the next page of files; absent from the response when the end of the files list has been reached',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ export const listCommentsTool: ToolConfig<
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of comments',
|
||||
description:
|
||||
'Page token for the next page of comments; absent from the response when the end of the comments list has been reached',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ export const listPermissionsTool: ToolConfig<
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of permissions',
|
||||
description:
|
||||
'Page token for the next page of permissions; absent from the response when the end of the permissions list has been reached',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -120,7 +120,8 @@ export const listRevisionsTool: ToolConfig<
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of revisions',
|
||||
description:
|
||||
'Page token for the next page of revisions; absent from the response when the end of the revisions list has been reached',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -144,7 +144,8 @@ export const searchTool: ToolConfig<GoogleDriveSearchParams, GoogleDriveSearchRe
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
description:
|
||||
'Page token for the next page of files; absent from the response when the end of the files list has been reached',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export const dataverseUploadFileTool: InternalToolConfig<
|
||||
id: 'microsoft_dataverse_upload_file',
|
||||
name: 'Upload File to Microsoft Dataverse',
|
||||
description:
|
||||
'Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. The file content must be provided as a base64-encoded string.',
|
||||
'Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. Provide the file through the File input; its bytes are read from storage and sent as the raw request body.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
|
||||
@@ -466,7 +466,8 @@ export const mistralParserV2Tool: InternalToolConfig<MistralParserInput, Mistral
|
||||
},
|
||||
image_base64: {
|
||||
type: 'string',
|
||||
description: 'Base64-encoded image data (when include_image_base64=true)',
|
||||
description:
|
||||
'Base64-encoded image data; returned only when the hidden includeImageBase64 input is enabled',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
@@ -484,7 +485,7 @@ export const mistralParserV2Tool: InternalToolConfig<MistralParserInput, Mistral
|
||||
tables: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Extracted tables as HTML/markdown (when table_format is set). Referenced via placeholders like [tbl-0.html]',
|
||||
'Separate table objects, referenced from the markdown via placeholders like [tbl-0.html]. Mistral populates these only when table_format is "markdown" or "html"; Sim never sets it, so tables stay inline in the markdown and this list is empty',
|
||||
},
|
||||
hyperlinks: {
|
||||
type: 'array',
|
||||
@@ -497,12 +498,14 @@ export const mistralParserV2Tool: InternalToolConfig<MistralParserInput, Mistral
|
||||
},
|
||||
header: {
|
||||
type: 'string',
|
||||
description: 'Page header content (when extract_header=true)',
|
||||
description:
|
||||
'Page header content. Mistral returns it only when extract_header is true (it defaults to false); Sim never sets it, so this is not returned',
|
||||
optional: true,
|
||||
},
|
||||
footer: {
|
||||
type: 'string',
|
||||
description: 'Page footer content (when extract_footer=true)',
|
||||
description:
|
||||
'Page footer content. Mistral returns it only when extract_footer is true (it defaults to false); Sim never sets it, so this is not returned',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,7 +18,8 @@ export const MISTRAL_OCR_IMAGE_OUTPUT_PROPERTIES = {
|
||||
bottom_right_y: { type: 'number', description: 'Bottom-right Y coordinate in pixels' },
|
||||
image_base64: {
|
||||
type: 'string',
|
||||
description: 'Base64-encoded image data (when include_image_base64=true)',
|
||||
description:
|
||||
'Base64-encoded image data; returned only when the hidden includeImageBase64 input is enabled',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
@@ -58,7 +59,8 @@ export const MISTRAL_OCR_PAGE_OUTPUT_PROPERTIES = {
|
||||
dimensions: MISTRAL_OCR_DIMENSIONS_OUTPUT,
|
||||
tables: {
|
||||
type: 'array',
|
||||
description: 'Extracted tables as HTML/markdown (when table_format is set)',
|
||||
description:
|
||||
'Separate table objects, referenced from the markdown via placeholders like [tbl-0.html]. Mistral populates these only when table_format is "markdown" or "html"; Sim never sets it, so tables stay inline in the markdown and this list is empty',
|
||||
},
|
||||
hyperlinks: {
|
||||
type: 'array',
|
||||
@@ -67,12 +69,14 @@ export const MISTRAL_OCR_PAGE_OUTPUT_PROPERTIES = {
|
||||
},
|
||||
header: {
|
||||
type: 'string',
|
||||
description: 'Page header content (when extract_header=true)',
|
||||
description:
|
||||
'Page header content. Mistral returns it only when extract_header is true (it defaults to false); Sim never sets it, so this is not returned',
|
||||
optional: true,
|
||||
},
|
||||
footer: {
|
||||
type: 'string',
|
||||
description: 'Page footer content (when extract_footer=true)',
|
||||
description:
|
||||
'Page footer content. Mistral returns it only when extract_footer is true (it defaults to false); Sim never sets it, so this is not returned',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
@@ -224,12 +224,13 @@ export const pulseParserTool: InternalToolConfig<PulseParserInput, PulseParserOu
|
||||
},
|
||||
html: {
|
||||
type: 'string',
|
||||
description: 'HTML content if requested',
|
||||
description: 'HTML content; returned only when the hidden returnHtml input is enabled',
|
||||
optional: true,
|
||||
},
|
||||
structured_output: {
|
||||
type: 'json',
|
||||
description: 'Structured output if schema was provided',
|
||||
description:
|
||||
'Structured output; Sim exposes no input for supplying a schema, so this is always null',
|
||||
optional: true,
|
||||
},
|
||||
chunks: {
|
||||
@@ -239,7 +240,8 @@ export const pulseParserTool: InternalToolConfig<PulseParserInput, PulseParserOu
|
||||
},
|
||||
figures: {
|
||||
type: 'json',
|
||||
description: 'Extracted figures if figure extraction was enabled',
|
||||
description:
|
||||
'Extracted figures; returned only when the hidden extractFigure input is enabled',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -101,7 +101,7 @@ interface PulseParserOutputData {
|
||||
/** HTML content if requested */
|
||||
html?: string
|
||||
|
||||
/** Structured output if schema was provided */
|
||||
/** Structured output; Sim exposes no input for supplying a schema, so this is always null. */
|
||||
structured_output?: Record<string, unknown>
|
||||
|
||||
/** Chunked content if chunking was enabled */
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { safeUrlPathSegment } from '@/tools/url-path'
|
||||
|
||||
const ORIGIN = 'https://api.example.com'
|
||||
|
||||
/**
|
||||
* Vectors that must be REJECTED outright — no encoding neutralizes them.
|
||||
*/
|
||||
const SEGMENT_REJECTED = ['..', '.', ' .. ', 'a/../../b', '\\..\\..', '', ' '] as const
|
||||
|
||||
/**
|
||||
* Vectors that must NOT throw because `encodeURIComponent` turns them into
|
||||
* literal names (`%` and `?` are escaped), leaving the path shape intact.
|
||||
*/
|
||||
const NEUTRALIZED = ['%2e%2e', '..%2f..', 'x?foo=attacker'] as const
|
||||
|
||||
describe('the premise these helpers exist for', () => {
|
||||
it.concurrent('encodeURIComponent does not escape a dot segment', () => {
|
||||
expect(encodeURIComponent('..')).toBe('..')
|
||||
expect(encodeURIComponent('.')).toBe('.')
|
||||
})
|
||||
|
||||
it.concurrent('the WHATWG parser removes dot segments after decoding', () => {
|
||||
expect(new URL('https://x/v1/a/b/..').pathname).toBe('/v1/a/')
|
||||
expect(new URL('https://x/v1/a/b/%2e%2e').pathname).toBe('/v1/a/')
|
||||
})
|
||||
|
||||
/**
|
||||
* The parser removes the percent-encoded spellings too, not just the two
|
||||
* literal ones. The guard below matches only the literal spellings, which is
|
||||
* sufficient solely because `encodeURIComponent` escapes `%` and so can never
|
||||
* emit a `%2e` form. Both halves are asserted here, because the second is
|
||||
* what makes the first safe. Every case-and-mixing combination of the one-
|
||||
* and two-dot forms is enumerated rather than counted in prose.
|
||||
*/
|
||||
it.concurrent.each([
|
||||
['.', '/v1/a/'],
|
||||
['%2e', '/v1/a/'],
|
||||
['%2E', '/v1/a/'],
|
||||
['..', '/v1/'],
|
||||
['.%2e', '/v1/'],
|
||||
['.%2E', '/v1/'],
|
||||
['%2e.', '/v1/'],
|
||||
['%2E.', '/v1/'],
|
||||
['%2e%2e', '/v1/'],
|
||||
['%2e%2E', '/v1/'],
|
||||
['%2E%2e', '/v1/'],
|
||||
['%2E%2E', '/v1/'],
|
||||
] as const)(
|
||||
'the parser also removes the encoded dot-segment spelling %j (=> %j)',
|
||||
(spelling, expected) => {
|
||||
expect(new URL(`https://x/v1/a/${spelling}`).pathname).toBe(expected)
|
||||
}
|
||||
)
|
||||
|
||||
it.concurrent.each(['...', '%2e%2e%2e', '%252e', 'a%2e'])(
|
||||
'the parser does NOT remove %j',
|
||||
(spelling) => {
|
||||
expect(new URL(`https://x/v1/a/${spelling}`).pathname.startsWith('/v1/a/')).toBe(true)
|
||||
}
|
||||
)
|
||||
|
||||
it.concurrent('encodeURIComponent escapes % so no helper can emit a %2e spelling', () => {
|
||||
expect(encodeURIComponent('%2e%2e')).toBe('%252e%252e')
|
||||
expect(new URL('https://x/v1/a/%252e%252e').pathname).toBe('/v1/a/%252e%252e')
|
||||
})
|
||||
})
|
||||
|
||||
describe('safeUrlPathSegment', () => {
|
||||
it.concurrent.each(SEGMENT_REJECTED)('rejects %j', (value) => {
|
||||
expect(() => safeUrlPathSegment(value, 'table')).toThrow(/table/)
|
||||
})
|
||||
|
||||
it.concurrent.each(NEUTRALIZED)('neutralizes %j into one literal segment', (value) => {
|
||||
const built = `${ORIGIN}/rest/v1/${safeUrlPathSegment(value, 'table')}?select=*`
|
||||
const url = new URL(built)
|
||||
const segments = url.pathname.split('/')
|
||||
|
||||
expect(segments).toHaveLength(4)
|
||||
expect(segments[0]).toBe('')
|
||||
expect(segments[1]).toBe('rest')
|
||||
expect(segments[2]).toBe('v1')
|
||||
expect(decodeURIComponent(segments[3])).toBe(value)
|
||||
expect(url.searchParams.get('foo')).toBeNull()
|
||||
expect(url.searchParams.get('select')).toBe('*')
|
||||
})
|
||||
|
||||
it.concurrent.each([
|
||||
'my-file.v2.txt',
|
||||
'..foo',
|
||||
'foo..',
|
||||
'users',
|
||||
'3f1c9a1e-6f27-4b2e-9b0f-2a1d4e5c6b7a',
|
||||
])('preserves %j byte-identically', (value) => {
|
||||
const built = `${ORIGIN}/rest/v1/${safeUrlPathSegment(value, 'table')}`
|
||||
const segments = new URL(built).pathname.split('/')
|
||||
|
||||
expect(segments).toHaveLength(4)
|
||||
expect(decodeURIComponent(segments[3])).toBe(value)
|
||||
})
|
||||
|
||||
it.concurrent('trims surrounding whitespace', () => {
|
||||
expect(safeUrlPathSegment(' users ', 'table')).toBe('users')
|
||||
})
|
||||
|
||||
it.concurrent('every vector either throws or keeps the exact path shape', () => {
|
||||
for (const value of [...SEGMENT_REJECTED, ...NEUTRALIZED]) {
|
||||
let built: string | null = null
|
||||
try {
|
||||
built = `${ORIGIN}/rest/v1/${safeUrlPathSegment(value, 'table')}`
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
const segments = new URL(built).pathname.split('/')
|
||||
expect(segments).toHaveLength(4)
|
||||
expect(segments[1]).toBe('rest')
|
||||
expect(segments[2]).toBe('v1')
|
||||
expect(segments[3]).not.toBe('')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Tool params are declared `type: 'string'` but nothing enforces that before
|
||||
* the value reaches the guard: an LLM tool call or stored workflow state can
|
||||
* hand a numeric-looking id over as a JSON number. Coercing a non-string to
|
||||
* `''` reported such a value as missing, which names the wrong problem.
|
||||
*/
|
||||
describe('non-string inputs', () => {
|
||||
it.concurrent.each([
|
||||
[123, '123'],
|
||||
[0, '0'],
|
||||
[1.5, '1.5'],
|
||||
[-7, '-7'],
|
||||
[2487956, '2487956'],
|
||||
] as const)('stringifies the number %j to %j instead of throwing', (value, expected) => {
|
||||
expect(safeUrlPathSegment(value, 'woeid')).toBe(expected)
|
||||
})
|
||||
|
||||
it.concurrent('keeps a numeric id addressable in the built URL', () => {
|
||||
const url = new URL(`${ORIGIN}/v1/trends/${safeUrlPathSegment(2487956, 'woeid')}`)
|
||||
|
||||
expect(url.pathname).toBe('/v1/trends/2487956')
|
||||
})
|
||||
|
||||
/**
|
||||
* `String(null)` is `'null'` and `String(undefined)` is `'undefined'` — both
|
||||
* truthy — so these must be rejected before coercion or the request would
|
||||
* silently address a resource literally named "null".
|
||||
*/
|
||||
it.concurrent.each([
|
||||
['null', null],
|
||||
['undefined', undefined],
|
||||
] as const)('still throws the required error for %s', (_label, value) => {
|
||||
expect(() => safeUrlPathSegment(value as unknown as string, 'id')).toThrow(/id is required/)
|
||||
})
|
||||
|
||||
it.concurrent('never lets null or undefined reach the path as literal text', () => {
|
||||
for (const value of [null, undefined]) {
|
||||
let built: string | null = null
|
||||
try {
|
||||
built = `${ORIGIN}/v1/${safeUrlPathSegment(value as unknown as string, 'id')}`
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
expect(built).toBeNull()
|
||||
}
|
||||
})
|
||||
|
||||
it.concurrent('applies the dot-segment guard to a coerced value too', () => {
|
||||
expect(() => safeUrlPathSegment({ toString: () => '..' } as unknown as string, 'id')).toThrow(
|
||||
/id/
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* The coercion is deliberately narrow. It exists so an id the caller genuinely
|
||||
* supplied as a JSON number is not reported as missing, and it must not be a
|
||||
* general `String(value)` — that turns a wrong-shaped value into a plausible
|
||||
* but wrong path segment instead of a clean, named error.
|
||||
*/
|
||||
describe('coercion boundary', () => {
|
||||
it.concurrent.each([
|
||||
['string', 'abc', 'abc'],
|
||||
['zero', 0, '0'],
|
||||
['negative', -7, '-7'],
|
||||
['decimal', 1.5, '1.5'],
|
||||
['bigint', 42n, '42'],
|
||||
['large safe integer', 9007199254740991, '9007199254740991'],
|
||||
] as const)('accepts the %s as the expected string', (_label, value, expected) => {
|
||||
expect(safeUrlPathSegment(value as never, 'id')).toBe(expected)
|
||||
})
|
||||
|
||||
it.concurrent.each([
|
||||
['plain object', {}],
|
||||
['populated object', { a: 1 }],
|
||||
['Map', new Map()],
|
||||
['null-prototype object', Object.create(null)],
|
||||
['true', true],
|
||||
['false', false],
|
||||
['NaN', Number.NaN],
|
||||
['Infinity', Number.POSITIVE_INFINITY],
|
||||
['-Infinity', Number.NEGATIVE_INFINITY],
|
||||
['array', [1, 2]],
|
||||
['Date', new Date(0)],
|
||||
['symbol', Symbol('s')],
|
||||
['exponential number', 1e21],
|
||||
['unsafe integer', Number.MAX_SAFE_INTEGER + 2],
|
||||
['snowflake-sized id parsed as a number', Number('1234567890123456789')],
|
||||
['function', () => 'x'],
|
||||
] as const)('rejects the %s with an error naming the param', (_label, value) => {
|
||||
let thrown: unknown = null
|
||||
try {
|
||||
safeUrlPathSegment(value as never, 'objectId')
|
||||
} catch (error) {
|
||||
thrown = error
|
||||
}
|
||||
expect(thrown).toBeInstanceOf(Error)
|
||||
expect((thrown as Error).message).toContain('objectId')
|
||||
expect((thrown as Error).message).not.toContain('[object')
|
||||
expect((thrown as Error).message).not.toMatch(/No default value/)
|
||||
})
|
||||
|
||||
/**
|
||||
* `null` and `undefined` keep reporting *"is required"* — the distinction
|
||||
* between "you sent nothing" and "you sent the wrong kind of thing" is what
|
||||
* makes the error actionable.
|
||||
*/
|
||||
it.concurrent.each([
|
||||
['null', null],
|
||||
['undefined', undefined],
|
||||
] as const)('keeps the required error for %s rather than the invalid-value one', (_l, value) => {
|
||||
expect(() => safeUrlPathSegment(value as never, 'objectId')).toThrow(/objectId is required/)
|
||||
})
|
||||
|
||||
it.concurrent('never lets a rejected value reach the built path', () => {
|
||||
for (const value of [{}, true, Number.NaN, [1, 2], 1e21, new Date(0)]) {
|
||||
let built: string | null = null
|
||||
try {
|
||||
built = `${ORIGIN}/v1/${safeUrlPathSegment(value as never, 'id')}`
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
expect(built).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* `JSON.parse` accepts a lone-surrogate escape (`"\ud83d"`, a truncated emoji
|
||||
* from an LLM tool call), so an unpaired surrogate reaches the guard as an
|
||||
* ordinary string that every other check passes. `encodeURIComponent` throws a
|
||||
* bare `URIError` on it, which loses the parameter name this module promises
|
||||
* every failure carries.
|
||||
*/
|
||||
describe('lone surrogates', () => {
|
||||
const LONE_SURROGATES = [
|
||||
JSON.parse('{"v":"dpl_\\ud83d"}').v,
|
||||
JSON.parse('{"v":"\\udc4d_tail"}').v,
|
||||
JSON.parse('{"v":"a\\ud800b"}').v,
|
||||
] as const
|
||||
|
||||
it.concurrent.each(LONE_SURROGATES)("rejects %j with this module's named error", (value) => {
|
||||
let thrown: unknown = null
|
||||
try {
|
||||
safeUrlPathSegment(value, 'deploymentId')
|
||||
} catch (error) {
|
||||
thrown = error
|
||||
}
|
||||
|
||||
expect(thrown).toBeInstanceOf(Error)
|
||||
expect(thrown).not.toBeInstanceOf(URIError)
|
||||
expect((thrown as Error).message).toContain('deploymentId')
|
||||
})
|
||||
|
||||
it.concurrent('keeps a WELL-PAIRED surrogate working', () => {
|
||||
expect(safeUrlPathSegment('dpl_😀', 'deploymentId')).toBe(`dpl_${encodeURIComponent('😀')}`)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* A tiny exponential (`1e-7`) round-trips through `String`/`Number` exactly, so
|
||||
* the "too large to be represented exactly" message is factually wrong for it.
|
||||
* The rejection is kept — an exponential spelling is not a usable identifier in
|
||||
* any provider path — but the message must say that, and the genuinely
|
||||
* imprecise large values must keep the precision message.
|
||||
*/
|
||||
describe('exponential number spellings', () => {
|
||||
it.concurrent.each([1e-7, 1.5e-7, 1e-21, 5e-324, -1e-7] as const)(
|
||||
'rejects the tiny exponential %j without claiming it is too large',
|
||||
(value) => {
|
||||
expect(Number(String(value))).toBe(value)
|
||||
|
||||
let thrown: unknown = null
|
||||
try {
|
||||
safeUrlPathSegment(value, 'woeid')
|
||||
} catch (error) {
|
||||
thrown = error
|
||||
}
|
||||
|
||||
expect(thrown).toBeInstanceOf(Error)
|
||||
expect((thrown as Error).message).toContain('woeid')
|
||||
expect((thrown as Error).message).not.toContain('too large')
|
||||
expect((thrown as Error).message).toContain('exponential')
|
||||
}
|
||||
)
|
||||
|
||||
it.concurrent.each([1e21, 1.5e300, Number.MAX_SAFE_INTEGER + 2] as const)(
|
||||
'keeps the precision message for the imprecise large value %j',
|
||||
(value) => {
|
||||
expect(() => safeUrlPathSegment(value, 'woeid')).toThrow(/too large to be represented/)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* The 44 live call sites (Vercel x43, Daytona x1) pass provider ids and
|
||||
* hostnames, and every one of them interpolated `${params.id.trim()}` before
|
||||
* these guards existed — so a string is the only shape any of them has ever
|
||||
* handled, and a string's output must stay byte-identical across this change.
|
||||
* A numeric id is the newly accepted shape, not a restored one: it threw
|
||||
* `TypeError: .trim is not a function` at those same call sites before.
|
||||
*/
|
||||
describe('live call-site values', () => {
|
||||
it.concurrent.each([
|
||||
'prj_2rXy9Qh0lE8vJmKpZ4aB1cD',
|
||||
'dpl_9fJk2LmN4pQr7sT1uV3wX5yZ',
|
||||
'team_abcDEF123',
|
||||
'my-app.vercel.app',
|
||||
'example.com',
|
||||
'sub.domain.example.co.uk',
|
||||
'rec_1a2b3c',
|
||||
'ecfg_xyz',
|
||||
'3f1c9a1e-6f27-4b2e-9b0f-2a1d4e5c6b7a',
|
||||
])('passes %j through unchanged', (value) => {
|
||||
expect(safeUrlPathSegment(value, 'id')).toBe(value)
|
||||
})
|
||||
|
||||
it.concurrent('stringifies a numeric id these call sites used to reject', () => {
|
||||
expect(safeUrlPathSegment(2487956, 'deploymentId')).toBe('2487956')
|
||||
expect(safeUrlPathSegment(0, 'sandboxId')).toBe('0')
|
||||
})
|
||||
})
|
||||
+165
-30
@@ -1,41 +1,176 @@
|
||||
/**
|
||||
* Traversal-safe construction of URL path components from tool parameters.
|
||||
*
|
||||
* The rule these helpers encode, stated once for the whole module: **no
|
||||
* encoding scheme neutralizes a dot segment — only value rejection does.**
|
||||
*
|
||||
* `.` and `..` are *unreserved* characters, so `encodeURIComponent('..')`
|
||||
* returns `'..'` verbatim. Double-encoding does not help either, because the
|
||||
* WHATWG URL parser that `fetch` uses removes the percent-encoded spellings of
|
||||
* a dot segment (`%2e`, `%2E`, and every mixed spelling of the two-dot form)
|
||||
* just as it removes the literal one:
|
||||
*
|
||||
* ```
|
||||
* new URL('https://x/v1/a/b/..').pathname // => '/v1/a/'
|
||||
* new URL('https://x/v1/a/b/%2e%2e').pathname // => '/v1/a/' (still removed)
|
||||
* ```
|
||||
*
|
||||
* Only the literal spellings need checking here, and that is not a shortcut:
|
||||
* `encodeURIComponent` escapes `%` itself, so a value whose literal text is
|
||||
* `%2e%2e` leaves as `%252e%252e` and no encoded spelling can ever be emitted.
|
||||
* The check is sufficient *because* every value goes through
|
||||
* `encodeURIComponent` — a pass-through for pre-encoded input would have to
|
||||
* widen it.
|
||||
*
|
||||
* A removed segment pops a path segment on a fixed host with the caller's
|
||||
* bearer token still attached — including on DELETE routes. These parameters
|
||||
* are typically `visibility: 'user-or-llm'`, so prompt injection controls them.
|
||||
* Therefore a value that is exactly `.` or `..` after trimming is rejected
|
||||
* outright rather than encoded, and no helper here may be "simplified" back to
|
||||
* a bare encode.
|
||||
*
|
||||
* A dot *inside* a longer segment is legitimate and preserved untouched:
|
||||
* `example.com`, `my-app.vercel.app`, `..foo`, and `foo..` all pass through.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalizes an incoming parameter to a string before it is guarded.
|
||||
*
|
||||
* Trimming is left to the caller, because whether surrounding whitespace is
|
||||
* copy-paste noise or part of the value is a per-helper decision.
|
||||
*
|
||||
* Tool params are declared `type: 'string'`, but that declaration is not
|
||||
* enforced anywhere before the value reaches here: it arrives from an LLM tool
|
||||
* call or from stored workflow state, where a numeric-looking id (a Vercel
|
||||
* `deploymentId`, a Daytona `sandboxId`) can be serialized as a JSON **number**
|
||||
* and stays one. The previous `typeof value === 'string' ? value.trim() : ''`
|
||||
* turned any such value into `''`, which the guards then reported as
|
||||
* *"<param> is required"* — the least actionable message available for a value
|
||||
* the caller did supply, and one that points at the wrong fix.
|
||||
*
|
||||
* This is not a restoration of prior behaviour. Every call site that predates
|
||||
* these guards interpolated `${params.id.trim()}`, so a numeric id threw
|
||||
* `TypeError: params.id.trim is not a function` there too. The widening is a
|
||||
* deliberate improvement: it accepts what callers actually send, and where it
|
||||
* still refuses (below) it says why by name.
|
||||
*
|
||||
* An id too large for a `double` — a Discord snowflake, a Twitter id — is
|
||||
* **not** in scope here and cannot be: `JSON.parse` destroys the precision
|
||||
* before this function is ever reached (`1234567890123456789` becomes
|
||||
* `1234567890123456800`). Such an id must arrive as a **string**; nothing this
|
||||
* function does can recover one that did not.
|
||||
*
|
||||
* The accepted set is therefore narrow on purpose — `string`, `number`, and
|
||||
* `bigint`, and nothing else. A bare `String(value)` would coerce every other
|
||||
* shape into a *plausible but wrong* segment (`{}` into
|
||||
* `'%5Bobject%20Object%5D'`, `true` into `'true'`, `[1,2]` into `'1%2C2'`,
|
||||
* `new Date(0)` into a 60-character encoded date), producing a 404 from the
|
||||
* provider instead of a named error from us. Rejecting them keeps the failure
|
||||
* legible and attributable to the caller's input.
|
||||
*
|
||||
* Three number spellings are rejected even though `typeof` says `'number'`,
|
||||
* because their decimal text is not the id the caller meant:
|
||||
*
|
||||
* - Non-finite (`NaN`, `±Infinity`) — no identifier reading at all.
|
||||
* - An integer beyond `Number.MAX_SAFE_INTEGER`, whose decimal text has already
|
||||
* lost digits. That is silent corruption of a large id, which is exactly the
|
||||
* failure mode a caller cannot debug from a 404. Every value large enough to
|
||||
* print exponentially (`1e21` → `'1e+21'`) is an integer double and lands
|
||||
* here, so it keeps this precision message.
|
||||
* - A value whose `String()` is exponential without being imprecise — only the
|
||||
* *tiny* magnitudes reach this (`1e-7`, `5e-324`). These do round-trip
|
||||
* exactly, so the precision complaint would be false; they are rejected on
|
||||
* the separate ground that `1e-7` is not a spelling any provider path
|
||||
* accepts as an identifier, and emitting `/v1/trends/1e-7` would rewrite the
|
||||
* caller's `0.0000001` into text they never wrote. The error says so.
|
||||
*
|
||||
* A plain decimal such as `1.5` is kept: it round-trips through `String`
|
||||
* exactly and reads as written, so it is the caller's value verbatim.
|
||||
*
|
||||
* `null` and `undefined` are rejected *first* and keep the distinct *"is
|
||||
* required"* message, because `String(null)` is the truthy `'null'` — coercing
|
||||
* would silently address a resource literally named `"null"` — and because
|
||||
* "you sent nothing" is a different fix for the caller than "you sent the
|
||||
* wrong kind of thing". The type check also runs before any stringification,
|
||||
* so an `Object.create(null)` produces this module's named error rather than a
|
||||
* bare `TypeError` with the parameter name lost.
|
||||
*/
|
||||
function toGuardedString(value: unknown, paramName: string): string {
|
||||
if (value === null || value === undefined) {
|
||||
throw new Error(`${paramName} is required`)
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
if (!Number.isFinite(value)) {
|
||||
throw new Error(`${paramName} must be a string or a finite number`)
|
||||
}
|
||||
|
||||
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
||||
throw new Error(
|
||||
`${paramName} is too large to be represented exactly as a number (pass it as a string)`
|
||||
)
|
||||
}
|
||||
|
||||
const stringified = String(value)
|
||||
|
||||
if (stringified.includes('e')) {
|
||||
throw new Error(
|
||||
`${paramName} must be a plain decimal, but ${stringified} is exponential notation (pass it as a string)`
|
||||
)
|
||||
}
|
||||
|
||||
return stringified
|
||||
}
|
||||
|
||||
throw new Error(`${paramName} must be a string or a number (received ${typeof value})`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encodes one segment, keeping this module's named-error contract.
|
||||
*
|
||||
* `encodeURIComponent` throws a bare `URIError` on an unpaired UTF-16
|
||||
* surrogate, and that error names neither the parameter nor the module.
|
||||
* Unpaired surrogates are reachable: `JSON.parse` accepts a lone `"\ud83d"`
|
||||
* escape, so a truncated emoji in an LLM tool call arrives here as an ordinary
|
||||
* string that every check above passes.
|
||||
*/
|
||||
function encodeSegment(segment: string, paramName: string): string {
|
||||
try {
|
||||
return encodeURIComponent(segment)
|
||||
} catch {
|
||||
throw new Error(`${paramName} contains an unpaired UTF-16 surrogate and cannot be encoded`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a single, traversal-safe URL path segment from an identifier that a
|
||||
* tool interpolates into a request path.
|
||||
*
|
||||
* `encodeURIComponent` alone is NOT sufficient, and this is the whole reason
|
||||
* this helper exists — do not "simplify" it back to a bare encode. `.` and `..`
|
||||
* are *unreserved* characters, so `encodeURIComponent('..') === '..'` and
|
||||
* `encodeURIComponent('.') === '.'`. The WHATWG URL parser that `fetch` uses
|
||||
* then applies RFC 3986 dot-segment removal, so the segment is normalized away
|
||||
* *after* encoding:
|
||||
* Rejects empty values, dot segments, and any value still carrying a `/` or
|
||||
* `\` separator (defense in depth — encoding already neutralizes those, but a
|
||||
* separator in a single-segment parameter means the caller passed something
|
||||
* other than what the parameter addresses).
|
||||
*
|
||||
* ```
|
||||
* new URL('https://api.vercel.com/v1/global-config/../items').pathname
|
||||
* // => '/v1/items'
|
||||
* new URL('https://proxy.app.daytona.io/toolbox/../files/upload-v2').pathname
|
||||
* // => '/files/upload-v2'
|
||||
* ```
|
||||
* See the module note above for why rejection, not encoding, is the mechanism.
|
||||
*
|
||||
* That pops exactly one path segment on a fixed host with the caller's bearer
|
||||
* token still attached — including on DELETE routes. Multi-level traversal
|
||||
* (`../..`) is already blocked by encoding because the inner `/` becomes
|
||||
* `%2F`, but the single-segment case is not. These IDs are
|
||||
* `visibility: 'user-or-llm'`, so prompt injection controls them.
|
||||
*
|
||||
* A value that is exactly `.` or `..` after trimming is therefore rejected
|
||||
* outright, as are empty values and any value still carrying a `/` or `\`
|
||||
* separator (defense in depth — encoding already neutralizes those).
|
||||
*
|
||||
* Note that a dot *inside* a longer segment is legitimate and preserved:
|
||||
* `example.com`, `my-app.vercel.app`, and `..foo` all pass through untouched.
|
||||
*
|
||||
* @param value - The raw identifier, typically LLM- or user-supplied.
|
||||
* @param value - The raw identifier, typically LLM- or user-supplied. A finite
|
||||
* number or a bigint is stringified, since an LLM can emit a numeric-looking
|
||||
* id as a JSON number; any other non-string kind is rejected by name.
|
||||
* @param paramName - The parameter name, used to name the offender in errors.
|
||||
* @returns The trimmed, percent-encoded segment, safe to interpolate.
|
||||
* @throws If the value is empty, a dot segment, or contains a path separator.
|
||||
* @throws If the value is not a string or a usable number, is empty, is a dot
|
||||
* segment, contains a path separator, or cannot be encoded.
|
||||
*/
|
||||
export function safeUrlPathSegment(value: string, paramName: string): string {
|
||||
const trimmed = typeof value === 'string' ? value.trim() : ''
|
||||
export function safeUrlPathSegment(value: string | number | bigint, paramName: string): string {
|
||||
const trimmed = toGuardedString(value, paramName).trim()
|
||||
|
||||
if (!trimmed) {
|
||||
throw new Error(`${paramName} is required`)
|
||||
@@ -49,5 +184,5 @@ export function safeUrlPathSegment(value: string, paramName: string): string {
|
||||
throw new Error(`${paramName} cannot contain a path separator`)
|
||||
}
|
||||
|
||||
return encodeURIComponent(trimmed)
|
||||
return encodeSegment(trimmed, paramName)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export const vantaUploadDocumentFileTool: InternalToolConfig<
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'MIME type of the file (e.g., application/pdf); used when uploading base64 content, since uploaded files already carry their own type',
|
||||
'MIME type of the file (e.g., application/pdf). Used only for base64 uploads; ignored for a file from the File input, whose content type is always resolved from storage.',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
|
||||
@@ -14238,7 +14238,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Upload File",
|
||||
"description": "Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. The file content must be provided as a base64-encoded string."
|
||||
"description": "Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. Provide the file through the File input; its bytes are read from storage and sent as the raw request body."
|
||||
},
|
||||
{
|
||||
"name": "Download File",
|
||||
@@ -23504,7 +23504,7 @@
|
||||
"name": "TinyFish",
|
||||
"description": "Automate and read the live web",
|
||||
"longDescription": "Integrate TinyFish into the workflow. Give a web agent a natural-language goal and let it drive a real browser on any site, queue and track long-running automations, search the web, and fetch pages as clean markdown.",
|
||||
"bgColor": "#FF6700",
|
||||
"bgColor": "#FFFFFF",
|
||||
"iconName": "TinyFishIcon",
|
||||
"docsUrl": "https://docs.sim.ai/integrations/tinyfish",
|
||||
"operations": [
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
extractAllBlockConfigs,
|
||||
extractBlockSuppliedParamIds,
|
||||
extractToolInfo,
|
||||
extractUserSettableParamIds,
|
||||
getToolInfo,
|
||||
parseConstProperties,
|
||||
parsePropertiesContent,
|
||||
@@ -106,6 +111,56 @@ describe('documentation input parameter parsing', () => {
|
||||
])
|
||||
})
|
||||
|
||||
/**
|
||||
* Pins the hidden-param filter on the source-parsing path in {@link extractToolInfo}. Tools
|
||||
* with an entry in tool-metadata.ts never reach it, so it must be driven with synthetic
|
||||
* source rather than through `getToolInfo`. Without this the filter can be deleted outright
|
||||
* and the whole suite stays green.
|
||||
*/
|
||||
describe('the hidden-param filter on the source-parsing path', () => {
|
||||
const source = `
|
||||
export const exampleTool = {
|
||||
id: 'example_send',
|
||||
description: 'Send an example',
|
||||
params: {
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The message',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The API key the block injects',
|
||||
},
|
||||
instanceUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Resolved from the credential',
|
||||
},
|
||||
},
|
||||
outputs: {},
|
||||
}
|
||||
`
|
||||
|
||||
const paramNames = (ids: ReadonlySet<string> | null) =>
|
||||
extractToolInfo('example_send', source, '', '', '', ids)?.params.map(({ name }) => name)
|
||||
|
||||
it('drops a hidden param the block does not supply', () => {
|
||||
expect(paramNames(new Set(['message']))).toEqual(['message'])
|
||||
})
|
||||
|
||||
it('keeps a hidden param the block exposes as its own field', () => {
|
||||
expect(paramNames(new Set(['message', 'apiKey']))).toEqual(['message', 'apiKey'])
|
||||
})
|
||||
|
||||
it('keeps every param when the block-supplied set is UNKNOWN', () => {
|
||||
expect(paramNames(null)).toEqual(['message', 'apiKey', 'instanceUrl'])
|
||||
})
|
||||
})
|
||||
|
||||
it('stops at legacy request metadata after a comment', () => {
|
||||
const tool = extractToolInfo(
|
||||
'example_send',
|
||||
@@ -215,3 +270,430 @@ describe('documentation output property parsing', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hidden tool params in the Input table', () => {
|
||||
const blockSource = (blockFile: string) =>
|
||||
fs.readFileSync(path.join(import.meta.dirname, '../apps/sim/blocks/blocks', blockFile), 'utf-8')
|
||||
|
||||
const paramNames = async (toolId: string, blockFile: string) => {
|
||||
const info = await getToolInfo(toolId, extractUserSettableParamIds(blockSource(blockFile)))
|
||||
return info?.params.map((param) => param.name) ?? []
|
||||
}
|
||||
|
||||
it('extracts the param ids a block exposes to the user', () => {
|
||||
const ids = extractUserSettableParamIds(blockSource('mailchimp.ts'))
|
||||
|
||||
expect(ids).toContain('apiKey')
|
||||
expect(extractUserSettableParamIds(blockSource('jira.ts'))).not.toContain('cloudId')
|
||||
})
|
||||
|
||||
it('keeps a hidden tool param the block exposes as a user-typed field', async () => {
|
||||
await expect(paramNames('mailchimp_add_member', 'mailchimp.ts')).resolves.toContain('apiKey')
|
||||
})
|
||||
|
||||
it('drops hidden params the block never exposes', async () => {
|
||||
await expect(paramNames('jira_retrieve', 'jira.ts')).resolves.not.toContain('cloudId')
|
||||
await expect(paramNames('jira_write', 'jira.ts')).resolves.not.toContain('cloudId')
|
||||
|
||||
const salesforce = await paramNames('salesforce_query', 'salesforce.ts')
|
||||
expect(salesforce).not.toContain('idToken')
|
||||
expect(salesforce).not.toContain('instanceUrl')
|
||||
|
||||
await expect(paramNames('netsuite_execute_suiteql', 'netsuite.ts')).resolves.not.toContain(
|
||||
'instanceUrl'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('subBlock param extraction', () => {
|
||||
const blockSource = (blockFile: string) =>
|
||||
fs.readFileSync(path.join(import.meta.dirname, '../apps/sim/blocks/blocks', blockFile), 'utf-8')
|
||||
|
||||
it('extracts ids from a block whose subBlocks array contains commented-out code', () => {
|
||||
const ids = extractUserSettableParamIds(blockSource('google_drive.ts'))
|
||||
|
||||
expect(ids).toContain('operation')
|
||||
expect(ids).toContain('mimeType')
|
||||
expect(ids).toContain('fileName')
|
||||
expect(ids).toContain('uploadFolderSelector')
|
||||
})
|
||||
|
||||
it('extracts ids past a commented-out subBlock that ends a line on an open bracket', () => {
|
||||
const ids = extractUserSettableParamIds(blockSource('human_in_the_loop.ts'))
|
||||
|
||||
expect(ids).toContain('notification')
|
||||
expect(ids).toContain('inputFormat')
|
||||
})
|
||||
|
||||
it('returns no ids for blocks whose subBlocks array is genuinely empty', () => {
|
||||
for (const blockFile of ['chat_trigger.ts', 'manual_trigger.ts']) {
|
||||
expect(extractUserSettableParamIds(blockSource(blockFile))).toEqual([])
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* The spreads name fields arrays this scanner never follows, so what the block supplies is
|
||||
* UNKNOWN. Answering `[]` asserts the block supplies nothing, and the hidden-param filter
|
||||
* reads that as licence to strip every hidden param from every tool the block owns — silently,
|
||||
* with no `parseError` and so no warning. `NotionV2Block` has exactly this shape and is only
|
||||
* harmless today because no `notion_*` tool carries a hidden param besides `accessToken`.
|
||||
*/
|
||||
it('reports a subBlocks array of only unfollowable spreads as UNKNOWN, not empty', () => {
|
||||
for (const blockFile of [
|
||||
'imap.ts',
|
||||
'generic_webhook.ts',
|
||||
'circleback.ts',
|
||||
'rss.ts',
|
||||
'sim_workspace_event.ts',
|
||||
]) {
|
||||
expect(extractUserSettableParamIds(blockSource(blockFile))).toBeNull()
|
||||
}
|
||||
|
||||
const supplied = extractBlockSuppliedParamIds(
|
||||
`subBlocks: [...NotionBlock.subBlocks],`,
|
||||
'NotionV2'
|
||||
)
|
||||
expect(supplied.ids).toBeNull()
|
||||
expect(supplied.parseError).toBeNull()
|
||||
})
|
||||
|
||||
it('still returns the inline ids when a spread sits alongside them', () => {
|
||||
expect(
|
||||
extractUserSettableParamIds(`subBlocks: [...Base.subBlocks, { id: 'operation' }],`)
|
||||
).toEqual(['operation'])
|
||||
})
|
||||
|
||||
it('ignores an id inside a comment or string literal at the top level of a subBlock', () => {
|
||||
expect(
|
||||
extractUserSettableParamIds(`subBlocks: [\n { // id: 'ghost',\n id: 'real' },\n],`)
|
||||
).toEqual(['real'])
|
||||
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n { placeholder: "id: 'ghost'",\n id: 'real' },\n],`
|
||||
)
|
||||
).toEqual(['real'])
|
||||
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n { placeholder: "canonicalParamId: 'ghost'",\n id: 'real',\n canonicalParamId: 'canonical' },\n],`
|
||||
)
|
||||
).toEqual(['real', 'canonical'])
|
||||
})
|
||||
|
||||
it('throws when the subBlocks array holds literal objects but yields no ids', () => {
|
||||
expect(() =>
|
||||
extractUserSettableParamIds(`subBlocks: [\n { title: 'No id here' },\n],`)
|
||||
).toThrow(/subBlocks/)
|
||||
})
|
||||
|
||||
it('throws when the subBlocks array bracket scan fails', () => {
|
||||
expect(() => extractUserSettableParamIds(`subBlocks: [\n { id: 'operation' },\n`)).toThrow(
|
||||
/subBlocks/
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* Shapes taken verbatim from the blocks that ship them: `SlackV2Block`,
|
||||
* `VideoGeneratorV3Block`, `NotionV2Block` and `LinearV2Block`.
|
||||
*/
|
||||
describe('subBlocks shapes the array-literal scan cannot walk', () => {
|
||||
it('reports a subBlocks value that is not an array literal at all', () => {
|
||||
expect(() =>
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: withFalAIModelOptions(VideoGeneratorV2Block.subBlocks, MODELS),`,
|
||||
'VideoGeneratorV3'
|
||||
)
|
||||
).toThrow(/VideoGeneratorV3: subBlocks/)
|
||||
})
|
||||
|
||||
it('reports an array whose only element is a bare helper call', () => {
|
||||
expect(() =>
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [...getSlackV2ActionSubBlocks(), ...getTrigger('slack_oauth').subBlocks],`,
|
||||
'SlackV2'
|
||||
)
|
||||
).toThrow(/SlackV2: subBlocks/)
|
||||
})
|
||||
|
||||
/**
|
||||
* The elements name fields arrays, so the array parsed fine and there is nothing to warn
|
||||
* about — but this scanner never follows a spread, so the fields are UNKNOWN rather than
|
||||
* absent. `[]` would be a confident wrong answer that strips every hidden param the block's
|
||||
* tools declare.
|
||||
*/
|
||||
it('reports an array of nothing but named fields arrays as UNKNOWN', () => {
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n ...NotionBlock.subBlocks,\n ...getTrigger('notion_page_created').subBlocks,\n],`,
|
||||
'NotionV2'
|
||||
)
|
||||
).toBeNull()
|
||||
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n ...LinearBlock.subBlocks.filter((sb) => !sb.id?.startsWith('webhookSecret')),\n],`,
|
||||
'LinearV2'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('does not fail a block that overrides a spread subBlock instead of naming an id', () => {
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n ...Base.subBlocks.map((sb) => (sb.id === 'x' ? { ...sb, required: true } : sb)),\n],`,
|
||||
'OverridingV2'
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('leaves a readable array alone even when it also spreads an opaque helper', () => {
|
||||
expect(
|
||||
extractUserSettableParamIds(
|
||||
`subBlocks: [\n ...SERVICE_ACCOUNT_SUBBLOCKS,\n { id: 'operation' },\n],`,
|
||||
'GoogleDrive'
|
||||
)
|
||||
).toEqual(['operation'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hidden params supplied by the block mapper', () => {
|
||||
const blockSource = (blockFile: string) =>
|
||||
fs.readFileSync(path.join(import.meta.dirname, '../apps/sim/blocks/blocks', blockFile), 'utf-8')
|
||||
|
||||
const paramNames = async (toolId: string, blockFile: string) => {
|
||||
const info = await getToolInfo(toolId, extractBlockSuppliedParamIds(blockSource(blockFile)).ids)
|
||||
return info?.params.map((param) => param.name) ?? []
|
||||
}
|
||||
|
||||
it("keeps Cal.com's required attendee, assembled as result.attendee in the mapper", async () => {
|
||||
expect(extractBlockSuppliedParamIds(blockSource('calcom.ts')).ids).toContain('attendee')
|
||||
await expect(paramNames('calcom_create_booking', 'calcom.ts')).resolves.toContain('attendee')
|
||||
})
|
||||
|
||||
it("keeps JSM's workspaceId, renamed from assetWorkspaceId in the mapper", async () => {
|
||||
expect(extractBlockSuppliedParamIds(blockSource('jira_service_management.ts')).ids).toContain(
|
||||
'workspaceId'
|
||||
)
|
||||
await expect(
|
||||
paramNames('jsm_list_object_schemas', 'jira_service_management.ts')
|
||||
).resolves.toContain('workspaceId')
|
||||
})
|
||||
|
||||
it('keeps the file params Textract renames from its document field', async () => {
|
||||
const ids = extractBlockSuppliedParamIds(blockSource('textract.ts')).ids
|
||||
expect(ids).toContain('file')
|
||||
expect(ids).toContain('fileBack')
|
||||
expect(ids).toContain('filePathBack')
|
||||
|
||||
const params = await paramNames('textract_analyze_id', 'textract.ts')
|
||||
expect(params).toContain('file')
|
||||
expect(params).toContain('fileBack')
|
||||
expect(params).toContain('filePathBack')
|
||||
})
|
||||
|
||||
it('keeps the Mistral parser file param, so its Input table is not empty', async () => {
|
||||
await expect(paramNames('mistral_parser_v3', 'mistral_parse.ts')).resolves.toContain('file')
|
||||
})
|
||||
|
||||
it('still drops resolver-derived hidden params with no user surface', async () => {
|
||||
await expect(paramNames('jira_retrieve', 'jira.ts')).resolves.not.toContain('cloudId')
|
||||
await expect(
|
||||
paramNames('jsm_list_object_schemas', 'jira_service_management.ts')
|
||||
).resolves.not.toContain('cloudId')
|
||||
|
||||
const salesforce = await paramNames('salesforce_query', 'salesforce.ts')
|
||||
expect(salesforce).not.toContain('idToken')
|
||||
expect(salesforce).not.toContain('instanceUrl')
|
||||
|
||||
await expect(paramNames('netsuite_execute_suiteql', 'netsuite.ts')).resolves.not.toContain(
|
||||
'instanceUrl'
|
||||
)
|
||||
await expect(paramNames('snowflake_execute_sql', 'snowflake.ts')).resolves.not.toContain(
|
||||
'domain'
|
||||
)
|
||||
await expect(paramNames('pipedrive_get_deal', 'pipedrive.ts')).resolves.not.toContain(
|
||||
'authStyle'
|
||||
)
|
||||
await expect(paramNames('zoho_desk_list_tickets', 'zoho-desk.ts')).resolves.not.toContain(
|
||||
'apiDomain'
|
||||
)
|
||||
})
|
||||
|
||||
it('finds the real mapper past a decoy params key that is not a mapper', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(`
|
||||
subBlocks: [{ id: 'operation' }],
|
||||
tools: {
|
||||
config: {
|
||||
params: (GitHubBlock.tools?.config as any)?.params,
|
||||
params: (params) => ({ renamed: params.original }),
|
||||
},
|
||||
},
|
||||
`)
|
||||
expect(ids).toContain('renamed')
|
||||
})
|
||||
|
||||
it('reads an async mapper body', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(`
|
||||
subBlocks: [{ id: 'operation' }],
|
||||
tools: {
|
||||
config: {
|
||||
params: async (params) => ({ renamed: params.original }),
|
||||
},
|
||||
},
|
||||
`)
|
||||
expect(ids).toContain('renamed')
|
||||
})
|
||||
|
||||
it('ignores a commented-out mapper assignment', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(`
|
||||
subBlocks: [{ id: 'operation' }],
|
||||
tools: {
|
||||
config: {
|
||||
params: (params) => {
|
||||
const result: Record<string, unknown> = {}
|
||||
// result.commentedOut = params.nope
|
||||
result.realOne = params.yes
|
||||
return result
|
||||
},
|
||||
},
|
||||
},
|
||||
`)
|
||||
expect(ids).toContain('realOne')
|
||||
expect(ids).not.toContain('commentedOut')
|
||||
})
|
||||
})
|
||||
|
||||
describe('an unreadable subBlocks array', () => {
|
||||
/**
|
||||
* Every shape ships in the tree today (`SlackV2Block`, `VideoGeneratorV3Block`, the
|
||||
* `COMMON_SUBBLOCKS` spread and a backtick id), and each one used to end the run for the
|
||||
* whole repository unless the block happened to spread a base whose fields were readable.
|
||||
*/
|
||||
const unreadable: [string, string][] = [
|
||||
['a bare identifier', 'subBlocks: myFields,'],
|
||||
['a helper call', 'subBlocks: withFalAIModelOptions(Base.subBlocks, MODELS),'],
|
||||
['a spread of an opaque constant', 'subBlocks: [...COMMON_SUBBLOCKS],'],
|
||||
['a backtick id', 'subBlocks: [{ id: `operation` }],'],
|
||||
]
|
||||
|
||||
it.each(unreadable)('reports %s as UNKNOWN instead of throwing', (_label, source) => {
|
||||
const supplied = extractBlockSuppliedParamIds(source, 'Widget')
|
||||
|
||||
expect(supplied.ids).toBeNull()
|
||||
expect(supplied.parseError).toMatch(/Widget/)
|
||||
})
|
||||
|
||||
it('still collects the mapper-written ids when only the subBlocks scan failed', () => {
|
||||
const supplied = extractBlockSuppliedParamIds(
|
||||
`
|
||||
subBlocks: myFields,
|
||||
tools: {
|
||||
config: {
|
||||
params: (params) => ({ renamed: params.original }),
|
||||
},
|
||||
},
|
||||
`,
|
||||
'Widget'
|
||||
)
|
||||
|
||||
expect(supplied.ids).toBeNull()
|
||||
expect(supplied.mapperIds).toContain('renamed')
|
||||
})
|
||||
|
||||
const syntheticBlock = (name: string, body: string) => `
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const ${name}Block: BlockConfig = {
|
||||
type: '${name.toLowerCase()}',
|
||||
name: '${name}',
|
||||
description: 'A synthetic block',
|
||||
tools: { access: ['${name.toLowerCase()}_do'] },
|
||||
${body}
|
||||
}
|
||||
`
|
||||
|
||||
it('leaves userSettableParamIds UNKNOWN on the block config it produces', () => {
|
||||
const [unknownConfig] = extractAllBlockConfigs(syntheticBlock('Opaque', 'subBlocks: myFields,'))
|
||||
expect(unknownConfig.userSettableParamIds).toBeNull()
|
||||
|
||||
const [readableConfig] = extractAllBlockConfigs(
|
||||
syntheticBlock('Readable', `subBlocks: [{ id: 'query' }],`)
|
||||
)
|
||||
expect(readableConfig.userSettableParamIds).toEqual(['query'])
|
||||
})
|
||||
|
||||
/**
|
||||
* The whole point of the UNKNOWN state: `[]` asserts the block supplies nothing and strips
|
||||
* every hidden param, so the two must not be spelled the same way.
|
||||
*/
|
||||
it('disables the hidden-param filter, where an empty list applies it', async () => {
|
||||
const unfiltered = await getToolInfo('jira_retrieve', null)
|
||||
expect(unfiltered?.params.map((param) => param.name)).toContain('cloudId')
|
||||
|
||||
const filtered = await getToolInfo('jira_retrieve', [])
|
||||
expect(filtered?.params.map((param) => param.name)).not.toContain('cloudId')
|
||||
})
|
||||
|
||||
it('defaults to not filtering when no param ids are passed at all', async () => {
|
||||
const info = await getToolInfo('jira_retrieve')
|
||||
expect(info?.params.map((param) => param.name)).toContain('cloudId')
|
||||
})
|
||||
})
|
||||
|
||||
describe('mapper param shapes', () => {
|
||||
const mapperBlock = (body: string) => `
|
||||
subBlocks: [{ id: 'doc' }],
|
||||
tools: {
|
||||
config: {
|
||||
params: (params) => ${body},
|
||||
},
|
||||
},
|
||||
`
|
||||
|
||||
it('reads a shorthand property', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(
|
||||
mapperBlock('{\n const file = params.doc\n return { file }\n}')
|
||||
)
|
||||
expect(ids).toContain('doc')
|
||||
expect(ids).toContain('file')
|
||||
})
|
||||
|
||||
it('reads a shorthand property alongside a spread and a named key', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(mapperBlock('({ ...rest, file, other: 1 })'))
|
||||
expect(ids).toEqual(expect.arrayContaining(['file', 'other']))
|
||||
expect(ids).not.toContain('rest')
|
||||
})
|
||||
|
||||
it('reads a shorthand property listed after another shorthand', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(mapperBlock('({ first, file })'))
|
||||
expect(ids).toEqual(expect.arrayContaining(['first', 'file']))
|
||||
})
|
||||
|
||||
it('reads a computed string assignment', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(
|
||||
mapperBlock(
|
||||
"{\n const result: Record<string, unknown> = {}\n result['file'] = params.doc\n return result\n}"
|
||||
)
|
||||
)
|
||||
expect(ids).toContain('file')
|
||||
})
|
||||
|
||||
it('does not take a call argument list for a shorthand property', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(
|
||||
mapperBlock('({ file: buildFile(alpha, beta, gamma) })')
|
||||
)
|
||||
expect(ids).toContain('file')
|
||||
expect(ids).not.toContain('beta')
|
||||
})
|
||||
|
||||
it('ignores a shorthand property inside a comment or a string', () => {
|
||||
const { ids } = extractBlockSuppliedParamIds(
|
||||
mapperBlock("({\n // { ghostComment }\n note: '{ ghostString }',\n file,\n})")
|
||||
)
|
||||
expect(ids).toContain('file')
|
||||
expect(ids).not.toContain('ghostComment')
|
||||
expect(ids).not.toContain('ghostString')
|
||||
})
|
||||
})
|
||||
|
||||
+534
-8
@@ -200,6 +200,15 @@ interface BlockConfig {
|
||||
access?: string[]
|
||||
}
|
||||
operations?: OperationInfo[]
|
||||
/**
|
||||
* Param names the block itself supplies — via a `subBlocks` field (id or
|
||||
* `canonicalParamId`) or via its `tools.config.params` mapper.
|
||||
*
|
||||
* `null` means the block's `subBlocks` array could not be read, so which params it supplies
|
||||
* is UNKNOWN and the hidden-param filter is skipped for it. Never conflate that with `[]`,
|
||||
* which asserts the block supplies nothing and strips every hidden param from its page.
|
||||
*/
|
||||
userSettableParamIds?: string[] | null
|
||||
docsLink?: string
|
||||
[key: string]: any
|
||||
}
|
||||
@@ -668,6 +677,417 @@ ${mappingEntries}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raised when a block's `subBlocks` array is present but cannot be read. Distinguishes
|
||||
* a parse failure from a block that genuinely exposes no fields — both used to surface
|
||||
* as an empty array, and the empty array silently strips documented rows.
|
||||
*/
|
||||
class SubBlockParseError extends Error {
|
||||
override name = 'SubBlockParseError'
|
||||
}
|
||||
|
||||
/** Blocks already warned about. The same block is re-parsed by the page pass, the icon pass and
|
||||
* each spread-base recursion, so without this the same warning prints several times. */
|
||||
const subBlockParseWarnings = new Set<string>()
|
||||
|
||||
/**
|
||||
* Collects the param names a block exposes to the user through its own `subBlocks`.
|
||||
*
|
||||
* A subBlock's `id` is the param it writes, unless it declares `canonicalParamId`,
|
||||
* which is how a differently-named field maps onto a tool param. A tool param marked
|
||||
* `visibility: 'hidden'` is not an LLM-settable tool argument, but when the block
|
||||
* declares a matching field the value is still typed by the user (e.g. Mailchimp's
|
||||
* `apiKey`) and must stay documented. Params with no matching field are genuinely
|
||||
* server-derived (Jira's `cloudId`, Salesforce's `idToken`) and stay filtered out.
|
||||
*
|
||||
* Brace matching runs on a blanked copy so braces inside string literals and comments
|
||||
* cannot skew it; only depth-1 properties of each subBlock are read, so `id` fields on
|
||||
* nested `options`/`condition` objects are never mistaken for the subBlock's own id.
|
||||
*
|
||||
* Returns `null` for UNKNOWN — an array whose elements are all spreads of fields arrays this
|
||||
* scanner cannot follow (`...NotionBlock.subBlocks`, `...getTrigger('x').subBlocks`). `[]` is
|
||||
* reserved for a block that genuinely exposes no fields, because `[]` strips every hidden param
|
||||
* from the page. Throws {@link SubBlockParseError} when the array is there but unreadable.
|
||||
*/
|
||||
export function extractUserSettableParamIds(
|
||||
blockContent: string,
|
||||
blockName = 'block'
|
||||
): string[] | null {
|
||||
const scannable = blankStringsAndComments(blockContent)
|
||||
const keyMatch = /\bsubBlocks\s*:/.exec(scannable)
|
||||
if (!keyMatch) return []
|
||||
|
||||
const afterKey = keyMatch.index + keyMatch[0].length
|
||||
const literalMatch = /^\s*\[/.exec(scannable.slice(afterKey))
|
||||
if (!literalMatch) {
|
||||
throw new SubBlockParseError(
|
||||
`${blockName}: subBlocks is built by an expression rather than an array literal, so the fields it contributes cannot be read`
|
||||
)
|
||||
}
|
||||
|
||||
const arrayStart = afterKey + literalMatch[0].length - 1
|
||||
const arrayEnd = findMatchingClose(scannable, arrayStart, '[', ']')
|
||||
if (arrayEnd === -1) {
|
||||
throw new SubBlockParseError(
|
||||
`${blockName}: found a subBlocks array but could not locate its closing bracket`
|
||||
)
|
||||
}
|
||||
|
||||
const ids = new Set<string>()
|
||||
let elementsWithoutIds = 0
|
||||
|
||||
/**
|
||||
* Text of the array's own elements with every object literal, call argument and nested
|
||||
* bracket elided, so each remaining comma-separated segment is one element's head. Used to
|
||||
* tell an element that names an existing fields array from one that hides its fields behind
|
||||
* a helper call.
|
||||
*/
|
||||
let elementHeads = ''
|
||||
let nesting = 0
|
||||
let i = arrayStart + 1
|
||||
|
||||
while (i < arrayEnd - 1) {
|
||||
const char = scannable[i]
|
||||
|
||||
if (nesting === 0 && char === '{') {
|
||||
const objectEnd = findMatchingClose(scannable, i)
|
||||
if (objectEnd === -1) break
|
||||
|
||||
let depth = 0
|
||||
let topLevel = ''
|
||||
const sourceIndices: number[] = []
|
||||
for (let k = i; k < objectEnd; k++) {
|
||||
const inner = scannable[k]
|
||||
if (inner === '{' || inner === '[') {
|
||||
depth++
|
||||
continue
|
||||
}
|
||||
if (inner === '}' || inner === ']') {
|
||||
depth--
|
||||
continue
|
||||
}
|
||||
if (depth === 1) {
|
||||
topLevel += inner
|
||||
sourceIndices.push(k)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching runs on the blanked characters, so an `id:` sitting inside a string value or a
|
||||
* `//` comment cannot be mistaken for the subBlock's own id. Blanking keeps a string's
|
||||
* quotes and its length, so the matched literal's value is read back character by character
|
||||
* from the original content at the indices the blanked copy matched at.
|
||||
*/
|
||||
const readLiteral = (match: RegExpExecArray): string => {
|
||||
const valueStart = match.index + match[0].length - 1 - match[1].length
|
||||
let value = ''
|
||||
for (let offset = 0; offset < match[1].length; offset++) {
|
||||
value += blockContent[sourceIndices[valueStart + offset]]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const idMatch = /\bid\s*:\s*['"]([^'"]+)['"]/.exec(topLevel)
|
||||
if (idMatch) ids.add(readLiteral(idMatch))
|
||||
const canonicalMatch = /\bcanonicalParamId\s*:\s*['"]([^'"]+)['"]/.exec(topLevel)
|
||||
if (canonicalMatch) ids.add(readLiteral(canonicalMatch))
|
||||
|
||||
/**
|
||||
* An object that spreads an existing subBlock to override one property
|
||||
* (`{ ...sb, required: true }`) legitimately carries no id of its own — the id comes from
|
||||
* the spread source. Only an object with neither an id nor a spread means the scan failed.
|
||||
*/
|
||||
if (!idMatch && !canonicalMatch && !topLevel.includes('...')) elementsWithoutIds++
|
||||
|
||||
i = objectEnd
|
||||
continue
|
||||
}
|
||||
|
||||
if (char === '(' || char === '[') {
|
||||
nesting++
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (char === ')' || char === ']') {
|
||||
nesting--
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (nesting === 0) elementHeads += char
|
||||
i++
|
||||
}
|
||||
|
||||
/**
|
||||
* Any id at all means the array was read and the page keeps a populated Input table. An
|
||||
* opaque element alongside real ids can only omit extra rows — the long-standing limitation
|
||||
* that a spread contributes ids this scanner never sees — and is not this guard's business.
|
||||
* The guard exists solely to stop an empty result, because empty is what strips every hidden
|
||||
* param from the page.
|
||||
*/
|
||||
if (ids.size > 0) return [...ids]
|
||||
|
||||
if (elementsWithoutIds > 0) {
|
||||
throw new SubBlockParseError(
|
||||
`${blockName}: subBlocks array holds object literals but no id was extracted`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero ids is a legitimate answer only when every element names an existing fields array
|
||||
* (`...NotionBlock.subBlocks`, `...getTrigger('x').subBlocks`, `...Base.subBlocks.filter(…)`),
|
||||
* because those fields reach the page through the spread base instead. An element that is a
|
||||
* bare helper call (`...getSlackV2ActionSubBlocks()`) hides whatever fields the helper builds,
|
||||
* and used to yield a silent empty array indistinguishable from a spread-only block.
|
||||
*/
|
||||
const segments = elementHeads
|
||||
.split(',')
|
||||
.map((segment) => segment.trim())
|
||||
.filter((segment) => segment.length > 0)
|
||||
const opaque = segments.filter((segment) => !segment.includes('.subBlocks'))
|
||||
if (opaque.length > 0) {
|
||||
throw new SubBlockParseError(
|
||||
`${blockName}: subBlocks array yielded no ids and element${
|
||||
opaque.length > 1 ? 's' : ''
|
||||
} ${opaque.map((segment) => `\`${segment}\``).join(', ')} do not name a fields array`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Every element named a fields array this scanner cannot follow, so the block's fields are
|
||||
* UNKNOWN, not empty. Returning `[]` here would assert the block supplies nothing and strip
|
||||
* every hidden param from its tools' Input tables with no warning — the silent false-drop the
|
||||
* `null` state exists to prevent. Only a genuinely empty array (`subBlocks: []`) reaches the
|
||||
* `[]` below.
|
||||
*/
|
||||
if (segments.length > 0) return null
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the bodies of every `tools.config.params` mapper in `scannable`.
|
||||
*
|
||||
* Returns `[start, end)` index pairs into `scannable` (a length-preserving blanked copy, so
|
||||
* the same indices address the original content).
|
||||
*
|
||||
* In production this only ever runs on a single block's slice, which holds at most one
|
||||
* `subBlocks:` key — the loop over every `tools` object is defensive rather than required, and
|
||||
* the multi-block file it was once justified by (Textract's v1 and v2) is split before it gets
|
||||
* here. The tests do pass whole files, so the loop is exercised on wider input than production
|
||||
* ever supplies.
|
||||
*
|
||||
* Handles `params: (params) => { ... }`, the concise `params: (params) => ({ ... })` form, the
|
||||
* `async` and generic-annotated variants, and method shorthand. Candidates are tried in order
|
||||
* rather than only the first, because a decoy key that is not a mapper at all
|
||||
* (`params: (GitHubBlock.tools?.config as any)?.params`) would otherwise mask the real one.
|
||||
*/
|
||||
function findMapperBodyRanges(scannable: string): [number, number][] {
|
||||
const ranges: [number, number][] = []
|
||||
const toolsRegex = /\btools\s*:\s*\{/g
|
||||
let toolsMatch: RegExpExecArray | null
|
||||
|
||||
while ((toolsMatch = toolsRegex.exec(scannable)) !== null) {
|
||||
const toolsEnd = findMatchingClose(scannable, toolsMatch.index + toolsMatch[0].length - 1)
|
||||
if (toolsEnd === -1) continue
|
||||
toolsRegex.lastIndex = toolsEnd
|
||||
|
||||
const toolsRegion = scannable.slice(toolsMatch.index, toolsEnd)
|
||||
const configMatch = /\bconfig\s*:\s*\{/.exec(toolsRegion)
|
||||
if (!configMatch) continue
|
||||
const configStart = toolsMatch.index + configMatch.index + configMatch[0].length - 1
|
||||
const configEnd = findMatchingClose(scannable, configStart)
|
||||
if (configEnd === -1) continue
|
||||
|
||||
const configRegion = scannable.slice(configStart, configEnd)
|
||||
const paramsRegex = /\bparams\s*(?::\s*(?:async\s*)?(?:<[^<>]*>\s*)?)?\(/g
|
||||
let paramsMatch: RegExpExecArray | null
|
||||
|
||||
while ((paramsMatch = paramsRegex.exec(configRegion)) !== null) {
|
||||
const argsStart = configStart + paramsMatch.index + paramsMatch[0].length - 1
|
||||
const argsEnd = findMatchingClose(scannable, argsStart, '(', ')')
|
||||
if (argsEnd === -1) continue
|
||||
|
||||
const afterArgs = scannable.slice(argsEnd, configEnd)
|
||||
const bodyMatch = /^\s*(?::[^=({]*)?(?:=>\s*)?([({])/.exec(afterArgs)
|
||||
if (!bodyMatch) continue
|
||||
const open = bodyMatch[1] as '(' | '{'
|
||||
const bodyStart = argsEnd + bodyMatch[0].length - 1
|
||||
const bodyEnd = findMatchingClose(scannable, bodyStart, open, open === '(' ? ')' : '}')
|
||||
if (bodyEnd === -1) continue
|
||||
ranges.push([bodyStart, bodyEnd])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ranges
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the shorthand property names of every object literal in `body` to `into`.
|
||||
*
|
||||
* `{ file }` names the `file` param exactly as `{ file: value }` does, but carries no colon,
|
||||
* so the key scan below cannot see it — a mapper written in the idiomatic shorthand form used
|
||||
* to drop the param from the docs silently, which is the one failure mode this whole filter
|
||||
* exists to prevent.
|
||||
*
|
||||
* Only the depth-1 comma segments of a brace-matched region are read, and a segment that
|
||||
* opens a call or an index is marked so it can no longer look like a bare identifier. That
|
||||
* keeps argument lists (`fn(a, b, c)`), calls (`{ doWork() }`) and nested values
|
||||
* (`{ a: { b: 1 }, file }`) from contributing names, while `{ ...rest, file }` still yields
|
||||
* `file` because `...rest` is not an identifier on its own.
|
||||
*/
|
||||
function collectShorthandPropertyNames(body: string, into: Set<string>): void {
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
if (body[i] !== '{') continue
|
||||
|
||||
const objectEnd = findMatchingClose(body, i)
|
||||
if (objectEnd === -1) continue
|
||||
|
||||
const segments: string[] = []
|
||||
let current = ''
|
||||
let depth = 0
|
||||
|
||||
for (let k = i; k < objectEnd; k++) {
|
||||
const char = body[k]
|
||||
if (char === '{' || char === '[' || char === '(') {
|
||||
depth++
|
||||
if (depth === 2) current += '#'
|
||||
continue
|
||||
}
|
||||
if (char === '}' || char === ']' || char === ')') {
|
||||
depth--
|
||||
continue
|
||||
}
|
||||
if (depth !== 1) continue
|
||||
if (char === ',') {
|
||||
segments.push(current)
|
||||
current = ''
|
||||
continue
|
||||
}
|
||||
current += char
|
||||
}
|
||||
segments.push(current)
|
||||
|
||||
for (const segment of segments) {
|
||||
const name = segment.trim()
|
||||
if (/^[A-Za-z_$][\w$]*$/.test(name)) into.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the tool-param names a block's own `tools.config.params` mapper writes.
|
||||
*
|
||||
* A block can supply a hidden tool param without ever declaring a subBlock of that name:
|
||||
* Cal.com assembles `result.attendee` from `attendeeName`/`attendeeEmail`/`attendeeTimeZone`,
|
||||
* JSM renames its `assetWorkspaceId` field to `workspaceId`, and Textract renames its
|
||||
* `document` field to `file`. All are user-driven and must stay documented, so this scan
|
||||
* catches both shapes — `<anyIdentifier>.<param> = …` assignments (the accumulator is named
|
||||
* `result` in one block and `parameters` in another, so the name is never assumed) and
|
||||
* `<param>:` keys of the objects the mapper returns.
|
||||
*
|
||||
* The rule is deliberately biased toward keeping. Object keys are collected without proving
|
||||
* they are top-level params, so a key on a nested object (Cal.com's `attendee.name`) can keep
|
||||
* a same-named hidden param that the mapper never actually supplies. That false keep costs a
|
||||
* reader one hard-to-set row; a false drop hides a required input — the exact failure this
|
||||
* filter exists to prevent, and one it has already caused. When the two are in tension, keep.
|
||||
*
|
||||
* Scanning runs on the blanked copy, so a commented-out or string-embedded mapper cannot
|
||||
* contribute names.
|
||||
*/
|
||||
export function extractMapperWrittenParamIds(blockContent: string): string[] {
|
||||
const scannable = blankStringsAndComments(blockContent)
|
||||
const ids = new Set<string>()
|
||||
|
||||
for (const [start, end] of findMapperBodyRanges(scannable)) {
|
||||
const body = scannable.slice(start, end)
|
||||
|
||||
const assignmentRegex = /(?:^|[^.\w$])[A-Za-z_$][\w$]*\.([A-Za-z_$][\w$]*)\s*=(?!=)/g
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = assignmentRegex.exec(body)) !== null) ids.add(match[1])
|
||||
|
||||
const keyRegex = /(?:^|[^?.\w$])([A-Za-z_$][\w$]*)\s*:/g
|
||||
while ((match = keyRegex.exec(body)) !== null) ids.add(match[1])
|
||||
|
||||
/**
|
||||
* Quoted keys survive blanking only as their delimiters, so the name is read back from
|
||||
* the original content at the same index — `blankStringsAndComments` is length-preserving.
|
||||
*/
|
||||
const quotedKeyRegex = /(['"])[^'"\n]*\1\s*:/g
|
||||
while ((match = quotedKeyRegex.exec(body)) !== null) {
|
||||
const keyStart = start + match.index
|
||||
const original = blockContent.slice(keyStart, keyStart + match[0].length)
|
||||
const name = /(['"])([A-Za-z_$][\w$]*)\1/.exec(original)?.[2]
|
||||
if (name) ids.add(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* A computed write (`result['file'] = …`) supplies a param exactly as `result.file = …`
|
||||
* does. Like the quoted keys above, the name only survives blanking as its delimiters and
|
||||
* is read back from the original content at the matched index.
|
||||
*/
|
||||
const bracketAssignmentRegex =
|
||||
/(?:^|[^.\w$])[A-Za-z_$][\w$]*\s*\[\s*(['"])[^'"\n]*\1\s*\]\s*=(?!=)/g
|
||||
while ((match = bracketAssignmentRegex.exec(body)) !== null) {
|
||||
const matchStart = start + match.index
|
||||
const original = blockContent.slice(matchStart, matchStart + match[0].length)
|
||||
const name = /\[\s*(['"])([A-Za-z_$][\w$]*)\1\s*\]/.exec(original)?.[2]
|
||||
if (name) ids.add(name)
|
||||
}
|
||||
|
||||
collectShorthandPropertyNames(body, ids)
|
||||
}
|
||||
|
||||
return [...ids]
|
||||
}
|
||||
|
||||
/** What {@link extractBlockSuppliedParamIds} could and could not read off a block. */
|
||||
export interface BlockSuppliedParams {
|
||||
/**
|
||||
* Every param the block supplies, or `null` when what its `subBlocks` array contributes is
|
||||
* unknown — either the array could not be read (`parseError` set) or it holds only spreads of
|
||||
* fields arrays this scanner cannot follow (`parseError` null).
|
||||
*
|
||||
* `null` is UNKNOWN and is deliberately distinct from `[]`: an empty array asserts the block
|
||||
* supplies nothing, which strips every hidden param from the page, while `null` says the scan
|
||||
* failed and the filter must be skipped entirely for this block.
|
||||
*/
|
||||
ids: string[] | null
|
||||
/**
|
||||
* The params the block's `tools.config.params` mapper writes. Collected even when the
|
||||
* `subBlocks` scan failed, so a spread-inheriting block does not lose its mapper's renames
|
||||
* along with its own fields.
|
||||
*/
|
||||
mapperIds: string[]
|
||||
/** Why the `subBlocks` scan failed, when it did. `null` on success. */
|
||||
parseError: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Every param name the block itself supplies — via a user-facing `subBlocks` field or via
|
||||
* its `tools.config.params` mapper. A hidden tool param in this set stays in the public
|
||||
* Input table; the rest are genuinely resolver-derived and stay filtered out.
|
||||
*
|
||||
* An unreadable `subBlocks` array is reported as `ids: null` rather than thrown, because the
|
||||
* only safe response to "the fields are unknown" is to stop filtering, never to filter against
|
||||
* an empty set. The mapper scan runs first so its result survives that failure.
|
||||
*/
|
||||
export function extractBlockSuppliedParamIds(
|
||||
blockContent: string,
|
||||
blockName = 'block'
|
||||
): BlockSuppliedParams {
|
||||
const mapperIds = extractMapperWrittenParamIds(blockContent)
|
||||
|
||||
try {
|
||||
const settableIds = extractUserSettableParamIds(blockContent, blockName)
|
||||
if (settableIds === null) return { ids: null, mapperIds, parseError: null }
|
||||
return { ids: [...new Set([...settableIds, ...mapperIds])], mapperIds, parseError: null }
|
||||
} catch (error) {
|
||||
if (!(error instanceof SubBlockParseError)) throw error
|
||||
return { ids: null, mapperIds, parseError: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract operation options from the subBlock with id: 'operation' (if present).
|
||||
* Returns { label, id } pairs — label is the display name, id is the option's id field
|
||||
@@ -842,7 +1262,17 @@ function extractAuthType(blockContent: string): 'oauth' | 'api-key' | 'none' {
|
||||
function blankStringsAndComments(content: string): string {
|
||||
return content.replace(
|
||||
/(['"`])(?:\\[\s\S]|(?!\1)[^\\])*\1|\/\/[^\n]*|\/\*[\s\S]*?\*\//g,
|
||||
(match) => match[0] + match.slice(1, -1).replace(/[^\n]/g, ' ') + match[match.length - 1]
|
||||
(match: string, quote: string | undefined) => {
|
||||
const blanked = match.replace(/[^\n]/g, ' ')
|
||||
/**
|
||||
* A comment has no delimiters worth preserving, so it is blanked whole. Keeping
|
||||
* its final character would leak arbitrary source text — commented-out code ending
|
||||
* in `[` or `{` leaves an unbalanced bracket that derails every scan downstream.
|
||||
* A quoted string keeps its own quotes so callers can still see where it began.
|
||||
*/
|
||||
if (quote === undefined) return blanked
|
||||
return quote + blanked.slice(1, -1) + quote
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1224,7 +1654,7 @@ async function writeIntegrationsJson(iconMapping: Record<string, IconRef>): Prom
|
||||
/**
|
||||
* Extract ALL block configs from a file, filtering out hidden blocks
|
||||
*/
|
||||
function extractAllBlockConfigs(fileContent: string): BlockConfig[] {
|
||||
export function extractAllBlockConfigs(fileContent: string): BlockConfig[] {
|
||||
const configs: BlockConfig[] = []
|
||||
|
||||
// First, extract the primary icon from the file (for V2 blocks that inherit via spread)
|
||||
@@ -1356,6 +1786,63 @@ function extractBlockConfigFromContent(
|
||||
|
||||
const operations = extractOperationsFromContent(blockContent)
|
||||
const triggerIds = extractTriggersAvailable(blockContent, fileContent)
|
||||
const supplied = extractBlockSuppliedParamIds(blockContent, blockName)
|
||||
/**
|
||||
* `null` on the base means the base's own scan failed, which is not the same as a block
|
||||
* with no base at all (`undefined`) — the fields the base contributes are unknown, so
|
||||
* anything inheriting them is unknown too.
|
||||
*/
|
||||
const baseParamIds: string[] | null | undefined = (baseConfig as any)?.userSettableParamIds
|
||||
const baseSettableParamIds: string[] = baseParamIds ?? []
|
||||
|
||||
/**
|
||||
* `null` means UNKNOWN and disables the hidden-param filter for this block, restoring the
|
||||
* pre-filter behaviour of documenting every param. An unreadable `subBlocks` array must
|
||||
* never be collapsed into `[]`, because `[]` asserts the block supplies nothing and strips
|
||||
* every hidden param from the page, and it must never abort the run either — one block the
|
||||
* scanner cannot read used to brick the generator for the entire repository.
|
||||
*/
|
||||
let userSettableParamIds: string[] | null
|
||||
if (supplied.parseError !== null) {
|
||||
/**
|
||||
* A block that spreads a base still documents the base's fields, so the filter can stay
|
||||
* on and lose at most the fields this block adds on top of the base — plus its mapper's
|
||||
* renames, which are read even when the `subBlocks` scan fails. With no base to fall back
|
||||
* on there is nothing to filter against, so the filter is switched off entirely.
|
||||
*/
|
||||
const fallback =
|
||||
baseSettableParamIds.length > 0
|
||||
? [...new Set([...baseSettableParamIds, ...supplied.mapperIds])]
|
||||
: null
|
||||
if (!subBlockParseWarnings.has(supplied.parseError)) {
|
||||
subBlockParseWarnings.add(supplied.parseError)
|
||||
console.warn(
|
||||
`⚠ ${supplied.parseError}; ${
|
||||
fallback
|
||||
? "documenting the spread base's fields instead"
|
||||
: 'documenting every param of its tools instead of filtering'
|
||||
}`
|
||||
)
|
||||
}
|
||||
userSettableParamIds = fallback
|
||||
} else if (baseParamIds === null) {
|
||||
userSettableParamIds = null
|
||||
} else if (supplied.ids === null) {
|
||||
/**
|
||||
* The block's `subBlocks` array holds only spreads of fields arrays this scanner cannot
|
||||
* follow. A config-level spread base still contributes its readable fields, so the filter
|
||||
* stays on against those plus the mapper's renames; with no base there is nothing to
|
||||
* filter against and the filter is switched off. No warning: unlike the `parseError`
|
||||
* cases the array itself parsed fine, and every field it names is documented through the
|
||||
* spread source's own page.
|
||||
*/
|
||||
userSettableParamIds =
|
||||
baseSettableParamIds.length > 0
|
||||
? [...new Set([...baseSettableParamIds, ...supplied.mapperIds])]
|
||||
: null
|
||||
} else {
|
||||
userSettableParamIds = [...new Set([...supplied.ids, ...baseSettableParamIds])]
|
||||
}
|
||||
const docsLink =
|
||||
extractStringPropertyFromContent(blockContent, 'docsLink', true) ||
|
||||
baseConfig?.docsLink ||
|
||||
@@ -1387,6 +1874,7 @@ function extractBlockConfigFromContent(
|
||||
access: finalToolsAccess.length > 0 ? finalToolsAccess : baseConfig?.tools?.access || [],
|
||||
},
|
||||
operations: operations.length > 0 ? operations : (baseConfig as any)?.operations || [],
|
||||
userSettableParamIds,
|
||||
triggerIds: triggerIds.length > 0 ? triggerIds : (baseConfig as any)?.triggerIds || [],
|
||||
docsLink,
|
||||
...(integrationType ? { integrationType } : {}),
|
||||
@@ -2200,7 +2688,8 @@ export function extractToolInfo(
|
||||
fileContent: string,
|
||||
factorySource = '',
|
||||
toolFilePath = '',
|
||||
rootDir = ''
|
||||
rootDir = '',
|
||||
userSettableParamIdSet: ReadonlySet<string> | null = null
|
||||
): {
|
||||
description: string
|
||||
params: Array<{ name: string; type: string; required: boolean; description: string }>
|
||||
@@ -2353,6 +2842,23 @@ export function extractToolInfo(
|
||||
continue
|
||||
}
|
||||
|
||||
/**
|
||||
* `visibility: 'hidden'` means the param is not an LLM-settable tool argument, so
|
||||
* it must not appear in the public Input table — emitting it tells integrators they
|
||||
* can override a value they cannot reach, and several such params are credential-
|
||||
* shaped (idToken, instanceUrl, apiToken). The exception is a param the owning block
|
||||
* still exposes as a field the user types: Mailchimp's `apiKey` is hidden on every
|
||||
* tool because the block injects it, yet the block's own `apiKey` subBlock is the
|
||||
* only place the requirement is documented. Keep those; drop the rest.
|
||||
*/
|
||||
if (
|
||||
userSettableParamIdSet !== null &&
|
||||
/visibility\s*:\s*['"]hidden['"]/.test(paramBlock) &&
|
||||
!userSettableParamIdSet.has(paramName)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
const typeMatch = paramBlock.match(/type\s*:\s*['"]([^'"]+)['"]/)
|
||||
const requiredMatch = paramBlock.match(/required\s*:\s*(true|false)/)
|
||||
|
||||
@@ -2954,11 +3460,17 @@ export function parsePropertiesContent(
|
||||
return properties
|
||||
}
|
||||
|
||||
export async function getToolInfo(toolName: string): Promise<{
|
||||
export async function getToolInfo(
|
||||
toolName: string,
|
||||
userSettableParamIds: readonly string[] | null = null
|
||||
): Promise<{
|
||||
description: string
|
||||
params: Array<{ name: string; type: string; required: boolean; description: string }>
|
||||
outputs: Record<string, any>
|
||||
} | null> {
|
||||
const userSettableParamIdSet =
|
||||
userSettableParamIds === null ? null : new Set(userSettableParamIds)
|
||||
|
||||
try {
|
||||
const metadata = (await loadToolMetadata())[toolName]
|
||||
const parts = toolName.split('_')
|
||||
@@ -3090,14 +3602,27 @@ export async function getToolInfo(toolName: string): Promise<{
|
||||
toolFileContent,
|
||||
resolveFactorySource(toolFileContent, foundFile, rootDir),
|
||||
foundFile,
|
||||
rootDir
|
||||
rootDir,
|
||||
userSettableParamIdSet
|
||||
)
|
||||
: null
|
||||
|
||||
if (!metadata) return sourceInfo
|
||||
|
||||
/**
|
||||
* The same hidden-param rule `extractToolInfo` applies to source-parsed params, applied to the
|
||||
* metadata-derived ones. `tool-metadata.ts` carries `visibility` for every param, so this is the
|
||||
* authoritative form of the check; the source-side filter remains for tools with no metadata
|
||||
* entry. A null set means the block's subBlocks could not be read, so nothing is filtered.
|
||||
*/
|
||||
const params = Object.entries(metadata.params ?? {})
|
||||
.filter(([name]) => name !== 'accessToken')
|
||||
.filter(
|
||||
([name, param]) =>
|
||||
userSettableParamIdSet === null ||
|
||||
param.visibility !== 'hidden' ||
|
||||
userSettableParamIdSet.has(name)
|
||||
)
|
||||
.map(([name, param]) => ({
|
||||
name,
|
||||
type: typeof param.type === 'string' ? param.type : 'string',
|
||||
@@ -3263,6 +3788,7 @@ async function generateMarkdownForBlock(
|
||||
bgColor,
|
||||
outputs = {},
|
||||
tools = { access: [] },
|
||||
userSettableParamIds = null,
|
||||
} = blockConfig
|
||||
|
||||
let outputsSection = ''
|
||||
@@ -3325,7 +3851,7 @@ async function generateMarkdownForBlock(
|
||||
toolsSection += `### ${heading ?? stripVersionSuffix(tool)}\n\n`
|
||||
|
||||
console.log(`Getting info for tool: ${tool}`)
|
||||
const toolInfo = await getToolInfo(tool)
|
||||
const toolInfo = await getToolInfo(tool, userSettableParamIds)
|
||||
|
||||
if (toolInfo) {
|
||||
if (toolInfo.description && toolInfo.description !== 'No description available') {
|
||||
@@ -4057,6 +4583,8 @@ async function generateAllTriggerDocs(): Promise<void> {
|
||||
|
||||
async function generateAllBlockDocs() {
|
||||
try {
|
||||
const blockFiles = (await glob(`${BLOCKS_PATH}/*.ts`)).sort()
|
||||
|
||||
copyIconsFile()
|
||||
|
||||
const docsIconMapping = await generateIconMapping({ includeHidden: true })
|
||||
@@ -4071,8 +4599,6 @@ async function generateAllBlockDocs() {
|
||||
const validToolDocs = await getCanonicalToolDocNames()
|
||||
cleanupStaleToolDocs(validToolDocs)
|
||||
|
||||
const blockFiles = (await glob(`${BLOCKS_PATH}/*.ts`)).sort()
|
||||
|
||||
for (const blockFile of blockFiles) {
|
||||
await generateBlockDoc(blockFile)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user