@wix/mcp 1.0.80 → 1.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/bin-standalone.js +72 -28
- package/build/cjs/index.cjs +90 -361
- package/build/cjs/toolkit.cjs +81 -30
- package/build/dts/api-call/index.js +41 -3
- package/build/dts/code-mode/index.js +8 -2
- package/build/dts/config/api-request-rules.d.ts +11 -0
- package/build/dts/config/api-request-rules.js +11 -0
- package/build/dts/config/default-config.js +8 -2
- package/build/dts/config/param-descriptions.d.ts +1 -1
- package/build/dts/config/param-descriptions.js +5 -3
- package/build/dts/docs/docs.js +6 -6
- package/build/dts/infra/tool-bi.d.ts +1 -1
- package/build/dts/infra/tool-bi.js +3 -2
- package/build/dts/toolkit-entry.d.cts +1 -1
- package/build/dts/toolkit-entry.d.ts +1 -1
- package/build/dts/toolkit.js +4 -1
- package/build/dts/wix-mcp-server.d.ts +2 -0
- package/build/dts/wix-mcp-server.js +4 -1
- package/build/dts/wixel/api.d.ts +0 -60
- package/build/dts/wixel/api.js +1 -50
- package/build/dts/wixel/constants.d.ts +0 -1
- package/build/dts/wixel/constants.js +0 -1
- package/build/dts/wixel/index.d.ts +0 -3
- package/build/dts/wixel/index.js +0 -9
- package/build/esm/index.js +90 -358
- package/build/esm/toolkit.js +81 -30
- package/package.json +3 -3
- package/build/dts/wixel/open-headless-wixel-project-tool.d.ts +0 -6
- package/build/dts/wixel/open-headless-wixel-project-tool.js +0 -84
- package/build/dts/wixel/pull-headless-wixel-job-tool.d.ts +0 -6
- package/build/dts/wixel/pull-headless-wixel-job-tool.js +0 -61
- package/build/dts/wixel/update-headless-wixel-job-tool.d.ts +0 -6
- package/build/dts/wixel/update-headless-wixel-job-tool.js +0 -106
package/build/cjs/toolkit.cjs
CHANGED
|
@@ -17648,6 +17648,9 @@ function rethrowIfUnauthorized(error) {
|
|
|
17648
17648
|
}
|
|
17649
17649
|
}
|
|
17650
17650
|
|
|
17651
|
+
// src/config/api-request-rules.ts
|
|
17652
|
+
var ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
|
|
17653
|
+
|
|
17651
17654
|
// src/config/default-config.ts
|
|
17652
17655
|
var AGENT_GOAL = dedent_default`
|
|
17653
17656
|
You are an agent that helps the user manage their Wix site.
|
|
@@ -17769,11 +17772,13 @@ var EXECUTE_WIX_API_INTRO = dedent_default`
|
|
|
17769
17772
|
CODE CONTRACT:
|
|
17770
17773
|
- Pass an async function expression: \`async function() { ... }\` or \`async () => { ... }\`. Put every declaration, \`await\`, and \`return\` inside it, and do not invoke it yourself.
|
|
17771
17774
|
- Use only \`wix.request({ method, url, body })\` and in-memory JavaScript. Node.js modules, filesystem access, and downloadable-file creation are unavailable.
|
|
17775
|
+
- Do not minify the code. Put each object property on its own line, with spaces after colons and commas, and declare deep literals (request bodies, filters, patch lists) as a named \`const\` before use so each \`wix.request({ ... })\` argument stays short.
|
|
17772
17776
|
|
|
17773
17777
|
CONFIRM API CONTRACTS:
|
|
17774
17778
|
- Do not rely on memory for endpoints, methods, bodies, response paths, filters, enum values, or auth scope. Confirm every method from conversation-provided details, a complete matching docs example, or the schema/docs tools before executing.
|
|
17775
17779
|
- Prefer \`method.publicUrl\` from SearchWixAPISpec; never use \`method.servers[0]\`. Pass all supporting documentation URLs through \`sourceDocUrls\`.
|
|
17776
17780
|
- A response field is not automatically filterable or sortable. Use only capabilities declared for that exact query/search method. If an unsupported filter is unavoidable, fetch a bounded result set and filter it in code.
|
|
17781
|
+
- ${ARRAY_QUERY_PARAMS_RULE}
|
|
17777
17782
|
- Read-only probes may inspect state or resolve IDs. Never use speculative create/update/delete calls to discover request or response shapes, and never repeat a successful mutation merely to obtain more response data.
|
|
17778
17783
|
`;
|
|
17779
17784
|
var EXECUTE_WIX_API_OUTPUT_AND_ERRORS = dedent_default`
|
|
@@ -17789,6 +17794,7 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
17789
17794
|
SCOPE AND SITE TARGETING:
|
|
17790
17795
|
- Set \`scope: "site"\` for site-level APIs and \`scope: "account"\` for account-level APIs. Account-level acts on the account itself, or on sites as objects within it; anything that reads or writes inside a site is site-level.
|
|
17791
17796
|
- If \`scope\` is omitted it defaults to site when either the ExecuteWixAPI call or \`wix.request\` provides a \`siteId\`; otherwise it defaults to account.
|
|
17797
|
+
- Site-level calls need a site ID; there is no implicit current site. Before any \`scope: "site"\` call, pass the site's ID as the ExecuteWixAPI \`siteId\` parameter or per request. \`scope: "site"\` without a \`siteId\` fails.
|
|
17792
17798
|
- \`scope: "account"\` ignores any \`siteId\` you pass, so a site-level endpoint called that way fails.
|
|
17793
17799
|
- Authentication is injected automatically; never set Authorization, wix-site-id, or wix-account-id headers.
|
|
17794
17800
|
- For one named site, prefer GetSiteContext. If it is unavailable, resolve the exact site and perform the action in the same invocation.
|
|
@@ -18109,8 +18115,10 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18109
18115
|
SearchBuildAppsDocumentation: dedent_default`
|
|
18110
18116
|
Searches the Wix Build Apps documentation.
|
|
18111
18117
|
`,
|
|
18112
|
-
|
|
18113
|
-
|
|
18118
|
+
SearchDevelopmentDocumentation: dedent_default`
|
|
18119
|
+
Broad Wix development documentation (platform concepts, developer guides, getting started). **Last resort — development tasks only.**
|
|
18120
|
+
Skip it entirely for general, business, support, or Wix-editor questions.
|
|
18121
|
+
Call it only after the specific search for your task returned nothing useful: \`SearchWixRESTDocumentation\` (REST), \`SearchWixSDKDocumentation\` (JS SDK), \`SearchWixCLIDocumentation\` (CLI), \`SearchBuildAppsDocumentation\` (apps), \`SearchWixHeadlessDocumentation\` (Headless), \`SearchWixWDSDocumentation\` (WDS). Never use it as a first search.
|
|
18114
18122
|
`,
|
|
18115
18123
|
SearchWixHeadlessDocumentation: dedent_default`
|
|
18116
18124
|
Searches the Wix Headless documentation.
|
|
@@ -18762,14 +18770,20 @@ function renderApps(apps, knownApps) {
|
|
|
18762
18770
|
}
|
|
18763
18771
|
function renderProperties(props) {
|
|
18764
18772
|
const sections = [];
|
|
18765
|
-
const
|
|
18766
|
-
if (props.language)
|
|
18767
|
-
if (props.locale?.country)
|
|
18768
|
-
|
|
18769
|
-
if (props.
|
|
18770
|
-
if (
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
+
const regionalFormat = [];
|
|
18774
|
+
if (props.language) regionalFormat.push(`- Language: **${props.language}**`);
|
|
18775
|
+
if (props.locale?.country)
|
|
18776
|
+
regionalFormat.push(`- Locale region: **${props.locale.country}**`);
|
|
18777
|
+
if (props.timeZone) regionalFormat.push(`- Timezone: **${props.timeZone}**`);
|
|
18778
|
+
if (props.paymentCurrency)
|
|
18779
|
+
regionalFormat.push(`- Currency: **${props.paymentCurrency}**`);
|
|
18780
|
+
if (regionalFormat.length) {
|
|
18781
|
+
sections.push(`**Regional Format**
|
|
18782
|
+
${regionalFormat.join("\n")}`);
|
|
18783
|
+
}
|
|
18784
|
+
if (props.address?.country) {
|
|
18785
|
+
sections.push(`**Location**
|
|
18786
|
+
- Country: **${props.address.country}**`);
|
|
18773
18787
|
}
|
|
18774
18788
|
const contact = [];
|
|
18775
18789
|
if (props.email) contact.push(`- Email: **${props.email}**`);
|
|
@@ -18916,6 +18930,7 @@ var known_apps_default = {
|
|
|
18916
18930
|
"225dd912-7dea-4738-8688-4b8c6955ffc2": "Wix Forms",
|
|
18917
18931
|
"d80111c5-a0f4-47a8-b63a-65b54d774a27": "Wix Gift Cards",
|
|
18918
18932
|
"148c2287-c669-d849-d153-463c7486a694": "Wix Groups",
|
|
18933
|
+
"14bca956-e09f-f4d6-14d7-466cb3f09103": "Wix Hotels",
|
|
18919
18934
|
"13ee94c1-b635-8505-3391-97919052c16f": "Wix Invoices",
|
|
18920
18935
|
"14cc59bc-f0b7-15b8-e1c7-89ce41d0e0c9": "Wix Members Area",
|
|
18921
18936
|
"14d84998-ae09-1abf-c6fc-3f3cace5bf19": "Wix Multilingual",
|
|
@@ -18950,8 +18965,8 @@ async function getDynamicContext(auth, httpClient, filter, options) {
|
|
|
18950
18965
|
partnerContextEnabled: options,
|
|
18951
18966
|
includeAccountContext: options
|
|
18952
18967
|
} : options;
|
|
18953
|
-
const includePartnerContext = contextOptions?.partnerContextEnabled ??
|
|
18954
|
-
const includeAccountContext = contextOptions?.includeAccountContext ??
|
|
18968
|
+
const includePartnerContext = contextOptions?.partnerContextEnabled ?? true;
|
|
18969
|
+
const includeAccountContext = contextOptions?.includeAccountContext ?? true;
|
|
18955
18970
|
try {
|
|
18956
18971
|
const accountHeaders = await auth.getAccountAuthHeaders();
|
|
18957
18972
|
const noFilter = !filter?.siteId && !filter?.siteName;
|
|
@@ -19065,7 +19080,8 @@ var paramDescriptions = {
|
|
|
19065
19080
|
"The url of the api to call - ALWAYS get the API url from the Wix REST docs or from the conversation context, the URL MUST BE ABSOLUTE URL",
|
|
19066
19081
|
'NEVER guess the API url, ALWAYS get it from the conversation context, i.e from the user prompt or from the "WixREADME" tool or from the "SearchWixRESTDocumentation" tool or from the "BrowseWixRESTDocsMenu" tool or from the "ReadFullDocsArticle" tool',
|
|
19067
19082
|
`Allowed API urls are: ${ALLOWED_HOSTNAMES.join(", ")}`,
|
|
19068
|
-
`Docs urls like https://dev.wix.com/docs/... are not API urls, if you want to read the docs, use the "ReadFullDocsArticle" tool
|
|
19083
|
+
`Docs urls like https://dev.wix.com/docs/... are not API urls, if you want to read the docs, use the "ReadFullDocsArticle" tool`,
|
|
19084
|
+
ARRAY_QUERY_PARAMS_RULE
|
|
19069
19085
|
].join("\n"),
|
|
19070
19086
|
method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
|
|
19071
19087
|
body: 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
|
|
@@ -19138,8 +19154,8 @@ var paramDescriptions = {
|
|
|
19138
19154
|
searchTerm: "The search term to search for in the Build Apps Documentation",
|
|
19139
19155
|
maxResults: "The maximum number of results to return, default is 10, max is 15"
|
|
19140
19156
|
},
|
|
19141
|
-
|
|
19142
|
-
searchTerm: "The search term
|
|
19157
|
+
SearchDevelopmentDocumentation: {
|
|
19158
|
+
searchTerm: "The development-related search term. Use only after a more specific docs search (REST/SDK/CLI/Build Apps/Headless/WDS) came back empty.",
|
|
19143
19159
|
maxResults: "The maximum number of results to return, default is 10, max is 15"
|
|
19144
19160
|
},
|
|
19145
19161
|
SearchWixHeadlessDocumentation: {
|
|
@@ -19935,15 +19951,15 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19935
19951
|
}
|
|
19936
19952
|
);
|
|
19937
19953
|
}
|
|
19938
|
-
if (allowedTools.includes("OVERVIEW") && !disableTools?.includes("
|
|
19954
|
+
if (allowedTools.includes("OVERVIEW") && !disableTools?.includes("SearchDevelopmentDocumentation")) {
|
|
19939
19955
|
server.tool(
|
|
19940
|
-
"
|
|
19956
|
+
"SearchDevelopmentDocumentation",
|
|
19941
19957
|
getDescription(
|
|
19942
|
-
"
|
|
19943
|
-
defaultToolDescriptions.
|
|
19958
|
+
"SearchDevelopmentDocumentation",
|
|
19959
|
+
defaultToolDescriptions.SearchDevelopmentDocumentation ?? ""
|
|
19944
19960
|
),
|
|
19945
19961
|
(() => {
|
|
19946
|
-
const d = desc("
|
|
19962
|
+
const d = desc("SearchDevelopmentDocumentation");
|
|
19947
19963
|
return {
|
|
19948
19964
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
19949
19965
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -19973,8 +19989,8 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19973
19989
|
panorama.errorMonitor().reportError(error);
|
|
19974
19990
|
captureException(error, {
|
|
19975
19991
|
tags: {
|
|
19976
|
-
componentId: "
|
|
19977
|
-
toolName: "
|
|
19992
|
+
componentId: "SearchDevelopmentDocumentation",
|
|
19993
|
+
toolName: "SearchDevelopmentDocumentation"
|
|
19978
19994
|
}
|
|
19979
19995
|
});
|
|
19980
19996
|
logger.error(
|
|
@@ -20838,6 +20854,19 @@ var MAX_ALLOWED_RESPONSE_CHARS = 2e6;
|
|
|
20838
20854
|
|
|
20839
20855
|
// src/api-call/index.ts
|
|
20840
20856
|
var DEFAULT_MAX_RESPONSE_SIZE = 5e4;
|
|
20857
|
+
function extractReturnedRows(responseData) {
|
|
20858
|
+
if (responseData === null || typeof responseData !== "object") {
|
|
20859
|
+
return void 0;
|
|
20860
|
+
}
|
|
20861
|
+
const data = responseData;
|
|
20862
|
+
const block = data.pagingMetadata ?? data.cursorPagingMetadata ?? data.pagingOffsetMetadata ?? data.metaData ?? data.metadata;
|
|
20863
|
+
const raw = block?.count;
|
|
20864
|
+
if (typeof raw !== "number" && (typeof raw !== "string" || raw.trim() === "")) {
|
|
20865
|
+
return void 0;
|
|
20866
|
+
}
|
|
20867
|
+
const count = Number(raw);
|
|
20868
|
+
return Number.isFinite(count) ? count : void 0;
|
|
20869
|
+
}
|
|
20841
20870
|
function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_SIZE) {
|
|
20842
20871
|
if (typeof responseData === "string" && responseData.trim().startsWith("<!DOCTYPE")) {
|
|
20843
20872
|
return {
|
|
@@ -20845,13 +20874,15 @@ function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_
|
|
|
20845
20874
|
isError: true
|
|
20846
20875
|
};
|
|
20847
20876
|
}
|
|
20877
|
+
const returnedRows = extractReturnedRows(responseData);
|
|
20848
20878
|
let responseText = JSON.stringify(responseData);
|
|
20879
|
+
const originalLength = responseText.length;
|
|
20849
20880
|
const effectiveMax = Math.min(maxResponseSize, MAX_ALLOWED_RESPONSE_CHARS);
|
|
20850
20881
|
if (responseText.length > effectiveMax) {
|
|
20851
20882
|
responseText = responseText.slice(0, effectiveMax) + `
|
|
20852
20883
|
...[TRUNCATED - response was ${responseText.length} chars, showing first ${effectiveMax}]`;
|
|
20853
20884
|
}
|
|
20854
|
-
return { text: responseText, isError: false };
|
|
20885
|
+
return { text: responseText, isError: false, originalLength, returnedRows };
|
|
20855
20886
|
}
|
|
20856
20887
|
function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "ListWixSites", "ManageWixSite"], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize) {
|
|
20857
20888
|
const effectiveAllowedTools = [
|
|
@@ -20914,9 +20945,14 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
20914
20945
|
data: method === "GET" ? void 0 : body
|
|
20915
20946
|
});
|
|
20916
20947
|
const requestId = response.headers?.["x-wix-request-id"];
|
|
20917
|
-
setToolBiParams({ requestId, siteId });
|
|
20918
20948
|
const responseData = await handleWixAPIResponse(response);
|
|
20919
20949
|
const formatted = formatApiResponse(responseData, maxResponseSize);
|
|
20950
|
+
setToolBiParams({
|
|
20951
|
+
requestId,
|
|
20952
|
+
siteId,
|
|
20953
|
+
returnedRows: formatted.returnedRows,
|
|
20954
|
+
originalLength: formatted.originalLength
|
|
20955
|
+
});
|
|
20920
20956
|
if (formatted.isError) {
|
|
20921
20957
|
return {
|
|
20922
20958
|
content: [
|
|
@@ -21055,7 +21091,7 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21055
21091
|
destructiveHint: true,
|
|
21056
21092
|
openWorldHint: false
|
|
21057
21093
|
}),
|
|
21058
|
-
async ({ url, body, method, reason }, { httpClient }) => {
|
|
21094
|
+
async ({ url, body, method, reason }, { httpClient, setToolBiParams }) => {
|
|
21059
21095
|
logger.log(
|
|
21060
21096
|
`Calling Wix Account level API: ${url}, reason="${reason}", body: ${JSON.stringify(body)}`
|
|
21061
21097
|
);
|
|
@@ -21084,6 +21120,10 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21084
21120
|
});
|
|
21085
21121
|
const responseData = await handleWixAPIResponse(response);
|
|
21086
21122
|
const formatted = formatApiResponse(responseData, maxResponseSize);
|
|
21123
|
+
setToolBiParams({
|
|
21124
|
+
returnedRows: formatted.returnedRows,
|
|
21125
|
+
originalLength: formatted.originalLength
|
|
21126
|
+
});
|
|
21087
21127
|
if (formatted.isError) {
|
|
21088
21128
|
return {
|
|
21089
21129
|
content: [
|
|
@@ -21306,10 +21346,13 @@ function executeTextResult(message) {
|
|
|
21306
21346
|
content: [{ type: "text", text: message }]
|
|
21307
21347
|
};
|
|
21308
21348
|
}
|
|
21309
|
-
function formatExecuteResult(data, maxResponseSize) {
|
|
21349
|
+
function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
21310
21350
|
if (data.error) {
|
|
21311
21351
|
return executeErrorResult(data.error);
|
|
21312
21352
|
}
|
|
21353
|
+
reportBi?.({
|
|
21354
|
+
originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
|
|
21355
|
+
});
|
|
21313
21356
|
return {
|
|
21314
21357
|
content: [
|
|
21315
21358
|
{ type: "text", text: truncateResponse(data.result, maxResponseSize) }
|
|
@@ -21419,7 +21462,11 @@ function addExecuteTool(server, options) {
|
|
|
21419
21462
|
status: data.authErrorStatus
|
|
21420
21463
|
});
|
|
21421
21464
|
}
|
|
21422
|
-
return formatExecuteResult(
|
|
21465
|
+
return formatExecuteResult(
|
|
21466
|
+
data,
|
|
21467
|
+
maxResponseSize,
|
|
21468
|
+
extra?.setToolBiParams
|
|
21469
|
+
);
|
|
21423
21470
|
} catch (err) {
|
|
21424
21471
|
const message = err instanceof Error ? err.message : String(err);
|
|
21425
21472
|
logger.log(
|
|
@@ -21511,7 +21558,7 @@ function extractText(result) {
|
|
|
21511
21558
|
return result.content.filter((content) => content.type === "text" && content.text != null).map((content) => content.text).join("\n");
|
|
21512
21559
|
}
|
|
21513
21560
|
var TOOL_RESULT_PREVIEW_MAX_CHARS = 4e3;
|
|
21514
|
-
function formatToolResultAdditionalFields(result) {
|
|
21561
|
+
function formatToolResultAdditionalFields(result, extras) {
|
|
21515
21562
|
const resultText = extractText(result);
|
|
21516
21563
|
const contentTypes = [
|
|
21517
21564
|
...new Set(
|
|
@@ -21527,7 +21574,8 @@ function formatToolResultAdditionalFields(result) {
|
|
|
21527
21574
|
resultPreview: resultText.slice(0, TOOL_RESULT_PREVIEW_MAX_CHARS),
|
|
21528
21575
|
resultTextLength: resultText.length,
|
|
21529
21576
|
resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
|
|
21530
|
-
resultContentTypes: contentTypes
|
|
21577
|
+
resultContentTypes: contentTypes,
|
|
21578
|
+
...extras
|
|
21531
21579
|
});
|
|
21532
21580
|
}
|
|
21533
21581
|
function createToolBiEvent(params) {
|
|
@@ -21765,7 +21813,10 @@ function createWixToolkit(options) {
|
|
|
21765
21813
|
if (!toolSucceeded) {
|
|
21766
21814
|
errorBody = extractText(cbResult);
|
|
21767
21815
|
} else {
|
|
21768
|
-
additionalFields = formatToolResultAdditionalFields(cbResult
|
|
21816
|
+
additionalFields = formatToolResultAdditionalFields(cbResult, {
|
|
21817
|
+
returnedRows: toolBiParams.returnedRows,
|
|
21818
|
+
originalLength: toolBiParams.originalLength
|
|
21819
|
+
});
|
|
21769
21820
|
}
|
|
21770
21821
|
if (!isDevEnvironment) {
|
|
21771
21822
|
if (toolSucceeded) {
|
|
@@ -10,6 +10,32 @@ import { desc } from '../config/param-descriptions.js';
|
|
|
10
10
|
import { MAX_ALLOWED_RESPONSE_CHARS } from '../infra/response-size-limits.js';
|
|
11
11
|
export { addUploadImageTool } from './upload-image-tool.js';
|
|
12
12
|
const DEFAULT_MAX_RESPONSE_SIZE = 50000; // ~50KB cap
|
|
13
|
+
/**
|
|
14
|
+
* Rows the API actually served, read before truncation. Endpoints often return
|
|
15
|
+
* fewer than asked for, so an offset advance that does not match
|
|
16
|
+
* offset + requested limit can still be correct -- without this, BI cannot tell
|
|
17
|
+
* a paging defect from a short page.
|
|
18
|
+
*/
|
|
19
|
+
function extractReturnedRows(responseData) {
|
|
20
|
+
if (responseData === null || typeof responseData !== 'object') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const data = responseData;
|
|
24
|
+
const block = (data.pagingMetadata ??
|
|
25
|
+
data.cursorPagingMetadata ??
|
|
26
|
+
data.pagingOffsetMetadata ??
|
|
27
|
+
data.metaData ??
|
|
28
|
+
data.metadata);
|
|
29
|
+
const raw = block?.count;
|
|
30
|
+
// A blank string coerces to 0, which would report a page size the endpoint
|
|
31
|
+
// never sent -- worse than reporting nothing.
|
|
32
|
+
if (typeof raw !== 'number' &&
|
|
33
|
+
(typeof raw !== 'string' || raw.trim() === '')) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const count = Number(raw);
|
|
37
|
+
return Number.isFinite(count) ? count : undefined;
|
|
38
|
+
}
|
|
13
39
|
function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_SIZE) {
|
|
14
40
|
// Detect if response is HTML (likely wrong endpoint or auth redirect)
|
|
15
41
|
if (typeof responseData === 'string' &&
|
|
@@ -19,14 +45,16 @@ function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_
|
|
|
19
45
|
isError: true
|
|
20
46
|
};
|
|
21
47
|
}
|
|
48
|
+
const returnedRows = extractReturnedRows(responseData);
|
|
22
49
|
let responseText = JSON.stringify(responseData);
|
|
50
|
+
const originalLength = responseText.length;
|
|
23
51
|
const effectiveMax = Math.min(maxResponseSize, MAX_ALLOWED_RESPONSE_CHARS);
|
|
24
52
|
if (responseText.length > effectiveMax) {
|
|
25
53
|
responseText =
|
|
26
54
|
responseText.slice(0, effectiveMax) +
|
|
27
55
|
`\n...[TRUNCATED - response was ${responseText.length} chars, showing first ${effectiveMax}]`;
|
|
28
56
|
}
|
|
29
|
-
return { text: responseText, isError: false };
|
|
57
|
+
return { text: responseText, isError: false, originalLength, returnedRows };
|
|
30
58
|
}
|
|
31
59
|
export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI', 'ListWixSites', 'ManageWixSite'], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize) {
|
|
32
60
|
// Compute effective allowed tools: (default + enableTools) - disableTools
|
|
@@ -89,9 +117,15 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
89
117
|
data: method === 'GET' ? undefined : body
|
|
90
118
|
});
|
|
91
119
|
const requestId = response.headers?.['x-wix-request-id'];
|
|
92
|
-
setToolBiParams({ requestId, siteId });
|
|
93
120
|
const responseData = await handleWixAPIResponse(response);
|
|
94
121
|
const formatted = formatApiResponse(responseData, maxResponseSize);
|
|
122
|
+
// One call only -- setToolBiParams replaces rather than merges.
|
|
123
|
+
setToolBiParams({
|
|
124
|
+
requestId,
|
|
125
|
+
siteId,
|
|
126
|
+
returnedRows: formatted.returnedRows,
|
|
127
|
+
originalLength: formatted.originalLength
|
|
128
|
+
});
|
|
95
129
|
if (formatted.isError) {
|
|
96
130
|
return {
|
|
97
131
|
content: [
|
|
@@ -222,7 +256,7 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
222
256
|
readOnlyHint: false,
|
|
223
257
|
destructiveHint: true,
|
|
224
258
|
openWorldHint: false
|
|
225
|
-
}), async ({ url, body, method, reason }, { httpClient }) => {
|
|
259
|
+
}), async ({ url, body, method, reason }, { httpClient, setToolBiParams }) => {
|
|
226
260
|
logger.log(`Calling Wix Account level API: ${url}, reason="${reason}", body: ${JSON.stringify(body)}`);
|
|
227
261
|
const { isAllowed, error: urlError } = isApiUrlAllowed(url);
|
|
228
262
|
if (!isAllowed) {
|
|
@@ -251,6 +285,10 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
251
285
|
});
|
|
252
286
|
const responseData = await handleWixAPIResponse(response);
|
|
253
287
|
const formatted = formatApiResponse(responseData, maxResponseSize);
|
|
288
|
+
setToolBiParams({
|
|
289
|
+
returnedRows: formatted.returnedRows,
|
|
290
|
+
originalLength: formatted.originalLength
|
|
291
|
+
});
|
|
254
292
|
if (formatted.isError) {
|
|
255
293
|
return {
|
|
256
294
|
content: [
|
|
@@ -162,10 +162,16 @@ function executeTextResult(message) {
|
|
|
162
162
|
content: [{ type: 'text', text: message }]
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
|
-
function formatExecuteResult(data, maxResponseSize) {
|
|
165
|
+
function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
166
166
|
if (data.error) {
|
|
167
167
|
return executeErrorResult(data.error);
|
|
168
168
|
}
|
|
169
|
+
// Before the slice -- resultTextLength is measured after it and saturates.
|
|
170
|
+
reportBi?.({
|
|
171
|
+
originalLength: typeof data.result === 'string'
|
|
172
|
+
? data.result.length
|
|
173
|
+
: JSON.stringify(data.result)?.length
|
|
174
|
+
});
|
|
169
175
|
return {
|
|
170
176
|
content: [
|
|
171
177
|
{ type: 'text', text: truncateResponse(data.result, maxResponseSize) }
|
|
@@ -293,7 +299,7 @@ function addExecuteTool(server, options) {
|
|
|
293
299
|
status: data.authErrorStatus
|
|
294
300
|
});
|
|
295
301
|
}
|
|
296
|
-
return formatExecuteResult(data, maxResponseSize);
|
|
302
|
+
return formatExecuteResult(data, maxResponseSize, extra?.setToolBiParams);
|
|
297
303
|
}
|
|
298
304
|
catch (err) {
|
|
299
305
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-shape rules shared by every tool that lets an agent write a Wix REST
|
|
3
|
+
* URL by hand (ExecuteWixAPI's `wix.request` and CallWixSiteAPI's `url`).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The Wix API gateway deserializes enum-typed array query params only when the
|
|
7
|
+
* parameter is repeated once per value. Comma-joined values are read as a
|
|
8
|
+
* single unknown enum member and rejected with HTTP 400.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
|
|
11
|
+
//# sourceMappingURL=api-request-rules.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-shape rules shared by every tool that lets an agent write a Wix REST
|
|
3
|
+
* URL by hand (ExecuteWixAPI's `wix.request` and CallWixSiteAPI's `url`).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The Wix API gateway deserializes enum-typed array query params only when the
|
|
7
|
+
* parameter is repeated once per value. Comma-joined values are read as a
|
|
8
|
+
* single unknown enum member and rejected with HTTP 400.
|
|
9
|
+
*/
|
|
10
|
+
export const ARRAY_QUERY_PARAMS_RULE = 'Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).';
|
|
11
|
+
//# sourceMappingURL=api-request-rules.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
|
+
import { ARRAY_QUERY_PARAMS_RULE } from './api-request-rules.js';
|
|
2
3
|
/**
|
|
3
4
|
* Common agent goal - used in both SYSTEM_REMINDER and defaultReadmeDocs
|
|
4
5
|
*/
|
|
@@ -104,11 +105,13 @@ const EXECUTE_WIX_API_INTRO = dedent `
|
|
|
104
105
|
CODE CONTRACT:
|
|
105
106
|
- Pass an async function expression: \`async function() { ... }\` or \`async () => { ... }\`. Put every declaration, \`await\`, and \`return\` inside it, and do not invoke it yourself.
|
|
106
107
|
- Use only \`wix.request({ method, url, body })\` and in-memory JavaScript. Node.js modules, filesystem access, and downloadable-file creation are unavailable.
|
|
108
|
+
- Do not minify the code. Put each object property on its own line, with spaces after colons and commas, and declare deep literals (request bodies, filters, patch lists) as a named \`const\` before use so each \`wix.request({ ... })\` argument stays short.
|
|
107
109
|
|
|
108
110
|
CONFIRM API CONTRACTS:
|
|
109
111
|
- Do not rely on memory for endpoints, methods, bodies, response paths, filters, enum values, or auth scope. Confirm every method from conversation-provided details, a complete matching docs example, or the schema/docs tools before executing.
|
|
110
112
|
- Prefer \`method.publicUrl\` from SearchWixAPISpec; never use \`method.servers[0]\`. Pass all supporting documentation URLs through \`sourceDocUrls\`.
|
|
111
113
|
- A response field is not automatically filterable or sortable. Use only capabilities declared for that exact query/search method. If an unsupported filter is unavoidable, fetch a bounded result set and filter it in code.
|
|
114
|
+
- ${ARRAY_QUERY_PARAMS_RULE}
|
|
112
115
|
- Read-only probes may inspect state or resolve IDs. Never use speculative create/update/delete calls to discover request or response shapes, and never repeat a successful mutation merely to obtain more response data.
|
|
113
116
|
`;
|
|
114
117
|
const EXECUTE_WIX_API_OUTPUT_AND_ERRORS = dedent `
|
|
@@ -124,6 +127,7 @@ const ACCOUNT_SITE_EXECUTE_SCOPE = dedent `
|
|
|
124
127
|
SCOPE AND SITE TARGETING:
|
|
125
128
|
- Set \`scope: "site"\` for site-level APIs and \`scope: "account"\` for account-level APIs. Account-level acts on the account itself, or on sites as objects within it; anything that reads or writes inside a site is site-level.
|
|
126
129
|
- If \`scope\` is omitted it defaults to site when either the ExecuteWixAPI call or \`wix.request\` provides a \`siteId\`; otherwise it defaults to account.
|
|
130
|
+
- Site-level calls need a site ID; there is no implicit current site. Before any \`scope: "site"\` call, pass the site's ID as the ExecuteWixAPI \`siteId\` parameter or per request. \`scope: "site"\` without a \`siteId\` fails.
|
|
127
131
|
- \`scope: "account"\` ignores any \`siteId\` you pass, so a site-level endpoint called that way fails.
|
|
128
132
|
- Authentication is injected automatically; never set Authorization, wix-site-id, or wix-account-id headers.
|
|
129
133
|
- For one named site, prefer GetSiteContext. If it is unavailable, resolve the exact site and perform the action in the same invocation.
|
|
@@ -441,8 +445,10 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
441
445
|
SearchBuildAppsDocumentation: dedent `
|
|
442
446
|
Searches the Wix Build Apps documentation.
|
|
443
447
|
`,
|
|
444
|
-
|
|
445
|
-
|
|
448
|
+
SearchDevelopmentDocumentation: dedent `
|
|
449
|
+
Broad Wix development documentation (platform concepts, developer guides, getting started). **Last resort — development tasks only.**
|
|
450
|
+
Skip it entirely for general, business, support, or Wix-editor questions.
|
|
451
|
+
Call it only after the specific search for your task returned nothing useful: \`SearchWixRESTDocumentation\` (REST), \`SearchWixSDKDocumentation\` (JS SDK), \`SearchWixCLIDocumentation\` (CLI), \`SearchBuildAppsDocumentation\` (apps), \`SearchWixHeadlessDocumentation\` (Headless), \`SearchWixWDSDocumentation\` (WDS). Never use it as a first search.
|
|
446
452
|
`,
|
|
447
453
|
SearchWixHeadlessDocumentation: dedent `
|
|
448
454
|
Searches the Wix Headless documentation.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ALLOWED_HOSTNAMES } from '../api-call/validation.js';
|
|
2
|
+
import { ARRAY_QUERY_PARAMS_RULE } from './api-request-rules.js';
|
|
2
3
|
/**
|
|
3
4
|
* Centralized parameter descriptions organized by tool.
|
|
4
5
|
* Each tool has all its parameter descriptions in one place.
|
|
@@ -10,7 +11,8 @@ export const paramDescriptions = {
|
|
|
10
11
|
'The url of the api to call - ALWAYS get the API url from the Wix REST docs or from the conversation context, the URL MUST BE ABSOLUTE URL',
|
|
11
12
|
'NEVER guess the API url, ALWAYS get it from the conversation context, i.e from the user prompt or from the "WixREADME" tool or from the "SearchWixRESTDocumentation" tool or from the "BrowseWixRESTDocsMenu" tool or from the "ReadFullDocsArticle" tool',
|
|
12
13
|
`Allowed API urls are: ${ALLOWED_HOSTNAMES.join(', ')}`,
|
|
13
|
-
`Docs urls like https://dev.wix.com/docs/... are not API urls, if you want to read the docs, use the "ReadFullDocsArticle" tool
|
|
14
|
+
`Docs urls like https://dev.wix.com/docs/... are not API urls, if you want to read the docs, use the "ReadFullDocsArticle" tool`,
|
|
15
|
+
ARRAY_QUERY_PARAMS_RULE
|
|
14
16
|
].join('\n'),
|
|
15
17
|
method: 'The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)',
|
|
16
18
|
body: 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
|
|
@@ -83,8 +85,8 @@ export const paramDescriptions = {
|
|
|
83
85
|
searchTerm: 'The search term to search for in the Build Apps Documentation',
|
|
84
86
|
maxResults: 'The maximum number of results to return, default is 10, max is 15'
|
|
85
87
|
},
|
|
86
|
-
|
|
87
|
-
searchTerm: 'The search term
|
|
88
|
+
SearchDevelopmentDocumentation: {
|
|
89
|
+
searchTerm: 'The development-related search term. Use only after a more specific docs search (REST/SDK/CLI/Build Apps/Headless/WDS) came back empty.',
|
|
88
90
|
maxResults: 'The maximum number of results to return, default is 10, max is 15'
|
|
89
91
|
},
|
|
90
92
|
SearchWixHeadlessDocumentation: {
|
package/build/dts/docs/docs.js
CHANGED
|
@@ -399,11 +399,11 @@ export const addDocsTools = (server, allowedTools = [
|
|
|
399
399
|
}
|
|
400
400
|
});
|
|
401
401
|
}
|
|
402
|
-
//
|
|
402
|
+
// Development Documentation
|
|
403
403
|
if (allowedTools.includes('OVERVIEW') &&
|
|
404
|
-
!disableTools?.includes('
|
|
405
|
-
server.tool('
|
|
406
|
-
const d = desc('
|
|
404
|
+
!disableTools?.includes('SearchDevelopmentDocumentation')) {
|
|
405
|
+
server.tool('SearchDevelopmentDocumentation', getDescription('SearchDevelopmentDocumentation', defaultToolDescriptions.SearchDevelopmentDocumentation ?? ''), (() => {
|
|
406
|
+
const d = desc('SearchDevelopmentDocumentation');
|
|
407
407
|
return {
|
|
408
408
|
searchTerm: z.string().describe(d.searchTerm),
|
|
409
409
|
maxResults: z
|
|
@@ -438,8 +438,8 @@ export const addDocsTools = (server, allowedTools = [
|
|
|
438
438
|
panorama.errorMonitor().reportError(error);
|
|
439
439
|
captureException(error, {
|
|
440
440
|
tags: {
|
|
441
|
-
componentId: '
|
|
442
|
-
toolName: '
|
|
441
|
+
componentId: 'SearchDevelopmentDocumentation',
|
|
442
|
+
toolName: 'SearchDevelopmentDocumentation'
|
|
443
443
|
}
|
|
444
444
|
});
|
|
445
445
|
logger.error(`Error searching for ${searchTerm} in Wix Overview: ${error}`);
|
|
@@ -6,7 +6,7 @@ import { wixMcpRequestSrc39Evid1607, executeWixApiToolRequestSrc39Evid1612 } fro
|
|
|
6
6
|
* and concatenates text blocks with newlines.
|
|
7
7
|
*/
|
|
8
8
|
export declare function extractText(result: WixToolCallResult): string;
|
|
9
|
-
export declare function formatToolResultAdditionalFields(result: WixToolCallResult): string | undefined;
|
|
9
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>): string | undefined;
|
|
10
10
|
export declare function createToolBiEvent(params: Parameters<typeof wixMcpRequestSrc39Evid1607>[0]): import("@wix/bi-logger-api-infra-data/v2/types").LogObject<import("@wix/bi-logger-api-infra-data/v2/types").wixMcpRequestSrc39Evid1607Params>;
|
|
11
11
|
export declare function createExecuteWixAPICallToolBiEvent(params: Parameters<typeof executeWixApiToolRequestSrc39Evid1612>[0]): import("@wix/bi-logger-api-infra-data/v2/types").LogObject<import("@wix/bi-logger-api-infra-data/v2/types").executeWixApiToolRequestSrc39Evid1612Params>;
|
|
12
12
|
//# sourceMappingURL=tool-bi.d.ts.map
|
|
@@ -11,7 +11,7 @@ export function extractText(result) {
|
|
|
11
11
|
.join('\n');
|
|
12
12
|
}
|
|
13
13
|
const TOOL_RESULT_PREVIEW_MAX_CHARS = 4000;
|
|
14
|
-
export function formatToolResultAdditionalFields(result) {
|
|
14
|
+
export function formatToolResultAdditionalFields(result, extras) {
|
|
15
15
|
const resultText = extractText(result);
|
|
16
16
|
const contentTypes = [
|
|
17
17
|
...new Set(result.content?.map((content) => content.type) ?? [])
|
|
@@ -23,7 +23,8 @@ export function formatToolResultAdditionalFields(result) {
|
|
|
23
23
|
resultPreview: resultText.slice(0, TOOL_RESULT_PREVIEW_MAX_CHARS),
|
|
24
24
|
resultTextLength: resultText.length,
|
|
25
25
|
resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
|
|
26
|
-
resultContentTypes: contentTypes
|
|
26
|
+
resultContentTypes: contentTypes,
|
|
27
|
+
...extras
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
export function createToolBiEvent(params) {
|
|
@@ -169,7 +169,7 @@ export interface ToolHintsConfig {
|
|
|
169
169
|
* and concatenates text blocks with newlines.
|
|
170
170
|
*/
|
|
171
171
|
export declare function extractText(result: WixToolCallResult): string;
|
|
172
|
-
export declare function formatToolResultAdditionalFields(result: WixToolCallResult): string | undefined;
|
|
172
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>): string | undefined;
|
|
173
173
|
/** A single tool extracted from @wix/mcp, ready to execute. */
|
|
174
174
|
export interface WixTool {
|
|
175
175
|
/** Tool name (e.g. 'CallWixSiteAPI', 'SearchWixRESTDocumentation') */
|
|
@@ -169,7 +169,7 @@ export interface ToolHintsConfig {
|
|
|
169
169
|
* and concatenates text blocks with newlines.
|
|
170
170
|
*/
|
|
171
171
|
export declare function extractText(result: WixToolCallResult): string;
|
|
172
|
-
export declare function formatToolResultAdditionalFields(result: WixToolCallResult): string | undefined;
|
|
172
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>): string | undefined;
|
|
173
173
|
/** A single tool extracted from @wix/mcp, ready to execute. */
|
|
174
174
|
export interface WixTool {
|
|
175
175
|
/** Tool name (e.g. 'CallWixSiteAPI', 'SearchWixRESTDocumentation') */
|
package/build/dts/toolkit.js
CHANGED
|
@@ -300,7 +300,10 @@ export function createWixToolkit(options) {
|
|
|
300
300
|
errorBody = extractText(cbResult);
|
|
301
301
|
}
|
|
302
302
|
else {
|
|
303
|
-
additionalFields = formatToolResultAdditionalFields(cbResult
|
|
303
|
+
additionalFields = formatToolResultAdditionalFields(cbResult, {
|
|
304
|
+
returnedRows: toolBiParams.returnedRows,
|
|
305
|
+
originalLength: toolBiParams.originalLength
|
|
306
|
+
});
|
|
304
307
|
}
|
|
305
308
|
// Panorama: finish or report error
|
|
306
309
|
if (!isDevEnvironment) {
|
|
@@ -175,7 +175,10 @@ export class WixMcpServer extends McpServer {
|
|
|
175
175
|
errorBody = extractText(cbResult);
|
|
176
176
|
}
|
|
177
177
|
else {
|
|
178
|
-
additionalFields = formatToolResultAdditionalFields(cbResult
|
|
178
|
+
additionalFields = formatToolResultAdditionalFields(cbResult, {
|
|
179
|
+
returnedRows: toolBiParams.returnedRows,
|
|
180
|
+
originalLength: toolBiParams.originalLength
|
|
181
|
+
});
|
|
179
182
|
}
|
|
180
183
|
if (toolSucceeded && !isDevEnvironment) {
|
|
181
184
|
panorama.transaction(toolName).finish({ toolInvocationId });
|