@wix/mcp 1.0.94 → 1.0.96
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 +2 -2
- package/build/bin-standalone.js +1457 -332
- package/build/cjs/index.cjs +3379 -1333
- package/build/cjs/toolkit.cjs +2158 -373
- package/build/dts/api-call/headless-release.d.ts +56 -0
- package/build/dts/api-call/headless-release.js +135 -0
- package/build/dts/api-call/index.d.ts +9 -1
- package/build/dts/api-call/index.js +36 -8
- package/build/dts/api-call/site-files.d.ts +14 -0
- package/build/dts/api-call/site-files.js +106 -10
- package/build/dts/api-call/upload-headless-website-files.d.ts +117 -5
- package/build/dts/api-call/upload-headless-website-files.js +565 -20
- package/build/dts/api-call/upload-image-tool.d.ts +2 -0
- package/build/dts/api-call/upload-image-tool.js +8 -4
- package/build/dts/bin.d.ts +1 -1
- package/build/dts/bin.js +3 -1
- package/build/dts/code-mode/attachments.d.ts +9 -6
- package/build/dts/code-mode/attachments.js +23 -16
- package/build/dts/code-mode/index.d.ts +6 -1
- package/build/dts/code-mode/index.js +32 -12
- package/build/dts/code-mode/truncate-response.js +5 -1
- package/build/dts/config/default-config.d.ts +35 -5
- package/build/dts/config/default-config.js +339 -182
- package/build/dts/config/file-params.d.ts +22 -0
- package/build/dts/config/file-params.js +24 -0
- package/build/dts/config/param-descriptions.d.ts +14 -2
- package/build/dts/config/param-descriptions.js +33 -14
- package/build/dts/constants.d.ts +1 -0
- package/build/dts/constants.js +1 -0
- package/build/dts/docs/docs.d.ts +12 -1
- package/build/dts/docs/docs.js +274 -43
- package/build/dts/docs/fetch-article.d.ts +21 -0
- package/build/dts/docs/fetch-article.js +66 -4
- package/build/dts/docs/get-to-know.d.ts +7 -2
- package/build/dts/docs/get-to-know.js +36 -14
- package/build/dts/docs/guessed-url.d.ts +41 -0
- package/build/dts/docs/guessed-url.js +185 -0
- package/build/dts/docs/mintlify-docs.d.ts +121 -0
- package/build/dts/docs/mintlify-docs.js +391 -0
- package/build/dts/docs/semanticSearch.d.ts +2 -1
- package/build/dts/docs/semanticSearch.js +14 -7
- package/build/dts/docs/start-deploy-website.d.ts +7 -6
- package/build/dts/docs/start-deploy-website.js +13 -8
- package/build/dts/docs/start-headless-build.d.ts +8 -10
- package/build/dts/docs/start-headless-build.js +14 -28
- package/build/dts/index.d.ts +2 -2
- package/build/dts/index.js +2 -2
- package/build/dts/infra/redact-personal-data.d.ts +4 -0
- package/build/dts/infra/redact-personal-data.js +148 -0
- package/build/dts/site-widget-tools/constants.d.ts +50 -2
- package/build/dts/site-widget-tools/constants.js +65 -8
- package/build/dts/site-widget-tools/index.d.ts +1 -0
- package/build/dts/site-widget-tools/index.js +1 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.d.ts +14 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.js +138 -0
- package/build/dts/site-widget-tools/pull-site-creation-job-tool/index.js +1 -1
- package/build/dts/site-widget-tools/search-site-templates-tool/index.js +25 -2
- package/build/dts/site-widget-tools/site-builder-tool/index.js +35 -3
- package/build/dts/site-widget-tools/site-creation-tool/index.d.ts +7 -1
- package/build/dts/site-widget-tools/site-creation-tool/index.js +22 -51
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +26 -12
- package/build/dts/support/index.d.ts +4 -0
- package/build/dts/support/index.js +13 -7
- package/build/dts/tool-utils.d.ts +8 -1
- package/build/dts/tool-utils.js +26 -2
- package/build/dts/toolkit-entry.d.cts +15 -10
- package/build/dts/toolkit-entry.d.ts +15 -10
- package/build/dts/toolkit.d.ts +2 -2
- package/build/dts/toolkit.js +21 -8
- package/build/dts/wix-mcp-server.d.ts +10 -1
- package/build/dts/wix-mcp-server.js +25 -7
- package/build/dts/wixel/constants.d.ts +8 -1
- package/build/dts/wixel/constants.js +8 -1
- package/build/dts/wixel/index.d.ts +1 -1
- package/build/dts/wixel/index.js +2 -2
- package/build/dts/wixel/wixel-a2a-client.d.ts +83 -0
- package/build/dts/wixel/wixel-a2a-client.js +166 -0
- package/build/dts/wixel/wixel-bi.d.ts +2 -1
- package/build/dts/wixel/wixel-bi.js +2 -1
- package/build/dts/wixel/{external-agent-api.d.ts → wixel-session-entries.d.ts} +11 -33
- package/build/dts/wixel/{external-agent-api.js → wixel-session-entries.js} +33 -54
- package/build/dts/wixel/wixel-session-tools.d.ts +21 -26
- package/build/dts/wixel/wixel-session-tools.js +494 -599
- package/build/esm/index.js +3422 -1388
- package/build/esm/toolkit.js +2158 -373
- package/package.json +4 -4
package/build/cjs/toolkit.cjs
CHANGED
|
@@ -16257,6 +16257,8 @@ var runSemanticSearch = async (toolName, toolParams, maxResults = 20, rerank = f
|
|
|
16257
16257
|
kbNames.push("VELO_DOCS_KB_ID", "VELO_METHODS_KB_ID");
|
|
16258
16258
|
} else if (toolName === "CLI") {
|
|
16259
16259
|
kbNames.push("CLI_KB_ID");
|
|
16260
|
+
} else if (toolName === "HELP_CENTER") {
|
|
16261
|
+
kbNames.push("HELP_CENTER_KB_ID");
|
|
16260
16262
|
}
|
|
16261
16263
|
logger.log(
|
|
16262
16264
|
`[SemanticSearch] Tool: ${toolName}, KBs: [${kbNames.join(", ")}], Query: "${toolParams.searchTerm}"`
|
|
@@ -16289,9 +16291,9 @@ var runSemanticSearch = async (toolName, toolParams, maxResults = 20, rerank = f
|
|
|
16289
16291
|
);
|
|
16290
16292
|
return data?.results;
|
|
16291
16293
|
};
|
|
16292
|
-
var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult, extraDocumentTypes) => {
|
|
16294
|
+
var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult, extraDocumentTypes, documentTypesOverride) => {
|
|
16293
16295
|
const startTime = Date.now();
|
|
16294
|
-
const documentTypes = extraDocumentTypes?.length ? [toolName, ...extraDocumentTypes] : void 0;
|
|
16296
|
+
const documentTypes = documentTypesOverride?.length ? documentTypesOverride : extraDocumentTypes?.length ? [toolName, ...extraDocumentTypes] : void 0;
|
|
16295
16297
|
logger.log(
|
|
16296
16298
|
`[SemanticSearch/v2] Tool: ${documentTypes ? documentTypes.join("+") : toolName}, Query: "${searchTerm}"`
|
|
16297
16299
|
);
|
|
@@ -16335,15 +16337,17 @@ var runSemanticSearchAndFormat = async ({
|
|
|
16335
16337
|
rerank,
|
|
16336
16338
|
linesInEachResult,
|
|
16337
16339
|
usePlatformizedSearch = true,
|
|
16338
|
-
extraDocumentTypes
|
|
16340
|
+
extraDocumentTypes,
|
|
16341
|
+
documentTypes
|
|
16339
16342
|
}) => {
|
|
16340
|
-
if (usePlatformizedSearch) {
|
|
16343
|
+
if (usePlatformizedSearch || documentTypes?.length) {
|
|
16341
16344
|
return runPlatformizedSemanticSearch(
|
|
16342
16345
|
toolName,
|
|
16343
16346
|
toolParams.searchTerm,
|
|
16344
16347
|
maxResults,
|
|
16345
16348
|
linesInEachResult,
|
|
16346
|
-
extraDocumentTypes
|
|
16349
|
+
extraDocumentTypes,
|
|
16350
|
+
documentTypes
|
|
16347
16351
|
);
|
|
16348
16352
|
} else {
|
|
16349
16353
|
const results = await runSemanticSearch(
|
|
@@ -17263,6 +17267,7 @@ var import_http_client = __toESM(require_index_node(), 1);
|
|
|
17263
17267
|
// src/constants.ts
|
|
17264
17268
|
var RAW_DOCS_API_URL = "https://dev.wix.com/rawdocs/api";
|
|
17265
17269
|
var GET_ARTICLE_CONTENT_URL = `${RAW_DOCS_API_URL}/get-article-content`;
|
|
17270
|
+
var GET_SOURCE_CONTENT_URL = `${RAW_DOCS_API_URL}/get-source-content`;
|
|
17266
17271
|
var PORTAL_INDEX_URL = `${RAW_DOCS_API_URL}/portal-index`;
|
|
17267
17272
|
var DOCS_API_BASE_URL = "https://dev.wix.com/docs/api/v1";
|
|
17268
17273
|
var GET_SKILLS_BY_PORTAL_URL = `${DOCS_API_BASE_URL}/docs-web-app/menu-node/get-skills-by-portal`;
|
|
@@ -17283,7 +17288,13 @@ var SKILLS_REGISTRY_URL = new RegExp(
|
|
|
17283
17288
|
"^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)|^https://www\\.wix\\.com/headless/"
|
|
17284
17289
|
);
|
|
17285
17290
|
function isSkillsRegistryUrl(url) {
|
|
17286
|
-
return SKILLS_REGISTRY_URL.test(url
|
|
17291
|
+
return SKILLS_REGISTRY_URL.test(normalizeSkillsUrl(url));
|
|
17292
|
+
}
|
|
17293
|
+
var GITHUB_SKILLS_URL = /^https:\/\/(?:www\.)?github\.com\/wix\/skills\/(?:blob|raw|tree)\/[^/]+\/skills\/([^#?]+)/;
|
|
17294
|
+
function normalizeSkillsUrl(url) {
|
|
17295
|
+
const apex = url.trim().replace(/^https:\/\/wix\.com\//, "https://www.wix.com/");
|
|
17296
|
+
const gh = apex.match(GITHUB_SKILLS_URL);
|
|
17297
|
+
return gh ? `https://www.wix.com/skills/${gh[1]}` : apex;
|
|
17287
17298
|
}
|
|
17288
17299
|
async function fetchSkillsRegistryPage(httpClient, url) {
|
|
17289
17300
|
let data;
|
|
@@ -17381,6 +17392,40 @@ async function fetchArticleContentFromDigor(httpClient, articleUrl, mode, stripH
|
|
|
17381
17392
|
}
|
|
17382
17393
|
return articleContent;
|
|
17383
17394
|
}
|
|
17395
|
+
function parseSourceContentUrl(url) {
|
|
17396
|
+
let parsed;
|
|
17397
|
+
try {
|
|
17398
|
+
parsed = new URL(url);
|
|
17399
|
+
} catch {
|
|
17400
|
+
return void 0;
|
|
17401
|
+
}
|
|
17402
|
+
if (`${parsed.origin}${parsed.pathname}` !== GET_SOURCE_CONTENT_URL) {
|
|
17403
|
+
return void 0;
|
|
17404
|
+
}
|
|
17405
|
+
const repo = parsed.searchParams.get("repo");
|
|
17406
|
+
const path = parsed.searchParams.get("path");
|
|
17407
|
+
const commitHash = parsed.searchParams.get("commitHash");
|
|
17408
|
+
if (!repo || !path) return void 0;
|
|
17409
|
+
return commitHash ? { repo, path, commitHash } : { repo, path };
|
|
17410
|
+
}
|
|
17411
|
+
async function fetchSourceContent(httpClient, repo, path, commitHash) {
|
|
17412
|
+
const url = new URL(GET_SOURCE_CONTENT_URL);
|
|
17413
|
+
url.searchParams.set("repo", repo);
|
|
17414
|
+
url.searchParams.set("path", path);
|
|
17415
|
+
if (commitHash) url.searchParams.set("commitHash", commitHash);
|
|
17416
|
+
let data;
|
|
17417
|
+
try {
|
|
17418
|
+
({ data } = await httpClient.get(url.toString()));
|
|
17419
|
+
} catch (error) {
|
|
17420
|
+
const { message, status } = getHttpErrorDetails(error);
|
|
17421
|
+
const statusSuffix = status ? ` (status ${status})` : "";
|
|
17422
|
+
throw new Error(`${message}${statusSuffix}`);
|
|
17423
|
+
}
|
|
17424
|
+
if (!data?.content) {
|
|
17425
|
+
throw new Error("Source content is empty");
|
|
17426
|
+
}
|
|
17427
|
+
return data.content;
|
|
17428
|
+
}
|
|
17384
17429
|
async function fetchArticleContent(httpClient, articleUrl, mode, stripHeader = false, skillsPortalUrl, repo, commitHash, fetchMcpWhiteList) {
|
|
17385
17430
|
const effectiveUrl = applyPortalUrlOverride(articleUrl, skillsPortalUrl);
|
|
17386
17431
|
const isOverridden = effectiveUrl !== articleUrl;
|
|
@@ -17394,7 +17439,10 @@ async function fetchArticleContent(httpClient, articleUrl, mode, stripHeader = f
|
|
|
17394
17439
|
`[fetch] Override failed for ${effectiveUrl}, falling back to ${articleUrl}:`,
|
|
17395
17440
|
error instanceof Error ? error.message : error
|
|
17396
17441
|
);
|
|
17397
|
-
content = isSkillsRegistryUrl(articleUrl) ? await fetchSkillsRegistryPage(
|
|
17442
|
+
content = isSkillsRegistryUrl(articleUrl) ? await fetchSkillsRegistryPage(
|
|
17443
|
+
httpClient,
|
|
17444
|
+
normalizeSkillsUrl(articleUrl)
|
|
17445
|
+
) : await fetchArticleContentFromDigor(
|
|
17398
17446
|
httpClient,
|
|
17399
17447
|
articleUrl,
|
|
17400
17448
|
mode,
|
|
@@ -17402,7 +17450,10 @@ async function fetchArticleContent(httpClient, articleUrl, mode, stripHeader = f
|
|
|
17402
17450
|
);
|
|
17403
17451
|
}
|
|
17404
17452
|
} else if (isSkillsRegistryUrl(articleUrl)) {
|
|
17405
|
-
content = await fetchSkillsRegistryPage(
|
|
17453
|
+
content = await fetchSkillsRegistryPage(
|
|
17454
|
+
httpClient,
|
|
17455
|
+
normalizeSkillsUrl(articleUrl)
|
|
17456
|
+
);
|
|
17406
17457
|
} else if (mode === "raw") {
|
|
17407
17458
|
let parsedUrl;
|
|
17408
17459
|
try {
|
|
@@ -17545,6 +17596,140 @@ async function fetchPlatformizedMenu(menuUrl) {
|
|
|
17545
17596
|
return data.content;
|
|
17546
17597
|
}
|
|
17547
17598
|
|
|
17599
|
+
// src/docs/guessed-url.ts
|
|
17600
|
+
var SKILLS_BASE = "https://www.wix.com/skills/";
|
|
17601
|
+
var registryUrl = (url) => normalizeSkillsUrl(url).replace(
|
|
17602
|
+
/^https:\/\/(?:dev\.wix\.com(?:\/docs)?|www\.wix\.com\/velo\/reference)\/skills\//,
|
|
17603
|
+
SKILLS_BASE
|
|
17604
|
+
);
|
|
17605
|
+
var DOCS_BASE = "https://dev.wix.com/docs/";
|
|
17606
|
+
var FILLER = /* @__PURE__ */ new Set([
|
|
17607
|
+
"",
|
|
17608
|
+
"md",
|
|
17609
|
+
"a",
|
|
17610
|
+
"an",
|
|
17611
|
+
"the",
|
|
17612
|
+
"and",
|
|
17613
|
+
"or",
|
|
17614
|
+
"of",
|
|
17615
|
+
"to",
|
|
17616
|
+
"wix",
|
|
17617
|
+
"s"
|
|
17618
|
+
]);
|
|
17619
|
+
var slugWords = (slug) => new Set(
|
|
17620
|
+
slug.toLowerCase().replace(/\.md$/, "").split(/[^a-z0-9]+/).filter((w) => !FILLER.has(w)).map((w) => w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w)
|
|
17621
|
+
);
|
|
17622
|
+
function slugSimilarity(a, b) {
|
|
17623
|
+
const x = slugWords(a);
|
|
17624
|
+
const y = slugWords(b);
|
|
17625
|
+
if (x.size === 0 || y.size === 0) return 0;
|
|
17626
|
+
let shared = 0;
|
|
17627
|
+
for (const w of x) if (y.has(w)) shared++;
|
|
17628
|
+
return shared / (x.size + y.size - shared);
|
|
17629
|
+
}
|
|
17630
|
+
var lastSegment = (path) => path.slice(path.lastIndexOf("/") + 1);
|
|
17631
|
+
var folderOf = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
|
|
17632
|
+
var SKILL_FILE_MATCH = 0.5;
|
|
17633
|
+
function closestSkillFile(url, manifest, manifestUrl) {
|
|
17634
|
+
const normalized = registryUrl(url).replace(/[?#].*$/, "");
|
|
17635
|
+
if (!normalized.startsWith(SKILLS_BASE)) return null;
|
|
17636
|
+
const [, ...rest] = normalized.slice(SKILLS_BASE.length).split("/");
|
|
17637
|
+
const asked = rest.join("/");
|
|
17638
|
+
const paths = (manifest.files ?? []).map((f) => f.path);
|
|
17639
|
+
if (!asked || paths.length === 0) return null;
|
|
17640
|
+
const base = manifest.base ?? manifestUrl.replace(/\.manifest\.json$/, "/");
|
|
17641
|
+
const name = lastSegment(asked);
|
|
17642
|
+
const folder = folderOf(asked);
|
|
17643
|
+
const sameName = paths.filter((p) => lastSegment(p) === name);
|
|
17644
|
+
if (sameName.length === 1) {
|
|
17645
|
+
return { kind: "file", path: sameName[0], url: base + sameName[0] };
|
|
17646
|
+
}
|
|
17647
|
+
const ranked = paths.filter((p) => folderOf(p) === folder).map((p) => ({ p, score: slugSimilarity(lastSegment(p), name) })).sort((a, b) => b.score - a.score);
|
|
17648
|
+
const [best, next] = ranked;
|
|
17649
|
+
if (best && best.score >= SKILL_FILE_MATCH && best.score > (next?.score ?? 0)) {
|
|
17650
|
+
return { kind: "file", path: best.p, url: base + best.p };
|
|
17651
|
+
}
|
|
17652
|
+
if (ranked.length > 0) {
|
|
17653
|
+
return {
|
|
17654
|
+
kind: "folder",
|
|
17655
|
+
folder,
|
|
17656
|
+
files: ranked.map((r) => base + r.p),
|
|
17657
|
+
manifestUrl
|
|
17658
|
+
};
|
|
17659
|
+
}
|
|
17660
|
+
const top = [...new Set(paths.map((p) => p.split("/")[0]))].sort();
|
|
17661
|
+
return {
|
|
17662
|
+
kind: "folder",
|
|
17663
|
+
folder: "",
|
|
17664
|
+
files: top.map((t) => base + t),
|
|
17665
|
+
manifestUrl
|
|
17666
|
+
};
|
|
17667
|
+
}
|
|
17668
|
+
function skillManifestUrl(url) {
|
|
17669
|
+
const normalized = registryUrl(url);
|
|
17670
|
+
if (!normalized.startsWith(SKILLS_BASE)) return null;
|
|
17671
|
+
const skill = normalized.slice(SKILLS_BASE.length).split(/[/?#]/)[0];
|
|
17672
|
+
return skill ? `${SKILLS_BASE}${skill}.manifest.json` : null;
|
|
17673
|
+
}
|
|
17674
|
+
var MENU_WALK_UP_LEVELS = 3;
|
|
17675
|
+
var MIN_MENU_SEGMENTS = 3;
|
|
17676
|
+
function parentMenuUrls(url) {
|
|
17677
|
+
const clean = url.replace(/[?#].*$/, "").replace(/\/+$/, "");
|
|
17678
|
+
if (!clean.startsWith(DOCS_BASE)) return [];
|
|
17679
|
+
const segments = clean.slice(DOCS_BASE.length).split("/");
|
|
17680
|
+
const out = [];
|
|
17681
|
+
for (let up = 1; up <= MENU_WALK_UP_LEVELS; up++) {
|
|
17682
|
+
const kept = segments.length - up;
|
|
17683
|
+
if (kept < MIN_MENU_SEGMENTS) break;
|
|
17684
|
+
out.push(DOCS_BASE + segments.slice(0, kept).join("/"));
|
|
17685
|
+
}
|
|
17686
|
+
return out;
|
|
17687
|
+
}
|
|
17688
|
+
function closestMenuLinks(menuText, menuUrl, askedUrl, limit = 3) {
|
|
17689
|
+
const asked = lastSegment(
|
|
17690
|
+
askedUrl.replace(/[?#].*$/, "").replace(/\/+$/, "")
|
|
17691
|
+
);
|
|
17692
|
+
const under = `${menuUrl.replace(/\/+$/, "")}/`;
|
|
17693
|
+
const links = [
|
|
17694
|
+
...new Set(
|
|
17695
|
+
menuText.match(/https:\/\/dev\.wix\.com\/docs\/[^\s)\]>"'`]+/g) ?? []
|
|
17696
|
+
)
|
|
17697
|
+
].filter((l) => l.startsWith(under));
|
|
17698
|
+
return links.map((l) => ({
|
|
17699
|
+
l,
|
|
17700
|
+
score: slugSimilarity(lastSegment(l.replace(/\.md$/, "")), asked)
|
|
17701
|
+
})).filter((r) => r.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((r) => r.l);
|
|
17702
|
+
}
|
|
17703
|
+
var SMALL_MENU = 3;
|
|
17704
|
+
var MENU_ENTRY_MATCH = 0.75;
|
|
17705
|
+
function subMenusToRead(menuText, menuUrl, askedUrl, limit = 2) {
|
|
17706
|
+
const asked = lastSegment(
|
|
17707
|
+
askedUrl.replace(/[?#].*$/, "").replace(/\/+$/, "")
|
|
17708
|
+
);
|
|
17709
|
+
const under = `${menuUrl.replace(/\/+$/, "")}/`;
|
|
17710
|
+
const methods = new Set(
|
|
17711
|
+
[
|
|
17712
|
+
...menuText.matchAll(
|
|
17713
|
+
/\]\((https:\/\/dev\.wix\.com\/docs\/[^)\s]+)\)\s*_\((?:GET|POST|PUT|PATCH|DELETE)\)_/g
|
|
17714
|
+
)
|
|
17715
|
+
].map((m) => m[1])
|
|
17716
|
+
);
|
|
17717
|
+
const entries = [
|
|
17718
|
+
...new Set(
|
|
17719
|
+
menuText.match(/https:\/\/dev\.wix\.com\/docs\/[^\s)\]>"'`]+/g) ?? []
|
|
17720
|
+
)
|
|
17721
|
+
].filter(
|
|
17722
|
+
(l) => l.startsWith(under) && !l.slice(under.length).replace(/\.md$/, "").includes("/") && !methods.has(l)
|
|
17723
|
+
);
|
|
17724
|
+
const scored = entries.map((l) => ({
|
|
17725
|
+
l,
|
|
17726
|
+
score: slugSimilarity(lastSegment(l.replace(/\.md$/, "")), asked)
|
|
17727
|
+
})).sort((a, b) => b.score - a.score);
|
|
17728
|
+
if (scored.some((s) => s.score >= MENU_ENTRY_MATCH)) return [];
|
|
17729
|
+
if (entries.length <= SMALL_MENU) return entries;
|
|
17730
|
+
return scored.filter((s) => s.score > 0).slice(0, limit).map((s) => s.l);
|
|
17731
|
+
}
|
|
17732
|
+
|
|
17548
17733
|
// ../../node_modules/dedent/dist/dedent.mjs
|
|
17549
17734
|
function ownKeys2(object, enumerableOnly) {
|
|
17550
17735
|
var keys = Object.keys(object);
|
|
@@ -17689,54 +17874,97 @@ function rethrowIfUnauthorized(error) {
|
|
|
17689
17874
|
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).";
|
|
17690
17875
|
var WQL_FILTER_RULE = "Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators \u2014 `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.";
|
|
17691
17876
|
|
|
17877
|
+
// src/config/file-params.ts
|
|
17878
|
+
var FILE_PARAM_CLIENTS = "OpenAI clients (ChatGPT, Codex)";
|
|
17879
|
+
function fileParamNote(otherwise) {
|
|
17880
|
+
return `${FILE_PARAM_CLIENTS} only: pass each file as you have it (attached in the chat or created in your sandbox, by its file id or path) and the client swaps it for { download_url, file_id, file_name?, mime_type? } before the call reaches Wix. No other client does this swap, so elsewhere a path or file:// URL here fails at once; leave this out and ${otherwise}.`;
|
|
17881
|
+
}
|
|
17882
|
+
|
|
17883
|
+
// src/site-widget-tools/constants.ts
|
|
17884
|
+
var WIX_BUSINESS_FEATURES = "a store, bookings or appointments, rentals, events and tickets, a restaurant menu with online orders or reservations, a blog, a portfolio, pricing plans and memberships, donations, Wix Forms, members and login, a CMS, payments";
|
|
17885
|
+
var SITE_CREATION_DEFAULT_ACTIONS = "add products, set up bookings, and adjust your content";
|
|
17886
|
+
var SITE_CREATION_CUSTOM_ACTIONS_GUIDANCE = `2\u20133 specific, high-value setup actions for the site's industry, written as a comma-separated phrase (it slots into "\u2026ask me to ___ right here in the chat"). Use the following domain patterns as guidance:
|
|
17887
|
+
* E-commerce / Retail: "add products, set up payment methods, and organize collections"
|
|
17888
|
+
* Restaurants / Food / Bakeries: "add menu items, set up online ordering, and collect table reservations"
|
|
17889
|
+
* Services / Fitness / Salons / Consultants: "set up booking slots, add service pricing, and manage appointments"
|
|
17890
|
+
* Creative / Portfolios / Photographers: "upload project galleries, feature client reviews, and customize contact forms"
|
|
17891
|
+
* Events / Weddings / Real Estate: "add event schedules, display listings, and manage RSVP forms"
|
|
17892
|
+
* General / Unspecified: "${SITE_CREATION_DEFAULT_ACTIONS}"
|
|
17893
|
+
If none of these patterns fit the site, infer 2\u20133 actions that suit what that kind of business would manage on its site.`;
|
|
17894
|
+
var SITE_BUILDER_CHOICE = {
|
|
17895
|
+
here: {
|
|
17896
|
+
label: "Build it here with me",
|
|
17897
|
+
pitch: "I build it in code right here, and you change anything by asking me"
|
|
17898
|
+
},
|
|
17899
|
+
editor: {
|
|
17900
|
+
label: "Design it in Wix's editor",
|
|
17901
|
+
pitch: "Wix designs a first version or you start from a template, then you edit it in Wix"
|
|
17902
|
+
},
|
|
17903
|
+
/** The editor's second step, in the widget and its reply. */
|
|
17904
|
+
editorStart: {
|
|
17905
|
+
wix: {
|
|
17906
|
+
label: "Let Wix design it",
|
|
17907
|
+
pitch: "Wix designs a first version from your description for you to edit"
|
|
17908
|
+
},
|
|
17909
|
+
template: {
|
|
17910
|
+
label: "Browse templates",
|
|
17911
|
+
pitch: "start from a ready-made design and edit it in Wix"
|
|
17912
|
+
}
|
|
17913
|
+
},
|
|
17914
|
+
/** Asked in chat when no widget is shown: who builds it. */
|
|
17915
|
+
whoQuestion: "Do you want me to build it here, so you can change anything just by asking me, or do you want to design it yourself in Wix's editor?",
|
|
17916
|
+
/** Asked next, only when the user chose Wix's editor. */
|
|
17917
|
+
editorQuestion: "Should Wix design a first version for you to edit, or do you want to start from a template?",
|
|
17918
|
+
/** What the widget sends as the user's message when they pick "Build it here with me". */
|
|
17919
|
+
hereFollowUp: "Build it here with me, in code, so I can keep changing it by asking you."
|
|
17920
|
+
};
|
|
17921
|
+
|
|
17692
17922
|
// src/config/default-config.ts
|
|
17693
17923
|
var AGENT_GOAL = dedent_default`
|
|
17694
|
-
You
|
|
17695
|
-
|
|
17924
|
+
You help the user with their Wix sites and account.
|
|
17925
|
+
Work out what the task needs and do it: read the Wix skills, docs and API schemas that cover it, and write and run the code that calls Wix APIs, until the task is done.
|
|
17696
17926
|
`;
|
|
17927
|
+
var CALL_WIX_SITE_API_RECONNECT_LINE = "If the Wix MCP offers no ExecuteWixAPI at all, your Wix connection is likely an old or cached version: tell the user once to reconnect the Wix connector or restart the client, and use this tool until then.";
|
|
17928
|
+
var CALL_WIX_SITE_API_NO_EXECUTE_LINE = "If ExecuteWixAPI isn't in your tool list, use this tool.";
|
|
17697
17929
|
var DISCOVERY_FLOWS = [
|
|
17698
|
-
"- CONVERSATION CONTEXT BASED: find relevant docs article or API example for the user's prompt/task in the conversation context -> call API
|
|
17699
|
-
"- METHOD CODE EXAMPLE BASED: BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() to get method code examples -> call API
|
|
17700
|
-
"- FULL SCHEMA BASED: BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() -> no method code examples found -> inspect the method schema using SearchWixAPISpec or ReadFullDocsMethodSchema -> call API
|
|
17930
|
+
"- CONVERSATION CONTEXT BASED: find relevant docs article or API example for the user's prompt/task in the conversation context -> call the API with ExecuteWixAPI based on the docs article or API example",
|
|
17931
|
+
"- METHOD CODE EXAMPLE BASED: BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() to get method code examples -> call the API with ExecuteWixAPI based on the method code examples",
|
|
17932
|
+
"- FULL SCHEMA BASED: BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() -> no method code examples found -> inspect the method schema using SearchWixAPISpec or ReadFullDocsMethodSchema -> call the API with ExecuteWixAPI based on the schema"
|
|
17701
17933
|
];
|
|
17934
|
+
function directRouteLines(includeReadme, includeImageUploadGuidance) {
|
|
17935
|
+
return [
|
|
17936
|
+
...includeReadme ? [
|
|
17937
|
+
`- **Creating a new site.** Skip the docs too. A site is written in code and uploaded: StartDeployWebsite returns the upload guide; StartHeadlessBuild returns the headless guide, for a site that needs Wix business features (${WIX_BUSINESS_FEATURES}). Call one right away, without asking the user anything first: StartHeadlessBuild when the site needs Wix business features, otherwise StartDeployWebsite. WixHarmonyEditorBuild or SearchSiteTemplates only when the user asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor. Never create a site through ManageWixSite, CallWixSiteAPI or ExecuteWixAPI.`,
|
|
17938
|
+
"- **Suggesting domain names**: call GetSuggestedDomains directly, with the business or site name as the query."
|
|
17939
|
+
] : [],
|
|
17940
|
+
"- **Listing, showing or finding their Wix sites**: call ListWixSites directly.",
|
|
17941
|
+
...includeImageUploadGuidance ? [
|
|
17942
|
+
"- **Uploading local or attached image files to a Wix site**: call UploadImageToWixSite directly, with no docs, schema or API calls. Do NOT use ExecuteWixAPI, SearchWixAPISpec, or any Media Manager REST API for image uploads."
|
|
17943
|
+
] : []
|
|
17944
|
+
];
|
|
17945
|
+
}
|
|
17702
17946
|
function buildSystemReminderGuidelines(includeReadme, includeImageUploadGuidance) {
|
|
17703
|
-
const exception = includeReadme ? "**Exception:** " : "";
|
|
17704
17947
|
const lines = [];
|
|
17705
|
-
if (includeReadme) {
|
|
17706
|
-
lines.push(
|
|
17707
|
-
"if the WixREADME tool is available to you, YOU MUST USE IT AT THE BEGINNING OF ANY CONVERSATION and then continue with calling the other tools and calling the Wix APIs until the task is completed.",
|
|
17708
|
-
"**Creating a new Wix site \u2014 and only then \u2014 skip WixREADME and the docs.** First settle one thing: will the user keep working on this site with you, in code, or design it themselves in Wix's editor? In code \u2014 you or the user have code, the two of you designed it here, they asked you to build it, they asked for a headless site, or it is an app: StartDeployWebsite if the code exists in any form, StartHeadlessBuild if not, called right away (it only returns the guide; the site's purpose is not needed first). In Wix's editor: the site-creation tools; their descriptions carry the rules. Everything else starts with WixREADME as usual.",
|
|
17709
|
-
"Never default to AI or search templates unless the user explicitly requests it. When the user named no creation method - let the user make that choice as the site-creation tools' descriptions direct. Never route site creation through ManageWixSite, CallWixSiteAPI, or ExecuteWixAPI."
|
|
17710
|
-
);
|
|
17711
|
-
}
|
|
17712
17948
|
lines.push(
|
|
17713
|
-
|
|
17949
|
+
includeReadme ? "Start every Wix task with WixREADME, questions included, then use the other tools and the Wix APIs until the task is done. If WixREADME is not available, follow the flows without it. These requests skip it and go straight to their own tool:" : "These requests go straight to their own tool, with no docs or API calls first:"
|
|
17714
17950
|
);
|
|
17715
|
-
|
|
17716
|
-
lines.push(
|
|
17717
|
-
`${exception}If the user wants to upload local or attached image files to a Wix site, skip ${includeReadme ? "WixREADME and " : ""}all docs/schema/API flows \u2014 call UploadImageToWixSite directly. Do NOT use ExecuteWixAPI, SearchWixAPISpec, or any Media Manager REST API for image uploads.`
|
|
17718
|
-
);
|
|
17719
|
-
}
|
|
17720
|
-
if (includeReadme) {
|
|
17721
|
-
lines.push(
|
|
17722
|
-
"If the WixREADME tool is not available to you, you should use the other flows as described without using the WixREADME tool until the task is completed."
|
|
17723
|
-
);
|
|
17724
|
-
}
|
|
17951
|
+
lines.push(...directRouteLines(includeReadme, includeImageUploadGuidance));
|
|
17725
17952
|
lines.push(
|
|
17726
|
-
`
|
|
17727
|
-
"
|
|
17953
|
+
`A question follows the same routes: one about those requests goes to its tool ("can you show my sites?" is ListWixSites). Any other Wix question is answered from ${includeReadme ? "WixREADME, the Wix skills, " : ""}the docs search and the matching docs articles, never from memory, and never with a reply that you lack the tools or APIs to answer. Many questions, including ones that need no API call, are answered by a ${includeReadme ? "Wix skill or docs article" : "docs article"}.`,
|
|
17954
|
+
"When the user asks you to do something in Wix, do it, in as few steps and questions as you can; don't tell them which docs to read or which API to call."
|
|
17728
17955
|
);
|
|
17729
17956
|
return lines.join("\n");
|
|
17730
17957
|
}
|
|
17731
17958
|
function buildSystemReminderFlows(includeReadme) {
|
|
17732
17959
|
const lines = ["Wix MCP Site Management Flows"];
|
|
17960
|
+
const noSkill = "no Wix skill covers the user's prompt/task -> ";
|
|
17733
17961
|
if (includeReadme) {
|
|
17734
17962
|
lines.push(
|
|
17735
17963
|
"With WixREADME tool:",
|
|
17736
|
-
"-
|
|
17964
|
+
"- SKILL BASED (PREFERRED!): WixREADME() -> find the Wix skill for the user's prompt/task (WixREADME says how to read one) -> follow it -> call the API with ExecuteWixAPI based on it",
|
|
17737
17965
|
DISCOVERY_FLOWS[0],
|
|
17738
|
-
|
|
17739
|
-
|
|
17966
|
+
`- EXAMPLE BASED: WixREADME() -> ${noSkill}BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() to get method code examples -> call the API with ExecuteWixAPI based on the method code examples`,
|
|
17967
|
+
`- SCHEMA BASED, FALLBACK: WixREADME() -> ${noSkill}BrowseWixRESTDocsMenu() or SearchWixRESTDocumentation() -> find relevant method -> read method article using ReadFullDocsArticle() -> no method code examples found -> inspect the method schema using SearchWixAPISpec or ReadFullDocsMethodSchema -> call the API with ExecuteWixAPI based on the schema`,
|
|
17740
17968
|
"Without WixREADME tool:"
|
|
17741
17969
|
);
|
|
17742
17970
|
}
|
|
@@ -17769,41 +17997,22 @@ function buildSystemReminder(includeReadme, includeImageUploadGuidance = true) {
|
|
|
17769
17997
|
`;
|
|
17770
17998
|
}
|
|
17771
17999
|
var SYSTEM_REMINDER = buildSystemReminder(true);
|
|
18000
|
+
var HELP_CENTER_IS_FOR_PEOPLE = "The Help Center is written for people doing things by hand in Wix's dashboard and editors. To act on a site yourself, the Wix developer docs come first: find the method with SearchWixRESTDocumentation or BrowseWixRESTDocsMenu.";
|
|
17772
18001
|
var SITE_CREATION_COPY = {
|
|
17773
18002
|
templatePick: {
|
|
17774
18003
|
nonWidget: "After the user selects a template, call `CreateSiteFromTemplate`.",
|
|
17775
18004
|
widget: "The gallery creates the site when the user picks one."
|
|
17776
|
-
},
|
|
17777
|
-
unstatedMethod: {
|
|
17778
|
-
nonWidget: "you MUST ask whether they want to build with AI or start from a template, and create nothing and show nothing until they answer",
|
|
17779
|
-
widget: "you MUST call `WixSiteCreation` and let them choose"
|
|
17780
18005
|
}
|
|
17781
18006
|
};
|
|
17782
|
-
var
|
|
17783
|
-
First decide who keeps building the site:
|
|
17784
|
-
- **You, in code**: a Wix Headless site, with full control of the design, changed and redeployed from this conversation, Wix business features added as a backend. This is the case when you or the user have code (hand-written files, a static build, a zip, another builder's export, a page you made here): call StartDeployWebsite. It is also the case when nothing is coded yet but the user asked you to build it or asked for a headless site, the two of you worked out its design in this conversation, or it is an app (a game, a dashboard, a tool): call StartHeadlessBuild right away, before asking what the site is for. Do not offer AI or templates for either.
|
|
17785
|
-
- **The user, in Wix's editor**: Wix generates it with AI or starts it from a template, and the user designs it in Wix. That is what the site-creation tools do; they never take files you already have.
|
|
17786
|
-
|
|
17787
|
-
For a new site in Wix's editor there are two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
|
|
17788
|
-
|
|
17789
|
-
Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
|
|
17790
|
-
If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
|
|
17791
|
-
Once the purpose is clear, unless the user already named a method, ${SITE_CREATION_COPY.unstatedMethod.nonWidget}. A method they named is settled — never re-open it. Never default to AI or to templates, and never ask the user to choose between Harmony and Wix Studio; infer that from their words.
|
|
17792
|
-
With the purpose and the method known, that's enough to begin — route and refine the specifics with the user afterward.
|
|
17793
|
-
|
|
17794
|
-
Preferred question when the user has given no site context at all:
|
|
17795
|
-
Happy to help. To give you the right starting point, tell me a bit more about what you have in mind:
|
|
17796
|
-
- What's the site for? (e.g. an online clothing store, a yoga studio that takes bookings, a restaurant)
|
|
17797
|
-
- What's the vibe? (e.g. sleek and modern, cozy and rustic, bold and energetic)
|
|
17798
|
-
Give me a quick summary and I'll take it from there
|
|
17799
|
-
|
|
17800
|
-
Tone: helpful, practical, short. Speak about the user's site — what you're making and what comes next — not the tools, payloads, or routing behind it.`;
|
|
18007
|
+
var EDITOR_TAIL = `When the user wants Wix's editor, or to design the site themselves, without saying how to start, ask them first: "${SITE_BUILDER_CHOICE.editorQuestion}" A new site is otherwise written in code and uploaded: call StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features, right away and without asking the user anything first.`;
|
|
17801
18008
|
var toWidget = (text) => Object.values(SITE_CREATION_COPY).reduce(
|
|
17802
18009
|
(acc, { nonWidget, widget }) => acc.replaceAll(nonWidget, widget),
|
|
17803
18010
|
text
|
|
17804
18011
|
);
|
|
18012
|
+
var WIX_API_DATA = "products, inventory, carts, checkouts, orders, bookings, services, staff, time slots, rentals, restaurant menus, online orders, reservations, events, tickets, RSVPs, blog posts, CMS collections and items, pricing plans, subscriptions, portfolios, gift cards, coupons, donations, forms, form submissions, contacts, members, loyalty points, groups, reviews, inbox messages, CRM tasks, automations, email campaigns, payments, invoices, SEO, redirects, translations, locations, analytics, media files, site properties, installed apps, and the account's sites and domains (searching whether a domain is available, buying one, and connecting one bought elsewhere)";
|
|
17805
18013
|
var EXECUTE_WIX_API_INTRO = dedent_default`
|
|
17806
|
-
|
|
18014
|
+
Reads and changes the data of a Wix site or account: query, list, count, create, update and delete it with one JavaScript function over the Wix REST API.
|
|
18015
|
+
One function holds the whole task: chained calls, pagination, loops, branching, and the reads that feed its writes.
|
|
17807
18016
|
Putting a site live or changing a headless site's files starts with StartDeployWebsite; building a new site in code with StartHeadlessBuild.
|
|
17808
18017
|
|
|
17809
18018
|
CODE CONTRACT:
|
|
@@ -17889,7 +18098,7 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
17889
18098
|
declare const files: Array<{ path: string; content: string; encoding?: "base64" }>;
|
|
17890
18099
|
\`\`\`
|
|
17891
18100
|
|
|
17892
|
-
FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments
|
|
18101
|
+
FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments\`: ${fileParamNote("upload from your shell if it reaches the internet")} Any type, up to 10 MB per call, all in one call: a zip of a whole site, HTML, images, PDFs; the bytes arrive as base64 entries named by file name. The \`files\` param takes content you have as text here: text goes raw; a small binary (icon, logo, font) rides as a \`base64\`-tagged entry — large images/video are referenced by URL (\`<img src="https://...">\`), not bundled. \`wix.multipart()\` turns the whole set into one upload body (bytes, so binary is safe):
|
|
17893
18102
|
\`\`\`javascript
|
|
17894
18103
|
// files param:
|
|
17895
18104
|
// === FILE: index.html ===
|
|
@@ -17904,6 +18113,7 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
17904
18113
|
return res.data;
|
|
17905
18114
|
}
|
|
17906
18115
|
\`\`\`
|
|
18116
|
+
HEADLESS SITE EDITS: \`wix.headless.files.list({siteId, prefix?, offset?, limit?})\` returns file metadata; \`read({siteId, path})\` returns UTF-8 text (up to 256 KB); \`patch({siteId, files?, deletePaths?, agent?})\` applies changed files and explicit deletions while preserving everything else. Patch defaults to the Execute \`files\` global (including resolved attachments and ZIPs). Paths are literal site-relative paths; no wrapping-folder stripping. It returns added/replaced/deleted/unchanged/missingDeletes path lists. List/read reuse one downloaded ZIP snapshot for the current site within an Execute call; switching sites discards the previous snapshot, and a new call downloads afresh. Patch downloads a fresh ZIP, then drops the full merged site and clears the read snapshot. Serialize edits to the same site; there is no atomic concurrency guarantee. For a full replacement, use the existing upload/drop path. See the upload-static-site recipe for examples and older-server fallback.
|
|
17907
18117
|
A site zip passed through \`attachments\` is one base64 entry; unzip it and upload the entries:
|
|
17908
18118
|
\`\`\`javascript
|
|
17909
18119
|
// attachments: [site.zip] → files = [{ path: "site.zip", content: "<base64>", encoding: "base64" }]
|
|
@@ -18027,22 +18237,37 @@ var executeWixApiDescription = dedent_default`
|
|
|
18027
18237
|
|
|
18028
18238
|
${EXECUTE_WIX_API_MULTI_SITE_EXAMPLE}
|
|
18029
18239
|
`;
|
|
18030
|
-
function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true) {
|
|
18031
|
-
const
|
|
18240
|
+
function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true, { usageGuideOnce = false } = {}) {
|
|
18241
|
+
const usageGuide = buildSystemReminder(
|
|
18032
18242
|
includeReadme,
|
|
18033
18243
|
includeImageUploadGuidance
|
|
18034
18244
|
);
|
|
18035
|
-
|
|
18245
|
+
const reminder = includeReadme && usageGuideOnce ? "" : usageGuide;
|
|
18246
|
+
const descriptions = {
|
|
18247
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18036
18248
|
WixREADME: dedent_default`
|
|
18037
18249
|
# Tool: WixREADME
|
|
18038
|
-
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
${
|
|
18250
|
+
The context every other Wix tool assumes. Call it first, once per conversation, for any Wix task or question; for another site later in the conversation, call \`GetSiteContext\` instead of calling this again.
|
|
18251
|
+
A Wix task includes reading or changing a site's or account's data: ${WIX_API_DATA}.
|
|
18252
|
+
These requests skip it and go straight to their own tool:
|
|
18253
|
+
${directRouteLines(includeReadme, includeImageUploadGuidance).join("\n")}
|
|
18254
|
+
A question about one of those goes to the same tool ("can you show my sites?" is ListWixSites). Any other Wix question is answered from a Wix skill or the docs, never from memory, and never with a reply that you lack the tools to answer.
|
|
18255
|
+
It returns, in full:
|
|
18256
|
+
- **Source of truth**: nothing Wix-specific from memory. Every URL, ID, setting location, product fact, request shape and field name comes from the docs tools, a Wix skill, an earlier tool result in the conversation, or the user.
|
|
18257
|
+
- **The site**: the account's sites with their properties and installed apps. The \`siteId\` comes from there (ask when several fit); a business solution's API works only on a site with its app installed, and an app's version changes its endpoints.
|
|
18258
|
+
- **Find the method**: an earlier tool result, else a Wix skill's recipe (wix-manage to manage a site, wix-headless-kit to build a headless one), else the docs: SearchWixRESTDocumentation or BrowseWixRESTDocsMenu, then ReadFullDocsArticle; the SDK, CLI, headless and apps docs for code.
|
|
18259
|
+
- **The exact shape**: model a call only on an example that matches the case; fill the rest with SearchWixAPISpec on the method's docs URL (request and response schemas, \`method.publicUrl\` for REST, \`method.sdk\` for the JavaScript SDK). A query's filter and sort fields and operators are a closed list per method.
|
|
18260
|
+
- **The call, or the code**: ExecuteWixAPI runs one async function holding every step code can resolve, the reads and the writes they feed together, \`hasMutations: true\` when anything writes. Code the user keeps (an app, a headless site, a script over REST or the JavaScript SDK) is written from the same shapes; the headless skills are a shortcut on that path.
|
|
18261
|
+
- **When a call fails**: the error says what was wrong and carries a Wix request id; fix that, never resend unchanged or guess.
|
|
18262
|
+
- **Wix skills**, what each is for and how to read one: from disk when installed (the Wix plugin, or \`npx skills add wix/skills\`, \`--skill <name>\` for one, \`-g\` for the user's home, into \`.agents/skills/<skill>/\` or a similar folder the client reads, such as \`.claude/skills/<skill>/\`), else online with ReadFullDocsArticle at \`https://www.wix.com/skills/<skill>/<path>\`.
|
|
18263
|
+
- **wix-manage**: REST recipes for managing a site's business solutions, one per task; follow the recipe and make its calls with ExecuteWixAPI, its docs URL in \`sourceDocUrls\`. Its list of recipes is \`SKILL.md\` in the installed skill, or online https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/skills/wix-management-recipes-index.md, and each API area has its own \`<API section>/skills.md\` (for example https://dev.wix.com/docs/api-reference/business-solutions/bookings/skills.md).
|
|
18264
|
+
- **wix-headless-kit**: builds a Wix Headless site on shipped, verified code from wix-headless-templates, a \`@wix/sdk\` React core or the same data layer as plain REST calls: scaffold with the CLI, deploy, seed, design on the shipped hooks, release. https://www.wix.com/skills/headless-kit/SKILL.md
|
|
18265
|
+
- **wix-headless-templates**: the code wix-headless-kit builds from, one folder per Wix Business Solution. https://www.wix.com/skills/headless-templates/
|
|
18266
|
+
When the user asks you to do something in Wix, do it yourself, in as few steps and questions as you can; don't tell them which docs to read or which API to call.
|
|
18042
18267
|
`,
|
|
18043
18268
|
SearchWixRESTDocumentation: dedent_default`
|
|
18044
18269
|
**Searches the official Wix REST API documentation — the starting point for any Wix REST API task.** Unless you already have the exact method \`docsUrl\` in context, begin the task here; this is how you discover the right endpoint. Reach for it before \`SearchWixAPISpec\`, and avoid writing code that scans or filters \`lightIndex\` to find an endpoint (e.g. \`lightIndex.filter(...)\`, \`lightIndex.flatMap(...)\`, or substring-matching on \`docsUrl\`/\`name\`/\`summary\`) — that has no relevance ranking and silently misses composite and method-level operations. This is the ranked semantic-search discovery tool over the Wix REST API docs: describe your intent in natural language and it returns the matching REST endpoints/methods (with their \`docsUrl\`) ranked by relevance. It is the first step whenever you need to do something on a Wix site via HTTP and don't already have the exact \`docsUrl\` in context — for creating, reading, updating, or deleting any kind of Wix entity, across every business domain.
|
|
18045
|
-
It covers all Wix REST domains equally
|
|
18270
|
+
It covers all Wix REST domains equally, every product's data: ${WIX_API_DATA}, and the rest. Just describe the resource, action, or goal in plain words (e.g. 'add a catalog item with inventory', 'book a service', 'update a data collection item', 'create a discount', 'list contacts', 'get site details endpoint', 'REST authentication'); the ranking handles matching your wording to the right API regardless of domain.
|
|
18046
18271
|
**Next step after this tool:** take the \`docsUrl\` it returns and inspect that method's exact request/response schema with \`SearchWixAPISpec\` (\`getResourceSchemaByUrl(docsUrl)\`), then execute. \`SearchWixAPISpec\` is only for INSPECTING a \`docsUrl\` you already have — it is NOT a discovery tool. Skipping this step to hand-search \`lightIndex\` yourself is a mistake: raw index filtering has no relevance ranking and misses operations this ranked search finds. Discover here first, every time.
|
|
18047
18272
|
If you can't find what you need, rephrase your search term or use a bigger maxResults value.
|
|
18048
18273
|
${reminder}
|
|
@@ -18064,6 +18289,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18064
18289
|
`,
|
|
18065
18290
|
CallWixSiteAPI: dedent_default`
|
|
18066
18291
|
Call Wix apis on a business or site. Use this to create, read, update, and delete data and other Wix entities in your Wix site.
|
|
18292
|
+
ExecuteWixAPI is the preferred tool for Wix API calls: one function holds every call of the task, the reads and the writes they feed together, where this tool makes one call per invocation. If your client loads tools on demand (tool search, deferred tools), load ExecuteWixAPI before using this tool. ${CALL_WIX_SITE_API_RECONNECT_LINE}
|
|
18067
18293
|
Site-level: this tool reads and writes inside the single site named by the required \`siteId\`.
|
|
18068
18294
|
**Prefer using the "ListWixSites" tool when the user asks to list or show their sites.** Only use this tool for site listing if the user needs advanced filtering or specific site details beyond what ListWixSites provides.
|
|
18069
18295
|
For POST/PATCH/PUT requests, pass the request body as a JSON object or array in the "body" parameter with all the required fields and values as described in the API schema, code examples, or docs you retrieved (e.g. body: {"name": "value", "nested": {"key": "value"}} or body: [{"key": "value"}]).
|
|
@@ -18078,6 +18304,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18078
18304
|
Docs urls like https://dev.wix.com/docs/... are not api urls, if you want to read the docs, use the "ReadFullDocsArticle" tool
|
|
18079
18305
|
${reminder}
|
|
18080
18306
|
`,
|
|
18307
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18081
18308
|
ListWixSites: dedent_default`
|
|
18082
18309
|
**Use this tool whenever the user asks to list, show, get, or find their Wix sites.** This is the dedicated tool for listing Wix sites for the current user. By default it returns all sites, but you can filter by name.${includeReadme ? "\n\n**Do NOT use WixREADME before this tool.** When the user asks to list their sites, call this tool directly \u2014 no need to read documentation first." : ""}
|
|
18083
18310
|
|
|
@@ -18098,13 +18325,13 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18098
18325
|
${reminder}
|
|
18099
18326
|
`,
|
|
18100
18327
|
ManageWixSite: dedent_default`
|
|
18101
|
-
|
|
18102
|
-
Account-level only: this tool acts on the account itself, or on sites as objects within it, and sends no site context. Anything that reads or writes inside a site is site-level and does not belong here — use a site-level tool with that site's \`siteId\`.
|
|
18328
|
+
Calls an account-level Wix API: one that acts on the account itself (its sites list, domains, account settings) or on sites as objects in it (create, rename, duplicate, delete a site). It sends no site, so an API that reads or writes inside a site (its products, data, bookings, members, pages, publishing) fails here: call that through ExecuteWixAPI with the site's \`siteId\`.
|
|
18103
18329
|
For POST/PATCH/PUT requests, pass the request body as a JSON object or array in the "body" parameter with all the required fields and values as described in the API schema, code examples, or docs you retrieved (e.g. body: {"name": "value", "nested": {"key": "value"}} or body: [{"key": "value"}]).
|
|
18104
18330
|
The API endpoint url param MUST ALWAYS be taken from the conversation context.
|
|
18105
18331
|
By conversation context we mean the endpoint url was given in the user prompt or got into the conversation context ${includeReadme ? 'by the "WixREADME" tool or ' : ""}by the "SearchWixRESTDocumentation" tool or by the "BrowseWixRESTDocsMenu" tool or by the "ReadFullDocsArticle" tool.
|
|
18106
18332
|
${reminder}
|
|
18107
18333
|
`,
|
|
18334
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18108
18335
|
UploadImageToWixSite: dedent_default`
|
|
18109
18336
|
Upload one or more images to a Wix site's Media Manager. Returns wixstatic.com URL and media ID.
|
|
18110
18337
|
Do NOT use ExecuteWixAPI or code execution for image uploads — use this tool directly.
|
|
@@ -18114,34 +18341,39 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18114
18341
|
NEVER call this tool with a missing, empty, or guessed siteId — ask first instead.
|
|
18115
18342
|
|
|
18116
18343
|
Parameters — provide image input via image and/or imageUrls, OR imageBase64:
|
|
18117
|
-
• image (array):
|
|
18344
|
+
• image (array): ${fileParamNote("send the image as imageBase64 (the whole file, one per call) or as a public URL in imageUrls")} Pass ALL of them in one call.
|
|
18118
18345
|
• imageUrls (array): plain public image URLs the user pasted or other tools produced (no file_id). Pass ALL URLs in one call. Can be combined with image.
|
|
18119
18346
|
• imageBase64 (string): base64-encoded image + mimeType. One image at a time. Do NOT combine with image/imageUrls.
|
|
18120
18347
|
|
|
18121
18348
|
Delivering the bytes — this is where uploads most often fail:
|
|
18122
|
-
•
|
|
18349
|
+
• This server does not share your filesystem: outside image's clients, a file you can only reach by path (e.g. /mnt/..., /home/user/..., /Users/...) goes as imageBase64 (the whole file, one per call) or, if it is public, as imageUrls.
|
|
18123
18350
|
• imageBase64 must be the WHOLE file, not truncated. For large images prefer imageUrls, since long base64 is easily cut off and will be rejected as unreadable.
|
|
18124
18351
|
`,
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
|
|
18352
|
+
// Registered for Claude clients only, beside UploadImageToWixSite.
|
|
18353
|
+
PickImagesForWixMedia: dedent_default`
|
|
18354
|
+
Shows a file picker where the user selects images from their device; they upload to the site's Media Manager and the result (each image's media ID and wixstatic URL) comes back in the conversation.
|
|
18355
|
+
Use it only when you can't deliver the image yourself with UploadImageToWixSite: the image is on the user's device and not in your files, or it is a file in your sandbox that you can't pass as a file, a public URL or base64. When UploadImageToWixSite can take it, use that.
|
|
18356
|
+
`,
|
|
18357
|
+
ReportIssueToWix: dedent_default`
|
|
18358
|
+
Report one specific problem with Wix's tools, APIs or docs to the Wix team, attributed to the authenticated user: an API error or a confusing API, wrong or missing docs, a Wix MCP tool or auth dead end. It is NOT a support channel and not for the user's own site content.
|
|
18128
18359
|
|
|
18129
18360
|
When to offer it (user-approved only — NEVER auto-send):
|
|
18130
|
-
- The user
|
|
18131
|
-
- The user complains
|
|
18132
|
-
-
|
|
18133
|
-
|
|
18134
|
-
When you offer, invite the user to add anything in their own words — whatever they give you goes into the
|
|
18135
|
-
|
|
18136
|
-
What to send —
|
|
18137
|
-
|
|
18138
|
-
|
|
18139
|
-
|
|
18140
|
-
|
|
18141
|
-
|
|
18142
|
-
|
|
18143
|
-
|
|
18144
|
-
|
|
18361
|
+
- The user asks to report something to Wix ("tell Wix that...", "report this", "send feedback"). Then confirm the wording and send.
|
|
18362
|
+
- The user complains or reports a Wix bug in the course of the work ("this API is broken", "the docs are wrong"). Acknowledge, then offer to pass it on.
|
|
18363
|
+
- A Wix API, tool or doc kept failing, or you had to invent a workaround for it. Offer: "This tripped us up — want me to report it to Wix?"
|
|
18364
|
+
|
|
18365
|
+
When you offer, invite the user to add anything in their own words — whatever they give you goes into the report verbatim.
|
|
18366
|
+
|
|
18367
|
+
What to send — that one issue, not the session:
|
|
18368
|
+
- What was being attempted, in a sentence.
|
|
18369
|
+
- Where it failed: the tool or endpoint, the HTTP status and error message, and the request id if there is one (pass it as requestId).
|
|
18370
|
+
- What you expected instead, and the workaround if you found one.
|
|
18371
|
+
- Where the fault likely lives: API behavior / API schema / API reference docs / docs articles or examples / Wix tooling / unsure.
|
|
18372
|
+
- Only the context needed to reproduce it: agent/model, site id, the Wix area or API.
|
|
18373
|
+
No session summary or play-by-play, and nothing unrelated to the issue. Several distinct issues are separate reports, each approved by the user.
|
|
18374
|
+
|
|
18375
|
+
**IMPORTANT**: This tool actually sends the report to Wix. Confirm the final wording with the user and call it only after an explicit yes.
|
|
18376
|
+
Do not send on a single transient error, and never more than once for the same issue. Reports cannot be replied to or tracked by the user.
|
|
18145
18377
|
`,
|
|
18146
18378
|
BrowseWixRESTDocsMenu: dedent_default`
|
|
18147
18379
|
Browse the Wix REST API documentation menu hierarchy.
|
|
@@ -18163,25 +18395,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18163
18395
|
${reminder}
|
|
18164
18396
|
`,
|
|
18165
18397
|
WixSiteCreation: dedent_default`
|
|
18166
|
-
|
|
18167
|
-
|
|
18168
|
-
Opens the in-chat Wix site creation chooser as an interactive widget where the user chooses between "Build with AI" and "Pick a template."
|
|
18169
|
-
|
|
18170
|
-
**Once you can tell what the site is for, call this tool directly** — no ${includeReadme ? "WixREADME, skill," : "skill"} or docs search first.${includeReadme ? " Any other Wix request still starts with WixREADME as usual." : ""}
|
|
18171
|
-
|
|
18172
|
-
Call this for normal new Wix site creation requests when the user's site intent is clear, but they have not chosen AI, templates, or Wix Studio.
|
|
18173
|
-
Do not guess, and never default to AI — even after the user answers a clarifying question about the site's purpose or features.
|
|
18174
|
-
This chooser is how the builder choice gets made — never ask in chat whether they want AI or a template; that choice is made in the widget.
|
|
18175
|
-
The widget also does the creating: once the user picks, it runs that path itself and the site is created there, so never call \`WixSiteBuilder\`, \`SearchSiteTemplates\`, or \`CreateSiteFromTemplate\` yourself after opening it.
|
|
18176
|
-
After calling, reply with the message it returns, which presents both options and the next steps.
|
|
18177
|
-
|
|
18178
|
-
Do not call this if the user explicitly asks to build with AI; call \`WixSiteBuilder\` instead.
|
|
18179
|
-
Do not call this if the user asks for templates or Wix Studio; call \`SearchSiteTemplates\` instead.
|
|
18180
|
-
Not for a site the user wants you to build in code, a design you worked out together, or an app: call \`StartHeadlessBuild\`.
|
|
18181
|
-
|
|
18182
|
-
When calling this tool, infer \`customActions\` from the user's request so the reply can be tailored — 2-3 concrete, industry-specific setup actions for the site (see the input's guidance).
|
|
18183
|
-
|
|
18184
|
-
After calling, you MUST speak the message provided in the tool result to the user as your reply — do not stay silent and do not replace it with a one-line pointer to the widget. Write out ALL of it, word for word: its opening line (the "Ready to set up your … site?" question INCLUDING "Choose your path in the widget above:"), BOTH of its option bullets, and its "Next up:" line, each on its own line and in that order. Do not summarize, shorten, drop any line, invent your own opener, rename the options, or add an intro or outro.
|
|
18398
|
+
Returns which Wix tool creates a new site, and creates nothing itself. A new site is written in code and uploaded: StartDeployWebsite returns the upload guide, and StartHeadlessBuild returns the guide for a site that needs Wix business features. Use WixHarmonyEditorBuild or SearchSiteTemplates only when the user asked for Wix's editor, a template, Wix Studio or the Classic Editor.
|
|
18185
18399
|
`,
|
|
18186
18400
|
"import-claude-design-from-url": dedent_default`
|
|
18187
18401
|
Import a design created in Claude's design tool into Wix. Only for the signed \`claudeusercontent.com\` bundle URL Claude Design's "Send to Wix" produces (a self-contained HTML bundle with all images, fonts, and styles inlined). Creates a live Wix-hosted site and returns its URL.
|
|
@@ -18211,6 +18425,13 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18211
18425
|
`,
|
|
18212
18426
|
SearchWixWDSDocumentation: dedent_default`
|
|
18213
18427
|
Searches the Wix Design System (WDS) documentation.
|
|
18428
|
+
`,
|
|
18429
|
+
SearchWixHelpCenter: dedent_default`
|
|
18430
|
+
Searches the Wix Help Center (support.wix.com): how site owners do things in the Wix dashboard and editors, such as connecting a domain, managing a Premium plan, setting up a store or bookings, and fixing account or site issues.
|
|
18431
|
+
${HELP_CENTER_IS_FOR_PEOPLE}
|
|
18432
|
+
Use it for "how do I ... in Wix" questions about the product, or to give the user click-by-click steps for something no API covers.
|
|
18433
|
+
Not for building with code: for Wix APIs, SDK, CLI, apps, or Headless, use the matching developer documentation search.
|
|
18434
|
+
Results are excerpts; read a full article by passing its support.wix.com link to \`ReadFullDocsArticle\`.
|
|
18214
18435
|
`,
|
|
18215
18436
|
SearchWixSDKDocumentation: dedent_default`
|
|
18216
18437
|
Searches the Wix JavaScript SDK documentation.
|
|
@@ -18221,7 +18442,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18221
18442
|
`,
|
|
18222
18443
|
SearchDevelopmentDocumentation: dedent_default`
|
|
18223
18444
|
Broad Wix development documentation (platform concepts, developer guides, getting started). **Last resort — development tasks only.**
|
|
18224
|
-
Skip it entirely for general, business, support, or Wix-editor questions
|
|
18445
|
+
Skip it entirely for general, business, support, or Wix-editor questions; for those, use \`SearchWixHelpCenter\`.
|
|
18225
18446
|
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.
|
|
18226
18447
|
`,
|
|
18227
18448
|
SearchWixHeadlessDocumentation: dedent_default`
|
|
@@ -18233,7 +18454,8 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18233
18454
|
// `sdk` is simply absent on methods the SDK docs don't list.
|
|
18234
18455
|
SearchWixAPISpec: dedent_default`
|
|
18235
18456
|
Inspect the Wix REST API spec, and the JavaScript SDK call for each method, by writing JavaScript code that runs in a sandboxed read-only environment.
|
|
18236
|
-
|
|
18457
|
+
The spec covers every Wix product's methods and their data: ${WIX_API_DATA}.
|
|
18458
|
+
**Precondition:** you already have a method \`docsUrl\` — from \`SearchWixRESTDocumentation\`, \`SearchWixSDKDocumentation\` (its \`?apiView=SDK\` URLs work here as-is), \`BrowseWixRESTDocsMenu\`, \`ReadFullDocsArticle\`, ${includeReadme ? "a Wix skill, " : ""}or a prior tool result / the user's message in this conversation. If you don't have one yet, call \`SearchWixRESTDocumentation\` (or \`SearchWixSDKDocumentation\` when writing SDK code) FIRST to discover it; do not use this tool to search for or choose an endpoint.
|
|
18237
18459
|
**Never pass a \`docsUrl\` you produced from memory.** If the exact URL you are about to pass to \`getResourceSchemaByUrl\` did not appear verbatim in an earlier tool result (or the user's message), it is a guess — call \`SearchWixRESTDocumentation\` first and use a URL from its ranked results. A guessed URL that merely looks plausible routinely resolves to the WRONG sibling method (e.g. \`.../products-v3/create-product\` when the task needs \`.../products-v3/create-product-with-inventory\`), sending the whole turn down a dead end. When in doubt, discover — don't guess.
|
|
18238
18460
|
**This tool's job is to INSPECT the exact schema of a method you have ALREADY located — its request/response shape, nested types, and enums, and its SDK call (\`method.sdk\`). It is NOT a discovery tool for choosing which method to use.**
|
|
18239
18461
|
With a method \`docsUrl\` in hand, call \`getResourceSchemaByUrl(methodDocsUrl)\` to get the resource schema, then read the target method from \`schema.methods\` and resolve any nested \`{ $circular: name }\` types via \`schema.components.schemas[name]\`.
|
|
@@ -18614,27 +18836,18 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18614
18836
|
\`\`\`
|
|
18615
18837
|
`,
|
|
18616
18838
|
ExecuteWixAPI: executeWixApiDescription,
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
|
|
18620
|
-
|
|
18621
|
-
|
|
18622
|
-
Call this ONLY when the user's own words name AI (for example "create a site using AI" or "build it with AI") and the site intent is clear. A description of what the site should do — reservations, online ordering, a store, bookings, menus, a portfolio, and the like — is NOT a request for AI and NOT enough to call this tool; never infer AI from features or industry.
|
|
18623
|
-
Never default to this tool: if the user hasn't explicitly named AI, don't call this tool — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.
|
|
18624
|
-
|
|
18625
|
-
For templates or Wix Studio, call \`SearchSiteTemplates\` (\`builder: classic\` for the Classic Editor); for a template id, \`CreateSiteFromTemplate\`. A site the user wants you to build in code, or an app: \`StartHeadlessBuild\`.
|
|
18626
|
-
|
|
18627
|
-
Do NOT suggest HTML code, prompt templates, or alternative approaches instead of building — actually build the site through this tool. (HTML the user already has is the StartDeployWebsite case, not a reason to build.)
|
|
18839
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18840
|
+
WixHarmonyEditorBuild: dedent_default`
|
|
18841
|
+
A secondary site-creation tool, only for a user who asked for the Wix Harmony Editor or for Wix to design the site in Wix's editor. A request for a site on Wix, or for Wix to build or make one, is neither. Don't call it for any other site request, not even to check with the flag false. A new site is otherwise written in code and uploaded, with StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features.
|
|
18842
|
+
Builds a site with the Wix Harmony Editor: Wix designs a first version from the user's description, and the user keeps designing it in Wix's editor. Shows the build's progress and a preview.
|
|
18843
|
+
Call it when the user asked for the Wix Harmony Editor, or for Wix to design the site in Wix's editor, in their own words or as their answer to your question, and quote those words in \`editorRequest\`. ${EDITOR_TAIL}
|
|
18628
18844
|
`,
|
|
18845
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18629
18846
|
SearchSiteTemplates: dedent_default`
|
|
18630
|
-
|
|
18631
|
-
|
|
18632
|
-
|
|
18633
|
-
|
|
18634
|
-
Call this ONLY when the user's own words ask for a template, for Wix Studio or for the Classic Editor (for example "start from a template", "show me some templates", "build it in Wix Studio", "the old Wix editor") and you can already tell what the site is for. If they mention Wix Studio, search Studio templates; if they ask for the Classic Editor (the Wix Editor), search \`classic\`; otherwise search Harmony templates.
|
|
18635
|
-
${SITE_CREATION_COPY.templatePick.nonWidget}
|
|
18636
|
-
|
|
18637
|
-
A description of what the site should do — bookings, a store, a menu, a portfolio, and the like — is NOT a request for templates or for Wix Studio; never infer either from features or industry. Templates are not the default: if the site intent is clear but no method is named, do not guess — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.`,
|
|
18847
|
+
A secondary site-creation tool, only for a user who asked for a template, Wix Studio or the Classic Editor. Don't call it for any other site request, not even to check with the flag false. A new site is otherwise written in code and uploaded, with StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features.
|
|
18848
|
+
Shows a gallery of Wix templates, from Harmony, Wix Studio or the Classic Editor, for a site the user will design in Wix's editor. Wix templates come with Wix's business features (a store, online ordering, bookings, forms), so a request for a template comes here even when the site needs them.
|
|
18849
|
+
Call it when the user asked for a template, Wix Studio or the Classic Editor, in their own words or as their answer to your question, and quote those words in \`templateRequest\`. ${EDITOR_TAIL} ${SITE_CREATION_COPY.templatePick.nonWidget}
|
|
18850
|
+
`,
|
|
18638
18851
|
CreateSiteFromTemplate: dedent_default`
|
|
18639
18852
|
Creates a Wix site from a specific template (by \`metaSiteId\` or \`templateId\`), publishes it when possible, and shows a live preview with links to the editor and the published site.
|
|
18640
18853
|
|
|
@@ -18668,102 +18881,211 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18668
18881
|
Set \`hasMutations: true\` for any document containing a mutation root field.
|
|
18669
18882
|
`,
|
|
18670
18883
|
UploadHeadlessWebsiteFiles: dedent_default`
|
|
18671
|
-
The upload step of the StartDeployWebsite and StartHeadlessBuild guides, and nothing else: puts a finished
|
|
18672
|
-
Pass the files themselves: in
|
|
18673
|
-
NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here (
|
|
18884
|
+
The upload step of the StartDeployWebsite and StartHeadlessBuild guides, and nothing else: puts a finished website's files — a static site (index.html plus its CSS/JS/images, a static build, or a zip of the site) or a Wix Astro build — onto an existing Wix Headless site by its metaSiteId. Call it ONLY after one of them has returned its guide in this conversation and the site exists: for a new site, first run the recipe's provision call and pass the metaSiteId it returns; for a site deployed earlier, pass its metaSiteId. It does not create sites. Every upload replaces the whole file set and the URL stays. Returns the live URL and the dashboard link, and points back to the recipe for what comes next.
|
|
18885
|
+
Pass the files themselves: in ${FILE_PARAM_CLIENTS} as \`attachments\` (any type, a zip is unpacked), elsewhere as the \`files\` text bundle (\`=== FILE: <path> ===\` sections, \`base64\` tag for small binaries). A static site must contain index.html at its root. A Wix Astro build with no file handle, from a shell that reaches the internet, goes in two calls: zip the build, call with \`prepare: true\` and the zip's \`size\` in bytes, PUT the zips it names with the \`curl\` commands it returns (the build, and the project source when the site's repository accepts it), then call again with the \`uploadKey\` it returned; that releases the build. The two calls, literally: {siteId, prepare: true, size} then {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. A sandbox whose network is limited to a few hosts cannot upload to those URLs. A static site does not use the two calls. A Wix Astro build (the project's .wix/ and dist/ after \`astro build\` with the Wix hosting adapter, zipped from the project root) needs no index.html: it is released to the site's Wix hosting instead, server-rendered pages, the same site and URL. The project's source is stored with the release when the upload holds the project and the site's repository accepts it.
|
|
18886
|
+
NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here first. Unless the user asked for Wix's editor or a template (WixHarmonyEditorBuild, SearchSiteTemplates), call StartDeployWebsite right away, without asking the user anything: it also takes files the user brought, uploaded first even when the site needs business features. StartHeadlessBuild instead for a new site with no files that needs Wix business features. Also NOT for media uploads to an existing site (UploadImageToWixSite) or any other API call (ExecuteWixAPI).
|
|
18674
18887
|
`,
|
|
18888
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18675
18889
|
StartDeployWebsite: dedent_default`
|
|
18676
|
-
|
|
18677
|
-
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
NOT for: a screenshot or reference of a site the user wants Wix to generate (the site-creation tools); a site described in words with no code yet (StartHeadlessBuild); a Wix editor site's content or settings (WixSiteBuilder's edit mode, or WixREADME); a Claude Design bundle URL (import-claude-design-from-url); migrating a live site by URL or CSV exports; adding HTML or code into an existing Wix site; uploading images or documents to a site's media (UploadImageToWixSite).
|
|
18890
|
+
Creates a website on Wix and puts it live, or replaces the files of one deployed earlier. You write the site's code, or take the files the user brought (HTML/CSS/JS, a static build, a zip, another builder's export), and this returns the guide to create the site and upload them. Updates keep the same site and URL. Works with or without a Wix login (anonymous deploy with a save link), through the Wix MCP alone or from a shell. Call it right away, without asking the user anything first.
|
|
18891
|
+
When a new site needs Wix business features (${WIX_BUSINESS_FEATURES}), call StartHeadlessBuild instead. When the user brought files and the site needs those features, upload the files here first; the upload's result then points to StartHeadlessBuild to connect them.
|
|
18892
|
+
Returns the guide; follow it and do the work yourself. Upload with UploadHeadlessWebsiteFiles once the site exists; the drop page is only the fallback.
|
|
18893
|
+
NOT for: a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor (WixHarmonyEditorBuild, SearchSiteTemplates); a Claude Design bundle URL (import-claude-design-from-url); migrating a live site by URL or CSV exports; adding HTML or code into an existing Wix editor site; uploading images or documents to a site's media (UploadImageToWixSite).
|
|
18681
18894
|
`,
|
|
18895
|
+
// Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
|
|
18682
18896
|
StartHeadlessBuild: dedent_default`
|
|
18683
|
-
|
|
18684
|
-
Call it right away: it only returns the guide, and the site's
|
|
18685
|
-
NOT for:
|
|
18897
|
+
Returns Wix's headless guide, for a site that needs Wix business features (${WIX_BUSINESS_FEATURES}), run by Wix as a Wix Headless site. You write the site; the guide creates it, connects the features, and uploads it. Also to add those features to an existing Wix Headless site (one deployed from files is one), from a project download link, or to move a Wix site to code.
|
|
18898
|
+
Call it right away: it only returns the guide, and the site's details are not needed first.
|
|
18899
|
+
NOT for: a site that needs no Wix business feature, or files the user brought that are not live yet (StartDeployWebsite: upload them first, then come back here for the features); a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor, even when the site needs business features (WixHarmonyEditorBuild, SearchSiteTemplates); a Claude Design URL (import-claude-design-from-url).
|
|
18686
18900
|
`
|
|
18687
18901
|
};
|
|
18902
|
+
descriptions.SupportAndFeedback = descriptions.ReportIssueToWix;
|
|
18903
|
+
return descriptions;
|
|
18688
18904
|
}
|
|
18689
|
-
var defaultToolDescriptions = buildToolDescriptions(true);
|
|
18905
|
+
var defaultToolDescriptions = buildToolDescriptions(true, true, { usageGuideOnce: true });
|
|
18690
18906
|
var executeCodeModeToolDescriptions = {
|
|
18691
18907
|
ExecuteWixAPI: executeWixApiDescription
|
|
18692
18908
|
};
|
|
18909
|
+
var WIX_SKILLS_README_SECTION = dedent_default`
|
|
18910
|
+
## Wix skills
|
|
18911
|
+
|
|
18912
|
+
Wix publishes agent skills: folders of instructions, scripts and shipped code for building on Wix, each with a \`SKILL.md\` at its root that says what the skill does and which of its files to read when. The source is https://github.com/wix/skills. Three of them matter here:
|
|
18913
|
+
|
|
18914
|
+
- **wix-headless-kit**: builds a Wix Headless site on shipped, verified code. In the Wix plugin. Its contents are below.
|
|
18915
|
+
- **wix-headless-templates**: the code wix-headless-kit builds from, one folder per Wix Business Solution. In the Wix plugin for Codex and Cursor. Its contents are below.
|
|
18916
|
+
- **wix-manage**: REST recipes to configure and manage a site's business solutions. In the Wix plugin. Its \`SKILL.md\` is the index of every recipe by area, with what it does and a link to read it.
|
|
18917
|
+
|
|
18918
|
+
On a site built in code (one deployed through StartDeployWebsite, started through StartHeadlessBuild, or any Wix Headless project), wix-headless-kit carries the work, with wix-headless-templates as its code, and it names the wix-manage recipes it uses as steps.
|
|
18919
|
+
|
|
18920
|
+
### How to read a skill
|
|
18921
|
+
|
|
18922
|
+
A skill is a folder, and every file in it has a path inside that folder (\`SKILL.md\`, \`guides/api-run.md\`). The same path finds the file on disk and online, so the paths below work either way.
|
|
18923
|
+
|
|
18924
|
+
1. **From disk, when it is already there.** This is the preferred way: the files are complete and need no network. The Wix plugin loads its skills into the client; \`npx skills add\` puts them under \`.agents/skills/<skill>/\` in the project, or in the user's home with \`-g\`; an earlier install may have left them wherever the client keeps skills (for example \`.claude/skills/\` or the plugin's folder). Open \`<skill folder>/SKILL.md\` and the files it names. An installed copy can be an older snapshot. A file whose byte size differs from the one in the skill's manifest (below) is out of date, so read it online; a matching size makes a file likely current, not certain, so when a file decides the work, read it online too.
|
|
18925
|
+
2. **Install them, with a shell that reaches the internet.** \`npx skills add wix/skills\` installs every skill; \`npx skills add wix/skills --skill wix-headless-kit\` installs one. Then read them from disk. To keep them across projects, the user can install the Wix plugin instead (in Claude Code: \`/plugin marketplace add wix/skills\`, then \`/plugin install wix@wix\`).
|
|
18926
|
+
3. **Read them online, with ReadFullDocsArticle**, when neither works: no shell, or a sandbox without internet. Call ReadFullDocsArticle with the URL as \`articleUrl\`; it returns the file whole, where a client's own web tool often cannot reach www.wix.com or summarizes what it fetches. The skills registry mirrors every skill from the main branch of wix/skills:
|
|
18927
|
+
- **A file**: \`https://www.wix.com/skills/<skill>/<path>\`, with \`<path>\` its path inside the skill folder. Start with \`<path>\` = \`SKILL.md\`, then read the files it names.
|
|
18928
|
+
- **A folder**: \`https://www.wix.com/skills/<skill>/<folder>\` returns the full URL of every file under it, at any depth. \`https://www.wix.com/skills/<skill>/<folder>/_content\` returns those files' contents in one response, each under a heading with its URL, Markdown first; binary files and lockfiles are left out, and past 150K characters the rest are named by URL, to read one by one.
|
|
18929
|
+
- **The skill's file list**: \`https://www.wix.com/skills/<skill>/\`, with the trailing slash, returns the full URL of every file in the skill; without the slash it returns the SKILL.md.
|
|
18930
|
+
- **Every file in the skill**: \`https://www.wix.com/skills/<skill>.manifest.json\` lists each path with its size, under a \`base\` URL.
|
|
18931
|
+
- **Names and hosts**: \`<skill>\` is the folder name with or without its \`wix-\` prefix (\`wix-headless-kit\` or \`headless-kit\`), and \`dev.wix.com\` works in place of \`www.wix.com\`.
|
|
18932
|
+
- **What comes back**: a Markdown file comes with a short note on top (it is mirrored from the main branch of wix/skills; the skill's manifest, folder listings, \`_content\`, and ReadFullDocsArticle) and its relative links turned into full URLs; code, scripts and JSON come as they are.
|
|
18933
|
+
- **A wrong path**: the answer names the files with that name elsewhere in the skill and the manifest URL, so one more call reaches the right file. ReadFullDocsArticle shows that answer as an error.
|
|
18934
|
+
- **Not served**: a whole skill as one page (\`<skill>-full\`) returns 404; read the skill's files instead.
|
|
18935
|
+
|
|
18936
|
+
The wix-manage recipes are also published in the Wix docs, as articles at \`…/skills/<recipe>\` under each API's section. Find the one that matches the task in the skill's recipe index (\`SKILL.md\`) or with SearchWixRESTDocumentation, and read it with ReadFullDocsArticle. Only wix-manage is in the docs. For example, one recipe in all three places:
|
|
18937
|
+
- on disk: \`<wix-manage folder>/references/sites/upload-static-site.md\`;
|
|
18938
|
+
- in the registry: https://www.wix.com/skills/manage/references/sites/upload-static-site.md;
|
|
18939
|
+
- in the docs: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.
|
|
18940
|
+
|
|
18941
|
+
### wix-manage
|
|
18942
|
+
|
|
18943
|
+
REST recipes for managing a site's business solutions: one recipe per task (installing apps, products, booking services, CMS items, SEO, domains and more), each with the exact endpoints, request bodies and order of steps. Open the recipe for a task before calling its APIs.
|
|
18944
|
+
|
|
18945
|
+
Ways to find and read a recipe, in this order:
|
|
18946
|
+
1. **On disk**: the Wix plugin's wix-manage folder, or \`.agents/skills/wix-manage/\` after \`npx skills add wix/skills --skill wix-manage\`. \`SKILL.md\` lists every recipe by area with what it does; the recipe itself is under \`references/<area>/\` (ecommerce nests one more folder, such as \`references/ecommerce/shipping/\`), so open the path \`SKILL.md\` gives rather than building it. An installed copy can be an older snapshot: when a recipe decides the work, read it online too.
|
|
18947
|
+
2. **Online**: the list of every recipe by area, each a link to its docs article, is https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/skills/wix-management-recipes-index.md. Read it with ReadFullDocsArticle, or with \`curl\` from a shell, then read the recipe for the task the same way. It is public: no sign-in or token.
|
|
18948
|
+
|
|
18949
|
+
Using a recipe: follow its steps, and make its calls with ExecuteWixAPI, passing the recipe's docs URL in \`sourceDocUrls\`. When a recipe's example doesn't fit a call, read that method's schema with SearchWixAPISpec first. When no recipe matches the task, find the method with SearchWixRESTDocumentation or BrowseWixRESTDocsMenu, read it with ReadFullDocsArticle, and call it the same way.
|
|
18950
|
+
Each API area also has a page that lists only its own recipes: the area's docs section with \`/skills.md\` appended. These are the ones today, but it is not a closed list: areas are added, so look a task up in the full list first, and for an API not named here try its section URL with \`/skills.md\` appended.
|
|
18951
|
+
- Accessibility: https://dev.wix.com/docs/api-reference/site/accessibility/skills.md
|
|
18952
|
+
- Analytics: https://dev.wix.com/docs/api-reference/business-management/analytics/skills.md
|
|
18953
|
+
- App Installation: https://dev.wix.com/docs/api-reference/business-management/app-installation/skills.md
|
|
18954
|
+
- App tools: https://dev.wix.com/docs/api-reference/app-management/app-tools/skills.md
|
|
18955
|
+
- Automations: https://dev.wix.com/docs/api-reference/business-management/automations/skills.md
|
|
18956
|
+
- Blog: https://dev.wix.com/docs/api-reference/business-solutions/blog/skills.md
|
|
18957
|
+
- Bookings: https://dev.wix.com/docs/api-reference/business-solutions/bookings/skills.md
|
|
18958
|
+
- Calendar: https://dev.wix.com/docs/api-reference/business-management/calendar/skills.md
|
|
18959
|
+
- CMS: https://dev.wix.com/docs/api-reference/business-solutions/cms/skills.md
|
|
18960
|
+
- Contacts: https://dev.wix.com/docs/api-reference/crm/members-contacts/contacts/skills.md
|
|
18961
|
+
- Domains: https://dev.wix.com/docs/api-reference/account-level/domains/skills.md
|
|
18962
|
+
- eCommerce: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills.md
|
|
18963
|
+
- Events: https://dev.wix.com/docs/api-reference/business-solutions/events/skills.md
|
|
18964
|
+
- Forms: https://dev.wix.com/docs/api-reference/crm/forms/skills.md
|
|
18965
|
+
- Get Paid: https://dev.wix.com/docs/api-reference/business-management/get-paid/skills.md
|
|
18966
|
+
- Google Ads: https://dev.wix.com/docs/api-reference/business-management/marketing/ads/google-ads/skills.md
|
|
18967
|
+
- Google Business Profile: https://dev.wix.com/docs/api-reference/business-management/google-business-profile/skills.md
|
|
18968
|
+
- Google Search Console: https://dev.wix.com/docs/api-reference/business-management/seo/google-search-console/skills.md
|
|
18969
|
+
- Headless: https://dev.wix.com/docs/api-reference/business-management/headless/skills.md
|
|
18970
|
+
- Marketing: https://dev.wix.com/docs/api-reference/business-management/marketing/skills.md
|
|
18971
|
+
- Media: https://dev.wix.com/docs/api-reference/assets/media/skills.md
|
|
18972
|
+
- Pricing Plans: https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/skills.md
|
|
18973
|
+
- Restaurants: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/skills.md
|
|
18974
|
+
- Rich Content: https://dev.wix.com/docs/api-reference/assets/rich-content/skills.md
|
|
18975
|
+
- SEO: https://dev.wix.com/docs/api-reference/business-management/seo/skills.md
|
|
18976
|
+
- SEO redirects: https://dev.wix.com/docs/api-reference/business-management/seo/redirects/skills.md
|
|
18977
|
+
- Site Properties: https://dev.wix.com/docs/api-reference/business-management/site-properties/skills.md
|
|
18978
|
+
- Sites: https://dev.wix.com/docs/api-reference/account-level/sites/skills.md
|
|
18979
|
+
- Stores: https://dev.wix.com/docs/api-reference/business-solutions/stores/skills.md
|
|
18980
|
+
- Wix MCP: https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/skills.md
|
|
18981
|
+
|
|
18982
|
+
### wix-headless-kit
|
|
18983
|
+
|
|
18984
|
+
Builds a Wix Headless site on shipped, verified code instead of writing the integration: each Wix Business Solution comes with a typed data layer, hooks, components, pages and a seed script that are already correct. The data layer ships twice, on \`@wix/sdk\` for the React core and as plain REST calls for a stack that cannot run it (\`guides/reference-mode.md\`), and the agent's work narrows to brand, layout, copy and wiring. It runs as a CLI flow from a shell that reaches Wix, and as Wix API calls through this MCP when it cannot.
|
|
18985
|
+
|
|
18986
|
+
Paths are inside the skill folder; online, each is \`https://www.wix.com/skills/headless-kit/<path>\`.
|
|
18987
|
+
- Before the skill: the cold start installs it, signs in and hands off. It is another skill's file, https://www.wix.com/skills/headless-cold-start/headless-kit.md, and StartHeadlessBuild returns it.
|
|
18988
|
+
- \`SKILL.md\`: the steps of a build and the table of Business Solutions the kit ships.
|
|
18989
|
+
- \`guides/api-run.md\`: the whole run as Wix API calls through this MCP, for an agent whose shell cannot reach Wix or that has no shell.
|
|
18990
|
+
- \`guides/capabilities.md\`: which Wix product owns which feature, and what the kit ships for it.
|
|
18991
|
+
- \`guides/existing-site.md\`: a new frontend for a Wix site that already has its content.
|
|
18992
|
+
- \`guides/migration.md\`: a site moving to code after "Go Headless" in its dashboard.
|
|
18993
|
+
- \`guides/reference-mode.md\`: a stack that cannot run the shipped React code (a static site, a server-rendered app in another language).
|
|
18994
|
+
- \`guides/feedback.md\`: sending the user's building-with-Wix feedback to Wix.
|
|
18995
|
+
- \`install/\`: the bootstrap, setup, deploy and templates scripts, and \`install/pins.json\` with the pinned CLI and templates versions.
|
|
18996
|
+
|
|
18997
|
+
### wix-headless-templates
|
|
18998
|
+
|
|
18999
|
+
The code wix-headless-kit builds from, one folder per Wix Business Solution: \`storefront\`, \`bookings\`, \`rentals\`, \`blog\`, \`cms\`, \`forms\`, \`members\`, \`events\`, \`restaurants\`, \`donations\`, \`pricing-plans\`, \`portfolio\`, \`faq\`. It holds no flow of its own; wix-headless-kit fetches it and copies, deploys and seeds from it.
|
|
19000
|
+
|
|
19001
|
+
Paths are inside the skill folder; online, each is \`https://www.wix.com/skills/headless-templates/<path>\`. Every Business Solution folder has the same parts, with \`<solution>\` one of the names above:
|
|
19002
|
+
- \`<solution>/INSTRUCTIONS.md\`: the playbook, covering what ships, what you build, and each page's contract.
|
|
19003
|
+
- \`<solution>/app/\`: the typed React core: the data layer under \`wix/<solution>/\`, hooks and components.
|
|
19004
|
+
- \`<solution>/app-astro/\`: the Astro page skeletons for Wix-managed Astro sites (every solution but \`forms\`).
|
|
19005
|
+
- \`<solution>/project/\`: the whole runnable project for that solution.
|
|
19006
|
+
- \`<solution>/rest/\`: the same data layer as plain REST calls, for stacks that cannot run the React core.
|
|
19007
|
+
- \`<solution>/seed/SEED.md\`: how to seed that solution's content, with the script that creates it in the right order.
|
|
19008
|
+
|
|
19009
|
+
Shared by every solution:
|
|
19010
|
+
- \`shared/DESIGN.md\`: the design floor when the brief gives no design direction.
|
|
19011
|
+
- \`shared/CONTENT.md\`: the copy floor for every page written.
|
|
19012
|
+
- \`shared/SUPPLIED-CONTENT.md\`: content the user brings (a CSV, a menu, a folder of photos) as the seed plan.
|
|
19013
|
+
- \`shared/CUSTOM_OPERATIONS.md\`: operations beyond what the shipped code covers.
|
|
19014
|
+
- \`shared/rest/client.ts\`: the REST client the \`rest/\` layers use.
|
|
19015
|
+
|
|
19016
|
+
Online, a solution's folder lists everything it ships: https://www.wix.com/skills/headless-templates/storefront returns the URLs of the store's files, and \`…/storefront/app\` those of its React core. A part is often small enough to read whole in one call: https://www.wix.com/skills/headless-templates/storefront/seed/_content returns the store's seeding guide and scripts together. A whole solution passes the 150K cap, so read it part by part.
|
|
19017
|
+
`;
|
|
18693
19018
|
var recipesIndexDoc = {
|
|
18694
19019
|
url: "https://dev.wix.com/docs/api-reference/articles/ai-tools/wix-mcp/skills/wix-management-recipes-index",
|
|
18695
19020
|
action: "include"
|
|
18696
19021
|
};
|
|
18697
19022
|
var README_INTRO = dedent_default`
|
|
18698
|
-
|
|
19023
|
+
# How to work with Wix through these tools
|
|
18699
19024
|
|
|
18700
|
-
|
|
19025
|
+
## Source of truth
|
|
18701
19026
|
|
|
18702
|
-
|
|
18703
|
-
Do not answer from memory about anything Wix-specific — this covers every part of your answer, not only API calls: any URL, link, ID, setting location, or product fact you give the user must come from the docs tools, the recipes below, a previous tool result in this conversation, or the user's own message (identifiers and URLs the user supplies are fine to use as given). If a recipe in the index below matches what the user asked for, read it before answering. For APIs, use the search, browse, read-docs, and schema tools as the source of truth for the exact URL, HTTP method, request body shape, response shape, field names, required fields, and enum values before calling an API.
|
|
18704
|
-
</source-of-truth>
|
|
19027
|
+
Do not answer from memory about anything Wix-specific — this covers every part of your answer, not only API calls: any URL, link, ID, setting location, or product fact you give the user must come from the docs tools, a Wix skill (installed, or read online as the Wix skills section below explains), a previous tool result in this conversation, or the user's own message (identifiers and URLs the user supplies are fine to use as given). When a Wix skill covers what the user asked for, read it before answering. For APIs, use the search, browse, read-docs, and schema tools as the source of truth for the exact URL, HTTP method, request body shape, response shape, field names, required fields, and enum values before calling an API.
|
|
18705
19028
|
|
|
18706
|
-
|
|
18707
|
-
<goal>
|
|
19029
|
+
## Goal
|
|
18708
19030
|
`;
|
|
18709
|
-
var README_FLOWS_HEADING =
|
|
18710
|
-
|
|
19031
|
+
var README_FLOWS_HEADING = "## Wix MCP Flows";
|
|
19032
|
+
var README_FLOWS_SITE = dedent_default`
|
|
19033
|
+
### The site
|
|
18711
19034
|
|
|
18712
|
-
|
|
19035
|
+
The site context below lists the account's sites with their installed apps. Take the \`siteId\` from there; when more than one site fits and the user's words don't pick one, ask which before any read or write. A business solution's API (Stores, Bookings, CMS and the rest) works only on a site with that app installed, and some apps have versions with different endpoints (Wix Stores V1 vs V3): check the site's apps first, and when the app is missing, install it (a Wix skill has the steps) before using its API.
|
|
18713
19036
|
`;
|
|
18714
|
-
var
|
|
18715
|
-
|
|
19037
|
+
var README_FIND_SOURCES = dedent_default`
|
|
19038
|
+
### Find the method
|
|
18716
19039
|
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
|
|
18720
|
-
|
|
18721
|
-
- SearchWixRESTDocumentation or BrowseWixRESTDocsMenu → ReadFullDocsArticle (no usable example) → SearchWixAPISpec for request schemas → ExecuteWixAPI
|
|
19040
|
+
A task may need several API methods. For each, take the first source that gives it:
|
|
19041
|
+
- A previous tool result in this conversation (not memory) already shows its request: go straight to the call.
|
|
19042
|
+
- A Wix skill that covers the task (the Wix skills section below says what each is for): follow it, from disk or online. When its example for a call matches your case, that is the source: pass its docs URL in \`sourceDocUrls\` instead of searching for the method pages (when it has no docs URL, one SearchWixRESTDocumentation call on its title finds the article).
|
|
19043
|
+
- The docs: SearchWixRESTDocumentation (semantic search) or BrowseWixRESTDocsMenu (menu hierarchy), then ReadFullDocsArticle on the method's article for its code examples. For SDK code, the CLI, a headless site or an app, start from those docs instead: SearchWixSDKDocumentation, SearchWixCLIDocumentation, SearchWixHeadlessDocumentation, SearchBuildAppsDocumentation.
|
|
18722
19044
|
|
|
18723
|
-
|
|
19045
|
+
Keep each method's docs URL even when no source gives a usable example: the shape comes from its schema, as the next section says.
|
|
19046
|
+
`;
|
|
19047
|
+
var README_FLOWS_BODY = dedent_default`
|
|
19048
|
+
### The exact shape
|
|
18724
19049
|
|
|
18725
|
-
|
|
18726
|
-
- SearchWixRESTDocumentation — semantic search over the Wix REST docs by keywords or natural-language queries.
|
|
18727
|
-
- BrowseWixRESTDocsMenu — navigate the Wix REST docs by menu hierarchy; alternative to keyword search.
|
|
18728
|
-
- ReadFullDocsArticle — fetch a full docs article by its docs URL; returns prose, fields, and code examples.
|
|
18729
|
-
- SearchWixAPISpec — JS-sandboxed query over the REST API spec; returns request schema, response schema, and the executable \`method.publicUrl\`.
|
|
18730
|
-
- ExecuteWixAPI — runs an async JS function in a sandbox with \`wix.request()\`; a single invocation can chain multiple API calls, paginate, loop, transform, and branch.
|
|
19050
|
+
A code example is the model for your call only when it matches your case, with every field your case needs. Fill the rest from the method's schema: SearchWixAPISpec on its docs URL (REST or SDK) returns the request and response schemas, \`method.publicUrl\` and \`method.sdk\`; ReadFullDocsMethodSchema when SearchWixAPISpec isn't available. A query's filter and sort fields and operators are a closed list per method: use only what its schema declares.
|
|
18731
19051
|
|
|
18732
|
-
|
|
19052
|
+
None of this comes from memory.
|
|
18733
19053
|
|
|
18734
|
-
|
|
18735
|
-
a. First try the WixREADME Recipes index below (pre-curated guides for common tasks). If a recipe matches, ReadFullDocsArticle on its docs URL to find relevant code examples; many recipes cover multi-step flows and supply code examples for several methods in one article.
|
|
18736
|
-
b. If no recipe matches the user's intent, or the matched recipe didn't yield the code examples you need, discover each missing method's docs URL via SearchWixRESTDocumentation (semantic search) or BrowseWixRESTDocsMenu (menu hierarchy), then ReadFullDocsArticle on each to find relevant code examples.
|
|
18737
|
-
c. If neither path turns up a usable code example for some method, continue without one for that method — step 2 will derive its request directly from the schema. Make sure you've still captured the docs URL of each method.
|
|
19054
|
+
### The call, or the code that makes it
|
|
18738
19055
|
|
|
18739
|
-
|
|
18740
|
-
- **Request** (URL, HTTP method, body fields) — skip the probe if a code example already shows them; otherwise SearchWixAPISpec returns publicUrl + exact request shape.
|
|
18741
|
-
- **Response shape** (field paths your code will read) — only relevant if your code reads response fields; skip the probe if the article(s) already made it clear; otherwise probe SearchWixAPISpec for the response schema.
|
|
18742
|
-
- **Filter and sort** (query/search methods only) — an example proves only the fields and operators it uses. Each method declares which fields it can filter and sort by, and with which operators; that list is closed for fields and their operators and covers that method only — \`$and\`/\`$or\`/\`$not\` are filter syntax and never appear in it. Anything else errors or silently returns the wrong rows. A listed operator is valid on its own, since a field takes a single operator; to combine conditions, use the logical operators instead — \`$and\` and \`$or\` take an array of expressions, \`$not\` takes one, and they can nest.
|
|
18743
|
-
- Never guess any of this from memory.
|
|
19056
|
+
**Call it with ExecuteWixAPI**: one async JS function with \`wix.request()\`, holding every step of the task that code can resolve. The reads that fetch an ID, check whether a record exists or list items to choose from go in the same function as the writes they feed; step numbers in a guide or skill give logical order, not separate calls. Set \`hasMutations: true\` when any step writes. Return the write's response as is and report from it; don't read again to confirm. A new invocation only for what code can't resolve: the user's answer, or a job that finishes later. One function chains calls, paginates, lists all, loops, transforms and branches. Without ExecuteWixAPI, make each call with CallWixSiteAPI (a site's APIs) or ManageWixSite (the account's), from the same exact shape.
|
|
18744
19057
|
|
|
18745
|
-
|
|
19058
|
+
**Write the code** when the task is code the user keeps: an app (built and run with the Wix CLI), a headless site, or a script that calls the REST API or the JavaScript SDK. The path is the same: find the method, learn its exact shape, then write the call from \`method.publicUrl\` (REST) or \`method.sdk\` (the JavaScript SDK), never from memory. Every way of building against Wix in code is this path; the headless skills are a shortcut on it, not a different one. wix-headless-kit builds a Wix Headless site from shipped, verified code (wix-headless-templates holds it, one folder per business solution: a \`@wix/sdk\` React core, and the same data layer as plain REST calls for a stack that cannot run it): scaffold with the CLI, deploy, seed, design the presentation on the shipped code, release. What no shipped solution covers is written from the docs, as above. A new site is written in code and uploaded: StartDeployWebsite returns the upload guide (create the site, upload its files, change them later); StartHeadlessBuild returns the headless guide, for a site that needs Wix business features: you write the site, and the guide creates it, connects the features and uploads it, through the cold start that hands off to the kit (or the same run as API calls when there is no shell).
|
|
18746
19059
|
|
|
18747
|
-
|
|
19060
|
+
### When a call fails
|
|
18748
19061
|
|
|
18749
|
-
|
|
18750
|
-
Beyond managing user sites, the Wix MCP tools can be used for any other purpose they support — for example, reading Wix SDK docs to write code against the SDK, using the Wix CLI to create apps, answering documentation questions, and so on.
|
|
18751
|
-
</other-uses>
|
|
18752
|
-
</wix-usage-guide>
|
|
19062
|
+
The error carries the failed call's status and message and a Wix request id. Read it: it says what was wrong, and that is what to fix before calling again, whether the request (from the schema), the site and what is installed on it, or what the account is allowed to do. Never resend the same request unchanged, and never guess a fix. When you report the failure to the user, include the request id.
|
|
18753
19063
|
`;
|
|
18754
|
-
|
|
18755
|
-
|
|
18756
|
-
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
18763
|
-
|
|
18764
|
-
|
|
18765
|
-
|
|
18766
|
-
|
|
19064
|
+
function buildReadmeDocs({
|
|
19065
|
+
recipesIndex = true
|
|
19066
|
+
} = {}) {
|
|
19067
|
+
return [
|
|
19068
|
+
{
|
|
19069
|
+
action: "text",
|
|
19070
|
+
content: [
|
|
19071
|
+
README_INTRO,
|
|
19072
|
+
"",
|
|
19073
|
+
AGENT_GOAL,
|
|
19074
|
+
"",
|
|
19075
|
+
README_FLOWS_HEADING,
|
|
19076
|
+
"",
|
|
19077
|
+
README_FLOWS_SITE,
|
|
19078
|
+
"",
|
|
19079
|
+
README_FIND_SOURCES,
|
|
19080
|
+
"",
|
|
19081
|
+
README_FLOWS_BODY
|
|
19082
|
+
].join("\n")
|
|
19083
|
+
},
|
|
19084
|
+
{ action: "text", content: WIX_SKILLS_README_SECTION },
|
|
19085
|
+
...recipesIndex ? [recipesIndexDoc] : []
|
|
19086
|
+
];
|
|
19087
|
+
}
|
|
19088
|
+
var defaultReadmeDocs = buildReadmeDocs();
|
|
18767
19089
|
function mergeConfig(base, overrides) {
|
|
18768
19090
|
return {
|
|
18769
19091
|
toolDescriptions: {
|
|
@@ -18802,7 +19124,7 @@ var defaultConfig = {
|
|
|
18802
19124
|
var widgetSiteCreationConfig = {
|
|
18803
19125
|
toolDescriptions: Object.fromEntries(
|
|
18804
19126
|
[
|
|
18805
|
-
"
|
|
19127
|
+
"WixHarmonyEditorBuild",
|
|
18806
19128
|
"WixSiteCreation",
|
|
18807
19129
|
"SearchSiteTemplates",
|
|
18808
19130
|
"CreateSiteFromTemplate"
|
|
@@ -18949,6 +19271,9 @@ function renderApps(apps, knownApps) {
|
|
|
18949
19271
|
if (app.catalogVersion) {
|
|
18950
19272
|
line += ` \u2014 Catalog app version: **${app.catalogVersion}**`;
|
|
18951
19273
|
}
|
|
19274
|
+
if (app.invoicesVersion) {
|
|
19275
|
+
line += ` \u2014 Invoices app version: **${app.invoicesVersion}**`;
|
|
19276
|
+
}
|
|
18952
19277
|
lines.push(line);
|
|
18953
19278
|
}
|
|
18954
19279
|
return lines.join("\n");
|
|
@@ -18969,6 +19294,11 @@ function renderCustomAppsWithSkillsTools(apps) {
|
|
|
18969
19294
|
}
|
|
18970
19295
|
return lines.join("\n");
|
|
18971
19296
|
}
|
|
19297
|
+
function renderCompanionApp(installedApps) {
|
|
19298
|
+
const appId = installedApps.find((app) => app.companionApp === true)?.appId;
|
|
19299
|
+
if (!appId || !GUID_PATTERN.test(appId)) return "";
|
|
19300
|
+
return `The site's code lives in its companion app (ID: \`${appId}\`).`;
|
|
19301
|
+
}
|
|
18972
19302
|
function renderProperties(props) {
|
|
18973
19303
|
const sections = [];
|
|
18974
19304
|
const regionalFormat = [];
|
|
@@ -18995,7 +19325,7 @@ ${contact.join("\n")}`);
|
|
|
18995
19325
|
}
|
|
18996
19326
|
return sections.join("\n\n");
|
|
18997
19327
|
}
|
|
18998
|
-
function renderSite(site, index, knownApps) {
|
|
19328
|
+
function renderSite(site, index, knownApps, includeCompanionApp) {
|
|
18999
19329
|
const lines = [];
|
|
19000
19330
|
const heading = site.displayName ?? `Site ${index}`;
|
|
19001
19331
|
lines.push(`## ${index}. ${heading}`);
|
|
@@ -19040,6 +19370,13 @@ function renderSite(site, index, knownApps) {
|
|
|
19040
19370
|
lines.push("");
|
|
19041
19371
|
lines.push(customAppsWithSkillsToolsContent);
|
|
19042
19372
|
}
|
|
19373
|
+
const companionAppContent = includeCompanionApp ? renderCompanionApp(site.installedApps ?? []) : "";
|
|
19374
|
+
if (companionAppContent) {
|
|
19375
|
+
lines.push("");
|
|
19376
|
+
lines.push("### Site code (companion app)");
|
|
19377
|
+
lines.push("");
|
|
19378
|
+
lines.push(companionAppContent);
|
|
19379
|
+
}
|
|
19043
19380
|
lines.push("");
|
|
19044
19381
|
lines.push("---");
|
|
19045
19382
|
lines.push("");
|
|
@@ -19059,6 +19396,7 @@ function enrichDynamicContext(data, knownApps = {}, options = {}) {
|
|
|
19059
19396
|
const response = snakeToCamel(parsed);
|
|
19060
19397
|
const includePartnerContext = options.includePartnerContext ?? false;
|
|
19061
19398
|
const includeAccountContext = options.includeAccountContext ?? false;
|
|
19399
|
+
const includeCompanionApp = options.includeCompanionApp ?? false;
|
|
19062
19400
|
const sections = [];
|
|
19063
19401
|
const allSites = response.sites ?? [];
|
|
19064
19402
|
const maxSites = options.maxSites ?? Infinity;
|
|
@@ -19111,9 +19449,18 @@ function enrichDynamicContext(data, knownApps = {}, options = {}) {
|
|
|
19111
19449
|
);
|
|
19112
19450
|
sections.push("");
|
|
19113
19451
|
}
|
|
19452
|
+
const hasInvoicesVersion = sitesToRender.some(
|
|
19453
|
+
(site) => site.installedApps?.some((app) => app.appId && knownApps[app.appId] && app.invoicesVersion)
|
|
19454
|
+
);
|
|
19455
|
+
if (hasInvoicesVersion) {
|
|
19456
|
+
sections.push(
|
|
19457
|
+
"> **Wix Invoices \u2014 Version**: Some sites below have Wix Invoices installed and show their Invoices version (V1/V2). When working on an Invoices task, check the version for that site \u2014 only read recipes and call endpoints that match its version. Never use an Invoices V2 skill or API on a V1 site, and vice versa."
|
|
19458
|
+
);
|
|
19459
|
+
sections.push("");
|
|
19460
|
+
}
|
|
19114
19461
|
if (sitesToRender.length) {
|
|
19115
19462
|
for (let i = 0; i < sitesToRender.length; i++) {
|
|
19116
|
-
sections.push(renderSite(sitesToRender[i], i + 1, knownApps));
|
|
19463
|
+
sections.push(renderSite(sitesToRender[i], i + 1, knownApps, includeCompanionApp));
|
|
19117
19464
|
}
|
|
19118
19465
|
}
|
|
19119
19466
|
if (!response.account && !siteCount && (!includePartnerContext || !hasPartnerContext(response.partnerContext))) {
|
|
@@ -19121,9 +19468,11 @@ function enrichDynamicContext(data, knownApps = {}, options = {}) {
|
|
|
19121
19468
|
}
|
|
19122
19469
|
return sections.join("\n");
|
|
19123
19470
|
}
|
|
19471
|
+
var GUID_PATTERN;
|
|
19124
19472
|
var init_enrich = __esm2({
|
|
19125
19473
|
"src/enrich.ts"() {
|
|
19126
19474
|
"use strict";
|
|
19475
|
+
GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19127
19476
|
}
|
|
19128
19477
|
});
|
|
19129
19478
|
init_enrich();
|
|
@@ -19186,6 +19535,7 @@ async function getDynamicContext(auth, httpClient, filter, options) {
|
|
|
19186
19535
|
} : options;
|
|
19187
19536
|
const includePartnerContext = contextOptions?.partnerContextEnabled ?? true;
|
|
19188
19537
|
const includeAccountContext = contextOptions?.includeAccountContext ?? true;
|
|
19538
|
+
const includeCompanionApp = contextOptions?.includeCompanionApp ?? false;
|
|
19189
19539
|
try {
|
|
19190
19540
|
const accountHeaders = await auth.getAccountAuthHeaders();
|
|
19191
19541
|
const noFilter = !filter?.siteId && !filter?.siteName;
|
|
@@ -19239,6 +19589,7 @@ async function getDynamicContext(auth, httpClient, filter, options) {
|
|
|
19239
19589
|
const context = enrichDynamicContext2(response.data, known_apps_default, {
|
|
19240
19590
|
includePartnerContext,
|
|
19241
19591
|
includeAccountContext,
|
|
19592
|
+
includeCompanionApp,
|
|
19242
19593
|
// Cap the account-wide view to the first N sites; a targeted fetch returns one site.
|
|
19243
19594
|
...noFilter ? { maxSites: MAX_SITES_FOR_DYNAMIC_CONTEXT, totalSiteCount } : {}
|
|
19244
19595
|
});
|
|
@@ -19292,6 +19643,21 @@ function isApiUrlAllowed(url) {
|
|
|
19292
19643
|
}
|
|
19293
19644
|
|
|
19294
19645
|
// src/config/param-descriptions.ts
|
|
19646
|
+
var REPORT_ISSUE_TO_WIX_PARAMS = {
|
|
19647
|
+
message: [
|
|
19648
|
+
"The report of one issue, sent to Wix as free text. Only this issue \u2014 no session summary, play-by-play or unrelated problems:",
|
|
19649
|
+
"- What was being attempted, in a sentence.",
|
|
19650
|
+
"- The tool or endpoint, the HTTP status and the error message.",
|
|
19651
|
+
"- What you expected instead, and the workaround if any.",
|
|
19652
|
+
"- Where the fault likely lives (API behavior / API schema / docs / Wix tooling / unsure), and whether you confirmed it.",
|
|
19653
|
+
"- The context needed to reproduce it: agent/model, site id, the Wix area or API.",
|
|
19654
|
+
"- The user's own words, verbatim, if they added any.",
|
|
19655
|
+
"NEVER include secrets \u2014 no bearer tokens, API keys, or credentials \u2014 and no personal data beyond what the report needs."
|
|
19656
|
+
].join("\n"),
|
|
19657
|
+
requestId: "Optional request id (x-wix-request-id) of the failed Wix API call this report is about."
|
|
19658
|
+
};
|
|
19659
|
+
var WHY_NOT_EXECUTE_RECONNECT = "if the Wix MCP doesn't offer it at all, say so here, and tell the user once that their Wix connection may be an old or cached version that a reconnect refreshes.";
|
|
19660
|
+
var WHY_NOT_EXECUTE_NO_EXECUTE = "if it isn't in your tool list, say so.";
|
|
19295
19661
|
var paramDescriptions = {
|
|
19296
19662
|
CallWixSiteAPI: {
|
|
19297
19663
|
siteId: "The id of the site selected using site selection tool",
|
|
@@ -19308,6 +19674,7 @@ var paramDescriptions = {
|
|
|
19308
19674
|
WQL_FILTER_RULE
|
|
19309
19675
|
].join("\n"),
|
|
19310
19676
|
reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
|
|
19677
|
+
whyNotExecuteWixAPI: `One sentence: why this call goes through CallWixSiteAPI and not ExecuteWixAPI, the preferred tool. If ExecuteWixAPI isn't loaded, load it through your client's tool search before calling this tool; ${WHY_NOT_EXECUTE_RECONNECT}`,
|
|
19311
19678
|
sourceDocUrl: [
|
|
19312
19679
|
"The URL of the documentation or recipe where you found this API endpoint.",
|
|
19313
19680
|
"MAKE SURE THE ENDPOINT URL IS REALLY THERE AND YOUR ARE NOT GUESSING IT !!!",
|
|
@@ -19343,18 +19710,9 @@ var paramDescriptions = {
|
|
|
19343
19710
|
'Use ["other"] ONLY IF YOU HAVE A VERY GOOD REASON TO DO SO'
|
|
19344
19711
|
].join("\n")
|
|
19345
19712
|
},
|
|
19346
|
-
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
"1. Provenance \u2014 labelled lines (omit ones that do not apply): agent/model, Wix tooling used, Wix areas/APIs touched, Wix products (stores, bookings, ...), site id, relevant links (dashboard, docs pages read, live site) and other ids created this run.",
|
|
19350
|
-
`2. Narrative \u2014 "In the user's words": anything the user added when you offered, quoted verbatim (redact secrets only). Then user intent + run summary (what they set out to build, what worked vs. what fought back), a condensed play-by-play of the session (key exchanges, tool calls, decisions), and the friction points \u2014 every place something got in the way, each with specifics: the step/endpoint, HTTP status and error message, the gap, the workaround, and what you expected instead. Include issues you recovered from.`,
|
|
19351
|
-
'3. Attribution \u2014 for each significant friction point, your best diagnosis of where the fault lives: API behavior / API schema / API reference docs / docs articles or examples / Wix tooling (MCP tools, auth flow) / other-unsure \u2014 plus whether you confirmed it or are assuming. Do not force a tag; "unsure" beats a wrong route.',
|
|
19352
|
-
"End with a bottom line: one or two sentences naming the single most important problem and its impact \u2014 a concise engineering assessment, not softened or hedged.",
|
|
19353
|
-
"Write it as a scannable few paragraphs or a short bulleted list \u2014 thorough on the friction, distilled rather than a transcript dump.",
|
|
19354
|
-
"NEVER include secrets \u2014 no bearer tokens, API keys, or credentials \u2014 and no personal data beyond what the feedback needs."
|
|
19355
|
-
].join("\n"),
|
|
19356
|
-
requestId: "Optional request id (x-wix-request-id) returned from a failed Wix API call this feedback relates to."
|
|
19357
|
-
},
|
|
19713
|
+
ReportIssueToWix: REPORT_ISSUE_TO_WIX_PARAMS,
|
|
19714
|
+
// The tool's former name, kept for importers that read it by that key.
|
|
19715
|
+
SupportAndFeedback: REPORT_ISSUE_TO_WIX_PARAMS,
|
|
19358
19716
|
SearchWixWDSDocumentation: {
|
|
19359
19717
|
searchTerm: "The search term to search for in the Wix Design System Documentation",
|
|
19360
19718
|
maxResults: "The maximum number of results to return, default is 10, max is 15"
|
|
@@ -19393,8 +19751,13 @@ var paramDescriptions = {
|
|
|
19393
19751
|
maxResults: "The maximum number of results to return, default is 10, max is 15",
|
|
19394
19752
|
reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
|
|
19395
19753
|
},
|
|
19754
|
+
SearchWixHelpCenter: {
|
|
19755
|
+
searchTerm: 'What the site owner wants to do or fix, in plain words (e.g. "connect a domain bought elsewhere", "cancel a Premium plan")',
|
|
19756
|
+
maxResults: "The maximum number of articles to return (1\u201315, default 15). Each article is returned with up to 60 lines.",
|
|
19757
|
+
reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
|
|
19758
|
+
},
|
|
19396
19759
|
ReadFullDocsArticle: {
|
|
19397
|
-
articleUrl: "The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../... For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include the query param ?apiView=SDK (e.g. https://dev.wix.com/docs/...?apiView=SDK). Wix skills pages (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...) and Wix Headless agent pages (https://www.wix.com/headless/..., e.g. https://www.wix.com/headless/skill.md) are read as-is, including their referenced files."
|
|
19760
|
+
articleUrl: "The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../... For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include the query param ?apiView=SDK (e.g. https://dev.wix.com/docs/...?apiView=SDK). Wix Help Center articles (https://support.wix.com/en/article/...) are read as-is. Wix skills pages (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...) and Wix Headless agent pages (https://www.wix.com/headless/..., e.g. https://www.wix.com/headless/skill.md) are read as-is, including their referenced files. Source links (https://dev.wix.com/rawdocs/api/get-source-content?repo=...&path=...) found in skills and recipes are read as-is."
|
|
19398
19761
|
},
|
|
19399
19762
|
ReadFullDocsMethodSchema: {
|
|
19400
19763
|
articleUrl: "The URL of the documentation to fetch. Should be something like https://dev.wix.com/docs/.../... For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include the query param ?apiView=SDK (e.g. https://dev.wix.com/docs/...?apiView=SDK).",
|
|
@@ -19410,7 +19773,7 @@ var paramDescriptions = {
|
|
|
19410
19773
|
},
|
|
19411
19774
|
UploadImageToWixSite: {
|
|
19412
19775
|
siteId: "The ID (UUID) of the Wix site to upload to. REQUIRED. If the user has not clearly specified which site, ASK them which site to upload to (or use ListWixSites to help them pick) BEFORE calling this tool. NEVER call this tool with a missing, empty, placeholder, or guessed siteId \u2014 ask first instead.",
|
|
19413
|
-
image: "
|
|
19776
|
+
image: `${fileParamNote("send the image as imageBase64 (the whole file, one per call) or as a public URL in imageUrls")} For plain image URLs the user pasted, use imageUrls instead. ALWAYS pass ALL files together in a single call \u2014 NEVER call this tool once per image. Do NOT use together with imageBase64.`,
|
|
19414
19777
|
imageUrls: "Array of publicly accessible image URLs (e.g. links the user pasted or URLs produced by other tools). Use this for plain URLs that are NOT chat attachments \u2014 no file_id is needed. ALWAYS pass ALL URLs together in a single call. Can be combined with image; do NOT use together with imageBase64.",
|
|
19415
19778
|
imageBase64: 'Base64-encoded image data. Use when the client can read a file and encode it as base64. May include a data URL prefix (e.g. "data:image/jpeg;base64,...") or be raw base64. Do NOT use together with image.',
|
|
19416
19779
|
mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
|
|
@@ -19447,10 +19810,315 @@ var paramDescriptions = {
|
|
|
19447
19810
|
};
|
|
19448
19811
|
var desc = (toolName) => paramDescriptions[toolName];
|
|
19449
19812
|
|
|
19813
|
+
// src/docs/mintlify-docs.ts
|
|
19814
|
+
var MINTLIFY_LINKS_DOCS_SITE = "mintlify-links";
|
|
19815
|
+
var MINTLIFY_DOCS_SITE = "mintlify";
|
|
19816
|
+
var MINTLIFY_HOST = "https://wix-internal.mintlify.site";
|
|
19817
|
+
var MINTLIFY_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
19818
|
+
"wix-internal.mintlify.site",
|
|
19819
|
+
"wix-internal.mintlify.app"
|
|
19820
|
+
]);
|
|
19821
|
+
var DEV_WIX_DOCS = "https://dev.wix.com/docs";
|
|
19822
|
+
var PORTAL_SLUG_TO_TAB = {
|
|
19823
|
+
rest: "api-reference",
|
|
19824
|
+
"mintlify-sandbox": "sandbox"
|
|
19825
|
+
};
|
|
19826
|
+
var TAB_TO_PORTAL_SLUG = {
|
|
19827
|
+
sandbox: "mintlify-sandbox"
|
|
19828
|
+
};
|
|
19829
|
+
var MINTLIFY_TABS = /* @__PURE__ */ new Set(["api-reference", "sandbox", "kb"]);
|
|
19830
|
+
var NON_DOCS_SLUGS = /* @__PURE__ */ new Set(["skills", "kb-only", "api"]);
|
|
19831
|
+
var CHECK_TTL_MS = 10 * 60 * 1e3;
|
|
19832
|
+
var MAX_CHECK_ENTRIES = 5e3;
|
|
19833
|
+
var CHECK_TIMEOUT_MS = 5e3;
|
|
19834
|
+
var MAX_REDIRECTS = 3;
|
|
19835
|
+
var CHECK_CONCURRENCY = 8;
|
|
19836
|
+
function isMintlifyDocsSite(docsSite) {
|
|
19837
|
+
return docsSite === MINTLIFY_LINKS_DOCS_SITE || docsSite === MINTLIFY_DOCS_SITE;
|
|
19838
|
+
}
|
|
19839
|
+
var MINTLIFY_DOCUMENT_TYPES = {
|
|
19840
|
+
REST: ["MINTLIFY_REST"],
|
|
19841
|
+
// includes the skills, as REST + SKILLS does today
|
|
19842
|
+
SDK: ["MINTLIFY_SDK"]
|
|
19843
|
+
};
|
|
19844
|
+
function mintlifyDocumentTypes(docsSite, toolName) {
|
|
19845
|
+
return docsSite === MINTLIFY_DOCS_SITE ? MINTLIFY_DOCUMENT_TYPES[toolName] : void 0;
|
|
19846
|
+
}
|
|
19847
|
+
function isMintlifyUrl(url) {
|
|
19848
|
+
try {
|
|
19849
|
+
return MINTLIFY_HOSTNAMES.has(new URL(url).hostname);
|
|
19850
|
+
} catch {
|
|
19851
|
+
return false;
|
|
19852
|
+
}
|
|
19853
|
+
}
|
|
19854
|
+
var MINTLIFY_URL_PREFIXES = [...MINTLIFY_HOSTNAMES].map(
|
|
19855
|
+
(host) => `https://${host}/`
|
|
19856
|
+
);
|
|
19857
|
+
function cleanPath(pathname) {
|
|
19858
|
+
const path = pathname.replace(/\.md$/, "").replace(/\/+$/, "");
|
|
19859
|
+
return path === "" ? "/" : path;
|
|
19860
|
+
}
|
|
19861
|
+
function flavourOf(url) {
|
|
19862
|
+
return url.searchParams.get("apiView")?.toUpperCase() === "SDK" ? "sdk" : "rest";
|
|
19863
|
+
}
|
|
19864
|
+
function toMintlifyCandidate(rawUrl) {
|
|
19865
|
+
let url;
|
|
19866
|
+
try {
|
|
19867
|
+
url = new URL(rawUrl);
|
|
19868
|
+
} catch {
|
|
19869
|
+
return null;
|
|
19870
|
+
}
|
|
19871
|
+
if (MINTLIFY_HOSTNAMES.has(url.hostname)) {
|
|
19872
|
+
const path2 = cleanPath(url.pathname);
|
|
19873
|
+
return { url: `${MINTLIFY_HOST}${path2}`, path: path2, flavour: flavourOf(url) };
|
|
19874
|
+
}
|
|
19875
|
+
if (url.hostname !== "dev.wix.com" || !url.pathname.startsWith("/docs/")) {
|
|
19876
|
+
return null;
|
|
19877
|
+
}
|
|
19878
|
+
const segments = cleanPath(url.pathname.slice("/docs".length)).split("/").filter(Boolean);
|
|
19879
|
+
if (segments.length === 0 || NON_DOCS_SLUGS.has(segments[0])) {
|
|
19880
|
+
return null;
|
|
19881
|
+
}
|
|
19882
|
+
segments[0] = PORTAL_SLUG_TO_TAB[segments[0]] ?? segments[0];
|
|
19883
|
+
const path = `/${segments.join("/")}`;
|
|
19884
|
+
return { url: `${MINTLIFY_HOST}${path}`, path, flavour: flavourOf(url) };
|
|
19885
|
+
}
|
|
19886
|
+
function toDevWixUrl(rawUrl) {
|
|
19887
|
+
const url = new URL(rawUrl);
|
|
19888
|
+
const segments = cleanPath(url.pathname).split("/").filter(Boolean);
|
|
19889
|
+
if (segments.length > 0) {
|
|
19890
|
+
segments[0] = TAB_TO_PORTAL_SLUG[segments[0]] ?? segments[0];
|
|
19891
|
+
}
|
|
19892
|
+
const query = flavourOf(url) === "sdk" ? "?apiView=SDK" : "";
|
|
19893
|
+
return `${DEV_WIX_DOCS}/${segments.join("/")}${query}`;
|
|
19894
|
+
}
|
|
19895
|
+
var checkCache = /* @__PURE__ */ new Map();
|
|
19896
|
+
function rememberCheck(path, result) {
|
|
19897
|
+
const now = Date.now();
|
|
19898
|
+
checkCache.delete(path);
|
|
19899
|
+
if (checkCache.size >= MAX_CHECK_ENTRIES) {
|
|
19900
|
+
for (const [key, entry] of checkCache) {
|
|
19901
|
+
if (entry.expires <= now) checkCache.delete(key);
|
|
19902
|
+
}
|
|
19903
|
+
}
|
|
19904
|
+
while (checkCache.size >= MAX_CHECK_ENTRIES) {
|
|
19905
|
+
const oldest = checkCache.keys().next().value;
|
|
19906
|
+
if (oldest === void 0) break;
|
|
19907
|
+
checkCache.delete(oldest);
|
|
19908
|
+
}
|
|
19909
|
+
checkCache.set(path, { result, expires: now + CHECK_TTL_MS });
|
|
19910
|
+
}
|
|
19911
|
+
async function headOnce(url) {
|
|
19912
|
+
return fetch(url, {
|
|
19913
|
+
method: "HEAD",
|
|
19914
|
+
redirect: "manual",
|
|
19915
|
+
signal: AbortSignal.timeout(CHECK_TIMEOUT_MS)
|
|
19916
|
+
});
|
|
19917
|
+
}
|
|
19918
|
+
async function checkMintlifyPage(path) {
|
|
19919
|
+
const cached = checkCache.get(path);
|
|
19920
|
+
if (cached && cached.expires > Date.now()) {
|
|
19921
|
+
return cached.result;
|
|
19922
|
+
}
|
|
19923
|
+
if (cached) checkCache.delete(path);
|
|
19924
|
+
let result;
|
|
19925
|
+
try {
|
|
19926
|
+
let current = path;
|
|
19927
|
+
let hops = 0;
|
|
19928
|
+
for (; ; ) {
|
|
19929
|
+
const response = await headOnce(`${MINTLIFY_HOST}${current}`);
|
|
19930
|
+
if (response.status === 200) {
|
|
19931
|
+
result = {
|
|
19932
|
+
outcome: hops === 0 ? "identity" : "redirect",
|
|
19933
|
+
path: current
|
|
19934
|
+
};
|
|
19935
|
+
break;
|
|
19936
|
+
}
|
|
19937
|
+
const location = response.headers.get("location");
|
|
19938
|
+
if (response.status >= 300 && response.status < 400 && location && hops < MAX_REDIRECTS) {
|
|
19939
|
+
current = cleanPath(new URL(location, MINTLIFY_HOST).pathname);
|
|
19940
|
+
hops++;
|
|
19941
|
+
continue;
|
|
19942
|
+
}
|
|
19943
|
+
result = { outcome: response.status === 404 ? "missing" : "error" };
|
|
19944
|
+
break;
|
|
19945
|
+
}
|
|
19946
|
+
} catch (error) {
|
|
19947
|
+
logger.error(`[mintlify] page check failed for ${path}: ${error}`);
|
|
19948
|
+
result = { outcome: "error" };
|
|
19949
|
+
}
|
|
19950
|
+
rememberCheck(path, result);
|
|
19951
|
+
return result;
|
|
19952
|
+
}
|
|
19953
|
+
async function resolveDocsLink(rawUrl, { verify = true } = {}) {
|
|
19954
|
+
const candidate = toMintlifyCandidate(rawUrl);
|
|
19955
|
+
if (!candidate) {
|
|
19956
|
+
return { outcome: "not-docs", url: rawUrl };
|
|
19957
|
+
}
|
|
19958
|
+
const tab = candidate.path.split("/")[1];
|
|
19959
|
+
if (!MINTLIFY_TABS.has(tab)) {
|
|
19960
|
+
return { outcome: "other-portal", url: rawUrl };
|
|
19961
|
+
}
|
|
19962
|
+
const query = candidate.flavour === "sdk" ? "?apiView=SDK" : "";
|
|
19963
|
+
if (!verify) {
|
|
19964
|
+
return { outcome: "identity", url: `${candidate.url}${query}` };
|
|
19965
|
+
}
|
|
19966
|
+
const check = await checkMintlifyPage(candidate.path);
|
|
19967
|
+
if (check.path) {
|
|
19968
|
+
return {
|
|
19969
|
+
outcome: check.outcome,
|
|
19970
|
+
url: `${MINTLIFY_HOST}${check.path}${query}`
|
|
19971
|
+
};
|
|
19972
|
+
}
|
|
19973
|
+
return { outcome: check.outcome, url: rawUrl };
|
|
19974
|
+
}
|
|
19975
|
+
var DOCS_LINK_PATTERN = /https:\/\/(?:dev\.wix\.com\/docs|wix-internal\.mintlify\.(?:site|app))\/[^\s)\]"'<>`]*/g;
|
|
19976
|
+
function trimTrailingPunctuation(url) {
|
|
19977
|
+
return url.replace(/[.,;:!?*]+$/, "");
|
|
19978
|
+
}
|
|
19979
|
+
var MINTLIFY_LINK_PATTERN = /https:\/\/wix-internal\.mintlify\.(?:site|app)\/[^\s)\]"'<>`]*/g;
|
|
19980
|
+
function toDevWixHostText(text) {
|
|
19981
|
+
return text.replace(MINTLIFY_LINK_PATTERN, (match) => {
|
|
19982
|
+
const link = trimTrailingPunctuation(match);
|
|
19983
|
+
let url;
|
|
19984
|
+
try {
|
|
19985
|
+
url = new URL(link);
|
|
19986
|
+
} catch {
|
|
19987
|
+
return match;
|
|
19988
|
+
}
|
|
19989
|
+
if (!MINTLIFY_TABS.has(url.pathname.split("/")[1] ?? "")) return match;
|
|
19990
|
+
return toDevWixUrl(link) + url.hash + match.slice(link.length);
|
|
19991
|
+
});
|
|
19992
|
+
}
|
|
19993
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
19994
|
+
const results = new Array(items.length);
|
|
19995
|
+
let next = 0;
|
|
19996
|
+
const workers = Array.from(
|
|
19997
|
+
{ length: Math.min(limit, items.length) },
|
|
19998
|
+
async () => {
|
|
19999
|
+
while (next < items.length) {
|
|
20000
|
+
const index = next++;
|
|
20001
|
+
results[index] = await fn(items[index]);
|
|
20002
|
+
}
|
|
20003
|
+
}
|
|
20004
|
+
);
|
|
20005
|
+
await Promise.all(workers);
|
|
20006
|
+
return results;
|
|
20007
|
+
}
|
|
20008
|
+
async function rewriteDocsLinks(text, { verify = true, label = "text" } = {}) {
|
|
20009
|
+
const found = [
|
|
20010
|
+
...new Set(
|
|
20011
|
+
(text.match(DOCS_LINK_PATTERN) ?? []).map(trimTrailingPunctuation)
|
|
20012
|
+
)
|
|
20013
|
+
];
|
|
20014
|
+
const stats = {
|
|
20015
|
+
links: found.length,
|
|
20016
|
+
byOutcome: {},
|
|
20017
|
+
misses: []
|
|
20018
|
+
};
|
|
20019
|
+
if (found.length === 0) {
|
|
20020
|
+
return { text, stats };
|
|
20021
|
+
}
|
|
20022
|
+
const resolved = await mapWithConcurrency(
|
|
20023
|
+
found,
|
|
20024
|
+
CHECK_CONCURRENCY,
|
|
20025
|
+
(url) => resolveDocsLink(url, { verify })
|
|
20026
|
+
);
|
|
20027
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
20028
|
+
found.forEach((url, i) => {
|
|
20029
|
+
const { outcome, url: target } = resolved[i];
|
|
20030
|
+
stats.byOutcome[outcome] = (stats.byOutcome[outcome] ?? 0) + 1;
|
|
20031
|
+
if (outcome === "identity" || outcome === "redirect") {
|
|
20032
|
+
if (target !== url) replacements.set(url, target);
|
|
20033
|
+
} else if (outcome !== "not-docs") {
|
|
20034
|
+
stats.misses.push(`${outcome} ${url}`);
|
|
20035
|
+
}
|
|
20036
|
+
});
|
|
20037
|
+
const rewritten = text.replace(DOCS_LINK_PATTERN, (match) => {
|
|
20038
|
+
const url = trimTrailingPunctuation(match);
|
|
20039
|
+
const target = replacements.get(url);
|
|
20040
|
+
return target ? target + match.slice(url.length) : match;
|
|
20041
|
+
});
|
|
20042
|
+
logger.log(
|
|
20043
|
+
`[mintlify] ${label}: ${stats.links} links ${JSON.stringify(stats.byOutcome)}` + (stats.misses.length ? ` misses: ${stats.misses.slice(0, 10).join(" | ")}` : "")
|
|
20044
|
+
);
|
|
20045
|
+
return { text: rewritten, stats };
|
|
20046
|
+
}
|
|
20047
|
+
var INDEX_BANNER = /^> ## Documentation Index\n(?:>.*\n)*\n?/;
|
|
20048
|
+
var INLINED_SNIPPET = /^export const \w+ = [\s\S]*?^};\n/gm;
|
|
20049
|
+
var REST_TAB = /<Tab title="REST">([\s\S]*?)<\/Tab>/;
|
|
20050
|
+
var SDK_TAB = /<Tab title="SDK">([\s\S]*?)<\/Tab>/;
|
|
20051
|
+
function cleanMintlifyMarkdown(markdown, flavour) {
|
|
20052
|
+
const text = markdown.replace(INLINED_SNIPPET, "");
|
|
20053
|
+
const rest = text.match(REST_TAB);
|
|
20054
|
+
const sdk = text.match(SDK_TAB);
|
|
20055
|
+
if (!rest || !sdk || (text.match(/<Tab title=/g) ?? []).length !== 2) {
|
|
20056
|
+
return text;
|
|
20057
|
+
}
|
|
20058
|
+
const start = text.lastIndexOf("<Tabs>", rest.index);
|
|
20059
|
+
const end = text.indexOf("</Tabs>", sdk.index);
|
|
20060
|
+
if (start === -1 || end === -1) {
|
|
20061
|
+
return text;
|
|
20062
|
+
}
|
|
20063
|
+
const kept = (flavour === "sdk" ? sdk : rest)[1];
|
|
20064
|
+
const note = flavour === "sdk" ? "_This is the SDK view. Drop `?apiView=SDK` from the URL for the REST view._" : "_This is the REST view. Add `?apiView=SDK` to the URL for the JavaScript SDK view._";
|
|
20065
|
+
return `${text.slice(0, start)}${note}
|
|
20066
|
+
|
|
20067
|
+
${kept.trim()}
|
|
20068
|
+
${text.slice(end + "</Tabs>".length)}`;
|
|
20069
|
+
}
|
|
20070
|
+
var DOCS_URL_IN_TEXT = /https:\/\/dev\.wix\.com\/docs\/(?=api-reference\b|\.\.\.)/g;
|
|
20071
|
+
function toMintlifyHostText(text) {
|
|
20072
|
+
return text.replace(DOCS_URL_IN_TEXT, `${MINTLIFY_HOST}/`);
|
|
20073
|
+
}
|
|
20074
|
+
var SITE_RELATIVE_LINK = /(\]\(|href=["'])\/(?!\/)/g;
|
|
20075
|
+
function absolutizeSiteLinks(markdown) {
|
|
20076
|
+
return markdown.replace(
|
|
20077
|
+
SITE_RELATIVE_LINK,
|
|
20078
|
+
(_, open) => `${open}${MINTLIFY_HOST}/`
|
|
20079
|
+
);
|
|
20080
|
+
}
|
|
20081
|
+
async function readMintlifyMarkdown(rawUrl) {
|
|
20082
|
+
const resolved = await resolveDocsLink(rawUrl);
|
|
20083
|
+
if (resolved.outcome !== "identity" && resolved.outcome !== "redirect") {
|
|
20084
|
+
return null;
|
|
20085
|
+
}
|
|
20086
|
+
const pageUrl = new URL(resolved.url);
|
|
20087
|
+
const response = await fetch(`${MINTLIFY_HOST}${pageUrl.pathname}.md`, {
|
|
20088
|
+
headers: { accept: "text/markdown" },
|
|
20089
|
+
redirect: "follow",
|
|
20090
|
+
signal: AbortSignal.timeout(15e3)
|
|
20091
|
+
});
|
|
20092
|
+
if (response.status === 404) {
|
|
20093
|
+
return null;
|
|
20094
|
+
}
|
|
20095
|
+
if (!response.ok) {
|
|
20096
|
+
throw new Error(
|
|
20097
|
+
`Mintlify returned ${response.status} for ${pageUrl.pathname}.md`
|
|
20098
|
+
);
|
|
20099
|
+
}
|
|
20100
|
+
const flavour = toMintlifyCandidate(rawUrl)?.flavour ?? "rest";
|
|
20101
|
+
const cleaned = cleanMintlifyMarkdown(
|
|
20102
|
+
(await response.text()).replace(INDEX_BANNER, ""),
|
|
20103
|
+
flavour
|
|
20104
|
+
);
|
|
20105
|
+
const { text: rewritten } = await rewriteDocsLinks(cleaned, {
|
|
20106
|
+
label: `read ${pageUrl.pathname}`
|
|
20107
|
+
});
|
|
20108
|
+
const markdown = absolutizeSiteLinks(rewritten);
|
|
20109
|
+
logger.log(
|
|
20110
|
+
`[mintlify] read ${pageUrl.pathname}.md (${markdown.length} chars) for ${rawUrl}`
|
|
20111
|
+
);
|
|
20112
|
+
return { url: `${MINTLIFY_HOST}${pageUrl.pathname}`, markdown };
|
|
20113
|
+
}
|
|
20114
|
+
|
|
19450
20115
|
// src/docs/get-to-know.ts
|
|
20116
|
+
var AX_SKILLS_REPO = "wix-private/ax-skills";
|
|
20117
|
+
var AX_SKILLS_INDEX_PATH = "skills/wix-manage/SKILL.md";
|
|
19451
20118
|
function addGetSiteContextTool({
|
|
19452
20119
|
server,
|
|
19453
20120
|
authStrategy,
|
|
20121
|
+
includeCompanionApp = false,
|
|
19454
20122
|
toolDescriptions
|
|
19455
20123
|
}) {
|
|
19456
20124
|
const d = desc("GetSiteContext");
|
|
@@ -19480,7 +20148,8 @@ function addGetSiteContextTool({
|
|
|
19480
20148
|
try {
|
|
19481
20149
|
context = await getDynamicContext(authStrategy, httpClient, filter, {
|
|
19482
20150
|
partnerContextEnabled: false,
|
|
19483
|
-
includeAccountContext: false
|
|
20151
|
+
includeAccountContext: false,
|
|
20152
|
+
includeCompanionApp
|
|
19484
20153
|
});
|
|
19485
20154
|
} catch (err) {
|
|
19486
20155
|
rethrowIfUnauthorized(err);
|
|
@@ -19516,15 +20185,17 @@ async function addGetToKnowTools({
|
|
|
19516
20185
|
dynamicContextEnabled,
|
|
19517
20186
|
repo,
|
|
19518
20187
|
commitHash,
|
|
19519
|
-
skillsTags
|
|
20188
|
+
skillsTags,
|
|
20189
|
+
readmeRecipesIndexEnabled = true,
|
|
20190
|
+
docsSite
|
|
19520
20191
|
}) {
|
|
19521
20192
|
if (disableTools.includes("WixREADME")) {
|
|
19522
20193
|
return;
|
|
19523
20194
|
}
|
|
19524
20195
|
const baseDocs = readmeDocs ?? defaultReadmeDocs;
|
|
19525
|
-
const
|
|
19526
|
-
|
|
19527
|
-
);
|
|
20196
|
+
const isRecipesIndex = (doc) => doc.url?.replace(/\.md$/, "") === RECIPES_INDEX_URL;
|
|
20197
|
+
const baseDocsWithoutRecipes = baseDocs.filter((doc) => !isRecipesIndex(doc));
|
|
20198
|
+
const recipesIndexDocs = readmeRecipesIndexEnabled ? baseDocs.filter(isRecipesIndex) : [];
|
|
19528
20199
|
server.tool(
|
|
19529
20200
|
"WixREADME",
|
|
19530
20201
|
toolDescriptions?.["WixREADME"] ?? defaultToolDescriptions.WixREADME,
|
|
@@ -19596,6 +20267,18 @@ ${responseText}` : responseText;
|
|
|
19596
20267
|
if (!url) {
|
|
19597
20268
|
throw new Error('URL is required for "include" action');
|
|
19598
20269
|
}
|
|
20270
|
+
const readsAxSkills = isRecipesIndex({ action, url }) && !skillsPortalUrl && (repo ? repo === AX_SKILLS_REPO : !commitHash);
|
|
20271
|
+
if (readsAxSkills) {
|
|
20272
|
+
return {
|
|
20273
|
+
type: "text",
|
|
20274
|
+
text: await fetchSourceContent(
|
|
20275
|
+
httpClient,
|
|
20276
|
+
AX_SKILLS_REPO,
|
|
20277
|
+
AX_SKILLS_INDEX_PATH,
|
|
20278
|
+
repo === AX_SKILLS_REPO ? commitHash : void 0
|
|
20279
|
+
)
|
|
20280
|
+
};
|
|
20281
|
+
}
|
|
19599
20282
|
const fetchedContent = await fetchArticleContent(
|
|
19600
20283
|
httpClient,
|
|
19601
20284
|
url,
|
|
@@ -19638,9 +20321,9 @@ Failed to fetch content: ${errorMsg}`
|
|
|
19638
20321
|
}).catch((err) => {
|
|
19639
20322
|
console.error("[WixREADME] Failed to fetch skills by portal:", err);
|
|
19640
20323
|
return null;
|
|
19641
|
-
}) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
|
|
20324
|
+
}) : readmeRecipesIndexEnabled ? fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
|
|
19642
20325
|
(doc) => doc?.text ?? null
|
|
19643
|
-
);
|
|
20326
|
+
) : Promise.resolve(null);
|
|
19644
20327
|
const filter = {};
|
|
19645
20328
|
if (siteId) filter.siteId = siteId;
|
|
19646
20329
|
else if (siteName) filter.siteName = siteName;
|
|
@@ -19660,7 +20343,7 @@ Failed to fetch content: ${errorMsg}`
|
|
|
19660
20343
|
const recipesIntro = dedent_default`
|
|
19661
20344
|
Reusable how-to guides for common ${isVisitorFlow ? "Wix site visitor" : "Wix management"} tasks, grouped by product area. Each recipe is a Wix docs article that bundles the right endpoints, request shapes, field names, and step ordering for one real-world use case.
|
|
19662
20345
|
|
|
19663
|
-
|
|
20346
|
+
Open the recipe that matches the task with \`ReadFullDocsArticle\` and follow it. When its example for a call matches your case (the same variant and shape), call from it; when it doesn't, or leaves out a field your case needs, read the method's schema with \`SearchWixAPISpec\` first. On query and search methods an example proves only the fields and operators it uses; the method's own declared list covers the rest. When no recipe fits, find the method with \`SearchWixRESTDocumentation\` or \`BrowseWixRESTDocsMenu\`, then \`SearchWixAPISpec\` for its schema.
|
|
19664
20347
|
`;
|
|
19665
20348
|
const recipesContents = skillsContent !== null ? [
|
|
19666
20349
|
await fetchDoc({
|
|
@@ -19670,9 +20353,7 @@ Failed to fetch content: ${errorMsg}`
|
|
|
19670
20353
|
|
|
19671
20354
|
${skillsContent}`
|
|
19672
20355
|
})
|
|
19673
|
-
] : await Promise.all(
|
|
19674
|
-
baseDocs.filter((doc) => doc.url === RECIPES_INDEX_URL).map(fetchDoc)
|
|
19675
|
-
);
|
|
20356
|
+
] : await Promise.all(recipesIndexDocs.map(fetchDoc));
|
|
19676
20357
|
const recipeNotes = !skillsContent?.includes("Dashboard Navigation") ? [] : [
|
|
19677
20358
|
await fetchDoc({
|
|
19678
20359
|
action: "text",
|
|
@@ -19690,7 +20371,8 @@ ${skillsContent}`
|
|
|
19690
20371
|
).map((item) => item.text),
|
|
19691
20372
|
...dynamicContext ? [dynamicContext] : []
|
|
19692
20373
|
];
|
|
19693
|
-
const
|
|
20374
|
+
const joined = parts.join("\n\n");
|
|
20375
|
+
const text = isMintlifyDocsSite(docsSite) ? (await rewriteDocsLinks(joined, { label: "readme WixREADME" })).text : joined;
|
|
19694
20376
|
return {
|
|
19695
20377
|
content: [{ type: "text", text }]
|
|
19696
20378
|
};
|
|
@@ -19699,13 +20381,14 @@ ${skillsContent}`
|
|
|
19699
20381
|
}
|
|
19700
20382
|
|
|
19701
20383
|
// src/docs/start-deploy-website.ts
|
|
20384
|
+
var NO_PERSONAL_DATA = "No personal data: no names, emails, phones, passwords, keys or local paths.";
|
|
19702
20385
|
var START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS = {
|
|
19703
|
-
userRequest:
|
|
19704
|
-
siteDescription:
|
|
19705
|
-
siteForm:
|
|
19706
|
-
siteFunctionality:
|
|
19707
|
-
agentIdentity:
|
|
19708
|
-
wixAccess:
|
|
20386
|
+
userRequest: `A short summary of what the user asked for and the context that led here, in a sentence or two, including whether this is a new site or an edit or new version of a site already on Wix. ${NO_PERSONAL_DATA}`,
|
|
20387
|
+
siteDescription: `What the site is: its purpose and who it is for. One or two sentences. ${NO_PERSONAL_DATA}`,
|
|
20388
|
+
siteForm: `The form the site exists in right now (HTML/CSS/JS files, a static build, a zip, a framework project, another builder's export, a design created in this conversation, or something else), roughly how many files, and whether it includes images. For a framework project: which framework, whether you have its built static output (for example dist/) or only the source, and whether it needs a server at runtime (SSR, API routes, a backend), or that you don't know. Also where it came from: built in this conversation, the user's own project or repo, another builder (which one), or a Wix skill or template (which one). ${NO_PERSONAL_DATA}`,
|
|
20389
|
+
siteFunctionality: `What the site must be able to do for its visitors beyond showing pages, if anything (for example selling, bookings or reservations, tickets, menus and orders, rentals, forms, memberships or paid plans, donations, a blog, or anything else the user expects to work), in your own words. If it is a plain static site with no such needs, say so. ${NO_PERSONAL_DATA}`,
|
|
20390
|
+
agentIdentity: `Which agent or product you are, which model if you know it, and the harness you run in. ${NO_PERSONAL_DATA}`,
|
|
20391
|
+
wixAccess: `How you can reach Wix from where you run: through this MCP only, or also from a shell whose network can reach wixapis.com; whether you can run the Wix CLI (npx @wix/cli) there and whether it is logged in; which Wix identity you act with \u2014 the user's login through this MCP, a CLI login, or none; and which Wix skills you have (for example wix-headless-kit or wix-manage), by name and where they come from (installed on disk, a plugin, read online), or none. Name the kind of identity, not the account or its email. ${NO_PERSONAL_DATA}`
|
|
19709
20392
|
};
|
|
19710
20393
|
var START_DEPLOY_WEBSITE_PARAMS = Object.keys(
|
|
19711
20394
|
START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS
|
|
@@ -19713,10 +20396,11 @@ var START_DEPLOY_WEBSITE_PARAMS = Object.keys(
|
|
|
19713
20396
|
var START_DEPLOY_WEBSITE_TOOL_NAME = "StartDeployWebsite";
|
|
19714
20397
|
var START_DEPLOY_WEBSITE_RECIPE_URL = "https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md";
|
|
19715
20398
|
var DROP_PAGE_URL = "https://www.wix.com/headless/drop?utm_campaign=mcp";
|
|
20399
|
+
var WRITE_THE_CODE_LINE = "When the user brought no files, write the site's code yourself, then upload it as the guide shows.";
|
|
19716
20400
|
var FALLBACK_LINE = `Only if you cannot run it (you can't reach the file bytes, or a step fails and you can't fix it): send the user to ${DROP_PAGE_URL} \u2014 give that URL exactly as written, keeping the utm_campaign=mcp parameter.`;
|
|
19717
20401
|
function startDeployWebsiteResponse(recipe) {
|
|
19718
20402
|
return [
|
|
19719
|
-
`The following is the content of ${START_DEPLOY_WEBSITE_RECIPE_URL}. Follow it end to end and do the publishing yourself \u2014 do not hand the user instructions
|
|
20403
|
+
`The following is the content of ${START_DEPLOY_WEBSITE_RECIPE_URL}. Follow it end to end and do the publishing yourself \u2014 do not hand the user instructions. ${WRITE_THE_CODE_LINE}`,
|
|
19720
20404
|
FALLBACK_LINE,
|
|
19721
20405
|
"---",
|
|
19722
20406
|
recipe.trim()
|
|
@@ -19724,7 +20408,7 @@ function startDeployWebsiteResponse(recipe) {
|
|
|
19724
20408
|
}
|
|
19725
20409
|
var START_DEPLOY_WEBSITE_POINTER_RESPONSE = [
|
|
19726
20410
|
`Read and follow this recipe end to end: ${START_DEPLOY_WEBSITE_RECIPE_URL}`,
|
|
19727
|
-
|
|
20411
|
+
`It covers creating the site and uploading the files (with or without a Wix login), updating a site deployed this way, and implementing a backend on it (stores, bookings, CMS, members) as a Wix Headless project. Do the publishing yourself \u2014 do not hand the user instructions. ${WRITE_THE_CODE_LINE}`,
|
|
19728
20412
|
FALLBACK_LINE
|
|
19729
20413
|
].join("\n\n");
|
|
19730
20414
|
function addStartDeployWebsiteTool(server, disableTools = [], toolDescriptions, fetchOptions = {}) {
|
|
@@ -19772,36 +20456,21 @@ function addStartDeployWebsiteTool(server, disableTools = [], toolDescriptions,
|
|
|
19772
20456
|
// src/docs/start-headless-build.ts
|
|
19773
20457
|
var START_HEADLESS_BUILD_TOOL_NAME = "StartHeadlessBuild";
|
|
19774
20458
|
var HEADLESS_GUIDE_URL = "https://www.wix.com/skills/headless-cold-start/headless-kit.md";
|
|
19775
|
-
var START_HEADLESS_BUILD_STARTING_POINTS = [
|
|
19776
|
-
"nothing yet",
|
|
19777
|
-
"a design worked out in this conversation",
|
|
19778
|
-
"an existing Wix Headless site",
|
|
19779
|
-
"a project download link",
|
|
19780
|
-
"a Wix site to move to code"
|
|
19781
|
-
];
|
|
19782
20459
|
var START_HEADLESS_BUILD_PARAM_DESCRIPTIONS = {
|
|
19783
20460
|
userRequest: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.userRequest,
|
|
19784
20461
|
siteDescription: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteDescription,
|
|
19785
|
-
startingPoint:
|
|
20462
|
+
startingPoint: `What the site starts from, in your own words: a new site; an existing Wix Headless site (a site deployed from files is one), with its metaSiteId if you have it; a project download link (a download.zip from Wix); a Wix site moving to code; or something else. ${NO_PERSONAL_DATA}`,
|
|
19786
20463
|
siteFunctionality: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteFunctionality,
|
|
19787
20464
|
agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
|
|
19788
20465
|
wixAccess: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.wixAccess
|
|
19789
20466
|
};
|
|
19790
|
-
var
|
|
19791
|
-
var
|
|
19792
|
-
function startHeadlessBuildResponse(guide
|
|
19793
|
-
const lines = [];
|
|
19794
|
-
if (startingPoint === "a Wix site to move to code") {
|
|
19795
|
-
lines.push(MOVE_TO_CODE_LINE);
|
|
19796
|
-
}
|
|
20467
|
+
var BUILT_HERE = "The site is built here, in code: don't offer Wix's editor or a template instead.";
|
|
20468
|
+
var PREFIX2 = `The following is Wix's headless guide. Follow it end to end and do the work yourself: you write the site, and the guide creates it, connects its Wix business features, and uploads it. ${BUILT_HERE}`;
|
|
20469
|
+
function startHeadlessBuildResponse(guide) {
|
|
19797
20470
|
if (guide && guide.trim()) {
|
|
19798
|
-
|
|
19799
|
-
} else {
|
|
19800
|
-
lines.push(
|
|
19801
|
-
`Wix's headless guide could not be loaded here. It is at ${HEADLESS_GUIDE_URL}: read it whole with ReadFullDocsArticle if you have that tool, otherwise fetch it with curl or your web tool, then follow it end to end and do the work yourself. If you find the user already has the site's code, call StartDeployWebsite instead.`
|
|
19802
|
-
);
|
|
20471
|
+
return [PREFIX2, "---", guide.trim()].join("\n\n");
|
|
19803
20472
|
}
|
|
19804
|
-
return
|
|
20473
|
+
return `Wix's headless guide could not be loaded here. It is at ${HEADLESS_GUIDE_URL}: read it whole with ReadFullDocsArticle if you have that tool, otherwise fetch it with curl or your web tool, then follow it end to end and do the work yourself. ${BUILT_HERE}`;
|
|
19805
20474
|
}
|
|
19806
20475
|
function addStartHeadlessBuildTool(server, disableTools = [], toolDescriptions, fetchOptions = {}) {
|
|
19807
20476
|
if (disableTools.includes(START_HEADLESS_BUILD_TOOL_NAME)) {
|
|
@@ -19814,13 +20483,13 @@ function addStartHeadlessBuildTool(server, disableTools = [], toolDescriptions,
|
|
|
19814
20483
|
{
|
|
19815
20484
|
userRequest: external_exports.string().min(1).describe(d.userRequest),
|
|
19816
20485
|
siteDescription: external_exports.string().min(1).describe(d.siteDescription),
|
|
19817
|
-
startingPoint: external_exports.
|
|
20486
|
+
startingPoint: external_exports.string().optional().describe(d.startingPoint),
|
|
19818
20487
|
siteFunctionality: external_exports.string().min(1).describe(d.siteFunctionality),
|
|
19819
20488
|
agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
|
|
19820
20489
|
wixAccess: external_exports.string().min(1).describe(d.wixAccess)
|
|
19821
20490
|
},
|
|
19822
20491
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
19823
|
-
async (
|
|
20492
|
+
async (_args, { httpClient }) => {
|
|
19824
20493
|
let guide;
|
|
19825
20494
|
try {
|
|
19826
20495
|
guide = await fetchArticleContent(
|
|
@@ -19842,7 +20511,7 @@ function addStartHeadlessBuildTool(server, disableTools = [], toolDescriptions,
|
|
|
19842
20511
|
content: [
|
|
19843
20512
|
{
|
|
19844
20513
|
type: "text",
|
|
19845
|
-
text: startHeadlessBuildResponse(guide
|
|
20514
|
+
text: startHeadlessBuildResponse(guide)
|
|
19846
20515
|
}
|
|
19847
20516
|
]
|
|
19848
20517
|
};
|
|
@@ -19878,9 +20547,16 @@ function shouldTryMenuFallbackForDocsRead(error, articleUrl) {
|
|
|
19878
20547
|
if (!message.includes("404") || !isMenuFallbackUrl) {
|
|
19879
20548
|
return false;
|
|
19880
20549
|
}
|
|
19881
|
-
return !
|
|
19882
|
-
|
|
19883
|
-
|
|
20550
|
+
return !isKnownArticleError(error);
|
|
20551
|
+
}
|
|
20552
|
+
function isKnownArticleError(error) {
|
|
20553
|
+
const message = getErrorMessage(error);
|
|
20554
|
+
return [
|
|
20555
|
+
"This resource is deprecated",
|
|
20556
|
+
"This method is deprecated",
|
|
20557
|
+
"This article is deprecated",
|
|
20558
|
+
"No schema found"
|
|
20559
|
+
].some((knownArticleError) => message.includes(knownArticleError));
|
|
19884
20560
|
}
|
|
19885
20561
|
function formatDocsReadError(toolAction, error) {
|
|
19886
20562
|
return `Could not fetch ${toolAction}: "${getErrorMessage(error)}".`;
|
|
@@ -19893,7 +20569,8 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19893
20569
|
"WIX_HEADLESS",
|
|
19894
20570
|
"BUSINESS_SOLUTIONS",
|
|
19895
20571
|
"CLI",
|
|
19896
|
-
"OVERVIEW"
|
|
20572
|
+
"OVERVIEW",
|
|
20573
|
+
"HELP_CENTER"
|
|
19897
20574
|
], options = {}) => {
|
|
19898
20575
|
const {
|
|
19899
20576
|
getToKnowWixEnabled = false,
|
|
@@ -19909,14 +20586,47 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19909
20586
|
skillsPr,
|
|
19910
20587
|
commitHash,
|
|
19911
20588
|
skillsTags,
|
|
20589
|
+
readmeRecipesIndexEnabled,
|
|
19912
20590
|
dynamicContextEnabled,
|
|
20591
|
+
includeCompanionApp,
|
|
19913
20592
|
usePlatformizedSearch = true,
|
|
19914
|
-
usePlatformizedBrowse = true
|
|
20593
|
+
usePlatformizedBrowse = true,
|
|
20594
|
+
docsSite
|
|
19915
20595
|
} = options;
|
|
19916
20596
|
const effectiveRepo = repo ?? skillsRepo;
|
|
19917
20597
|
const effectiveCommitHash = commitHash ?? skillsPr;
|
|
19918
|
-
const
|
|
20598
|
+
const mintlifyDocs = isMintlifyDocsSite(docsSite);
|
|
20599
|
+
const runSearch = mintlifyDocs ? async (params) => {
|
|
20600
|
+
const documentTypes = mintlifyDocumentTypes(docsSite, params.toolName);
|
|
20601
|
+
const result = await runSemanticSearchAndFormat(
|
|
20602
|
+
documentTypes ? { ...params, documentTypes } : params
|
|
20603
|
+
);
|
|
20604
|
+
return (await rewriteDocsLinks(result, { label: `search ${params.toolName}` })).text;
|
|
20605
|
+
} : runSemanticSearchAndFormat;
|
|
20606
|
+
const docsUrlPrefixes = mintlifyDocs ? ["https://dev.wix.com/docs/", ...MINTLIFY_URL_PREFIXES] : ["https://dev.wix.com/docs/"];
|
|
20607
|
+
const toBackendUrl = (url) => mintlifyDocs && isMintlifyUrl(url) ? toDevWixUrl(url) : url;
|
|
20608
|
+
const sourceNote = (source) => mintlifyDocs ? [{ type: "text", text: `[docs source] ${source}` }] : [];
|
|
20609
|
+
const docsText = (text) => mintlifyDocs ? toMintlifyHostText(text) : text;
|
|
20610
|
+
const desc2 = (toolName) => {
|
|
20611
|
+
const descriptions = desc(toolName);
|
|
20612
|
+
if (!mintlifyDocs) return descriptions;
|
|
20613
|
+
return Object.fromEntries(
|
|
20614
|
+
Object.entries(descriptions).map(([param, text]) => [
|
|
20615
|
+
param,
|
|
20616
|
+
docsText(text)
|
|
20617
|
+
])
|
|
20618
|
+
);
|
|
20619
|
+
};
|
|
20620
|
+
const getDescription = (toolName, defaultDesc) => docsText(toolDescriptions?.[toolName] ?? defaultDesc);
|
|
19919
20621
|
const fetchMenuTextForUrl = async (httpClient, url) => {
|
|
20622
|
+
const text = await fetchMenuTextForBackendUrl(
|
|
20623
|
+
httpClient,
|
|
20624
|
+
toBackendUrl(url)
|
|
20625
|
+
);
|
|
20626
|
+
if (!text || !mintlifyDocs) return text;
|
|
20627
|
+
return (await rewriteDocsLinks(text, { verify: false, label: "menu" })).text;
|
|
20628
|
+
};
|
|
20629
|
+
const fetchMenuTextForBackendUrl = async (httpClient, url) => {
|
|
19920
20630
|
if (usePlatformizedBrowse) {
|
|
19921
20631
|
try {
|
|
19922
20632
|
return await fetchPlatformizedMenu(url);
|
|
@@ -19933,10 +20643,77 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19933
20643
|
}
|
|
19934
20644
|
return menuContent.length > MENU_CONTENT_THRESHOLD ? summarizeLargeMenu(menuContent, url) : normalizeMenuContent(menuContent);
|
|
19935
20645
|
};
|
|
20646
|
+
const nearestToMissingPage = async (httpClient, url, isSkillsPage) => {
|
|
20647
|
+
try {
|
|
20648
|
+
if (isSkillsPage) {
|
|
20649
|
+
const manifestUrl = skillManifestUrl(url);
|
|
20650
|
+
if (!manifestUrl) return null;
|
|
20651
|
+
const manifest = JSON.parse(
|
|
20652
|
+
await fetchArticleContent(httpClient, manifestUrl, "article")
|
|
20653
|
+
);
|
|
20654
|
+
const guess = closestSkillFile(url, manifest, manifestUrl);
|
|
20655
|
+
if (!guess) return null;
|
|
20656
|
+
if (guess.kind === "file") {
|
|
20657
|
+
const text = await fetchArticleContent(
|
|
20658
|
+
httpClient,
|
|
20659
|
+
guess.url,
|
|
20660
|
+
"article"
|
|
20661
|
+
);
|
|
20662
|
+
return `Note: "${url}" does not exist. Found a similar file in the skill, ${guess.url}, and read it:
|
|
20663
|
+
|
|
20664
|
+
${text}`;
|
|
20665
|
+
}
|
|
20666
|
+
return `Note: "${url}" does not exist, and no file in ${guess.folder ? `its folder (${guess.folder}/)` : "the skill"} has a name close to it. ${guess.folder ? "The files in that folder" : "The skill's top-level folders and files"}, to read the one you need (every file: ${guess.manifestUrl}):
|
|
20667
|
+
|
|
20668
|
+
${guess.files.map((f) => `- ${f}`).join("\n")}`;
|
|
20669
|
+
}
|
|
20670
|
+
for (const menuUrl of parentMenuUrls(url)) {
|
|
20671
|
+
const text = await fetchMenuTextForUrl(httpClient, menuUrl);
|
|
20672
|
+
if (!text) continue;
|
|
20673
|
+
const subMenus = (await Promise.all(
|
|
20674
|
+
subMenusToRead(text, menuUrl, url).map(async (sub) => ({
|
|
20675
|
+
sub,
|
|
20676
|
+
text: await fetchMenuTextForUrl(httpClient, sub).catch(() => null)
|
|
20677
|
+
}))
|
|
20678
|
+
)).filter((m) => !!m.text);
|
|
20679
|
+
const closest = closestMenuLinks(
|
|
20680
|
+
[text, ...subMenus.map((m) => m.text)].join("\n"),
|
|
20681
|
+
menuUrl,
|
|
20682
|
+
url
|
|
20683
|
+
);
|
|
20684
|
+
return [
|
|
20685
|
+
`Note: "${url}" does not exist. Showing the nearest documentation menu above it, ${menuUrl}; read the page you need from its links.`,
|
|
20686
|
+
...closest.length ? [
|
|
20687
|
+
`Closest to what you asked for:
|
|
20688
|
+
${closest.map((l) => `- ${l}`).join("\n")}`
|
|
20689
|
+
] : [],
|
|
20690
|
+
text
|
|
20691
|
+
].join("\n\n");
|
|
20692
|
+
}
|
|
20693
|
+
} catch (error) {
|
|
20694
|
+
logger.error(`[docs] no nearest page for ${url}: ${error}`);
|
|
20695
|
+
}
|
|
20696
|
+
return null;
|
|
20697
|
+
};
|
|
20698
|
+
if (startDeployWebsiteEnabled) {
|
|
20699
|
+
addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
|
|
20700
|
+
skillsPortalUrl,
|
|
20701
|
+
repo: effectiveRepo,
|
|
20702
|
+
commitHash: effectiveCommitHash
|
|
20703
|
+
});
|
|
20704
|
+
}
|
|
20705
|
+
if (startHeadlessBuildEnabled) {
|
|
20706
|
+
addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
|
|
20707
|
+
skillsPortalUrl,
|
|
20708
|
+
repo: effectiveRepo,
|
|
20709
|
+
commitHash: effectiveCommitHash
|
|
20710
|
+
});
|
|
20711
|
+
}
|
|
19936
20712
|
if (dynamicContextEnabled && authStrategy && !disableTools?.includes("GetSiteContext")) {
|
|
19937
20713
|
addGetSiteContextTool({
|
|
19938
20714
|
server,
|
|
19939
20715
|
authStrategy,
|
|
20716
|
+
includeCompanionApp,
|
|
19940
20717
|
toolDescriptions,
|
|
19941
20718
|
disableTools: disableTools || []
|
|
19942
20719
|
});
|
|
@@ -19952,21 +20729,9 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19952
20729
|
dynamicContextEnabled,
|
|
19953
20730
|
repo: effectiveRepo,
|
|
19954
20731
|
commitHash: effectiveCommitHash,
|
|
19955
|
-
skillsTags
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
if (startDeployWebsiteEnabled) {
|
|
19959
|
-
addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
|
|
19960
|
-
skillsPortalUrl,
|
|
19961
|
-
repo: effectiveRepo,
|
|
19962
|
-
commitHash: effectiveCommitHash
|
|
19963
|
-
});
|
|
19964
|
-
}
|
|
19965
|
-
if (startHeadlessBuildEnabled) {
|
|
19966
|
-
addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
|
|
19967
|
-
skillsPortalUrl,
|
|
19968
|
-
repo: effectiveRepo,
|
|
19969
|
-
commitHash: effectiveCommitHash
|
|
20732
|
+
skillsTags,
|
|
20733
|
+
readmeRecipesIndexEnabled,
|
|
20734
|
+
docsSite
|
|
19970
20735
|
});
|
|
19971
20736
|
}
|
|
19972
20737
|
if (allowedTools.includes("WDS") && !disableTools?.includes("SearchWixWDSDocumentation")) {
|
|
@@ -19977,7 +20742,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19977
20742
|
defaultToolDescriptions.SearchWixWDSDocumentation ?? ""
|
|
19978
20743
|
),
|
|
19979
20744
|
(() => {
|
|
19980
|
-
const d =
|
|
20745
|
+
const d = desc2("SearchWixWDSDocumentation");
|
|
19981
20746
|
return {
|
|
19982
20747
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
19983
20748
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -19987,7 +20752,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
19987
20752
|
async ({ searchTerm, maxResults }, { panorama }) => {
|
|
19988
20753
|
try {
|
|
19989
20754
|
logger.log(`Searching for ${searchTerm} in Wix WDS`);
|
|
19990
|
-
const result = await
|
|
20755
|
+
const result = await runSearch({
|
|
19991
20756
|
toolName: "WDS",
|
|
19992
20757
|
toolParams: {
|
|
19993
20758
|
searchTerm
|
|
@@ -20030,7 +20795,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20030
20795
|
defaultToolDescriptions.SearchWixRESTDocumentation
|
|
20031
20796
|
),
|
|
20032
20797
|
(() => {
|
|
20033
|
-
const d =
|
|
20798
|
+
const d = desc2("SearchWixRESTDocumentation");
|
|
20034
20799
|
return {
|
|
20035
20800
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20036
20801
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(17).optional().default(17),
|
|
@@ -20043,7 +20808,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20043
20808
|
logger.log(
|
|
20044
20809
|
`[SearchWixRESTDocumentation] searchTerm="${searchTerm}", reason="${reason}"`
|
|
20045
20810
|
);
|
|
20046
|
-
const result = await
|
|
20811
|
+
const result = await runSearch({
|
|
20047
20812
|
toolName: "REST",
|
|
20048
20813
|
toolParams: {
|
|
20049
20814
|
searchTerm
|
|
@@ -20094,7 +20859,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20094
20859
|
defaultToolDescriptions.WixBusinessFlowsDocumentation ?? ""
|
|
20095
20860
|
),
|
|
20096
20861
|
(() => {
|
|
20097
|
-
const d =
|
|
20862
|
+
const d = desc2("WixBusinessFlowsDocumentation");
|
|
20098
20863
|
return {
|
|
20099
20864
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20100
20865
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -20104,7 +20869,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20104
20869
|
async ({ searchTerm, maxResults }, { panorama }) => {
|
|
20105
20870
|
try {
|
|
20106
20871
|
logger.log(`Searching for ${searchTerm} in Wix Sample Flows`);
|
|
20107
|
-
const result = await
|
|
20872
|
+
const result = await runSearch({
|
|
20108
20873
|
toolName: "BUSINESS_SOLUTIONS",
|
|
20109
20874
|
toolParams: {
|
|
20110
20875
|
searchTerm: "**RECIPE**: Business Recipes - full setup flow for::: " + searchTerm
|
|
@@ -20150,7 +20915,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20150
20915
|
defaultToolDescriptions.SearchWixSDKDocumentation ?? ""
|
|
20151
20916
|
),
|
|
20152
20917
|
(() => {
|
|
20153
|
-
const d =
|
|
20918
|
+
const d = desc2("SearchWixSDKDocumentation");
|
|
20154
20919
|
return {
|
|
20155
20920
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20156
20921
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -20161,7 +20926,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20161
20926
|
try {
|
|
20162
20927
|
logger.log(`Searching for ${searchTerm} in Wix SDK`);
|
|
20163
20928
|
panorama.transaction("SearchWixSDKDocumentation").start({ maxResults });
|
|
20164
|
-
const result = await
|
|
20929
|
+
const result = await runSearch({
|
|
20165
20930
|
toolName: "SDK",
|
|
20166
20931
|
toolParams: {
|
|
20167
20932
|
searchTerm
|
|
@@ -20209,7 +20974,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20209
20974
|
defaultToolDescriptions.SearchBuildAppsDocumentation ?? ""
|
|
20210
20975
|
),
|
|
20211
20976
|
(() => {
|
|
20212
|
-
const d =
|
|
20977
|
+
const d = desc2("SearchBuildAppsDocumentation");
|
|
20213
20978
|
return {
|
|
20214
20979
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20215
20980
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -20219,7 +20984,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20219
20984
|
async ({ searchTerm, maxResults }, { panorama }) => {
|
|
20220
20985
|
try {
|
|
20221
20986
|
logger.log(`Searching for ${searchTerm} in Build Apps`);
|
|
20222
|
-
const result = await
|
|
20987
|
+
const result = await runSearch({
|
|
20223
20988
|
toolName: "BUILD_APPS",
|
|
20224
20989
|
toolParams: {
|
|
20225
20990
|
searchTerm: `wix cli docs for ${searchTerm}`
|
|
@@ -20261,7 +21026,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20261
21026
|
defaultToolDescriptions.SearchDevelopmentDocumentation ?? ""
|
|
20262
21027
|
),
|
|
20263
21028
|
(() => {
|
|
20264
|
-
const d =
|
|
21029
|
+
const d = desc2("SearchDevelopmentDocumentation");
|
|
20265
21030
|
return {
|
|
20266
21031
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20267
21032
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -20271,7 +21036,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20271
21036
|
async ({ searchTerm, maxResults }, { panorama }) => {
|
|
20272
21037
|
try {
|
|
20273
21038
|
logger.log(`Searching for ${searchTerm} in Wix Overview`);
|
|
20274
|
-
const result = await
|
|
21039
|
+
const result = await runSearch({
|
|
20275
21040
|
toolName: "OVERVIEW",
|
|
20276
21041
|
toolParams: {
|
|
20277
21042
|
searchTerm
|
|
@@ -20313,7 +21078,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20313
21078
|
defaultToolDescriptions.SearchWixHeadlessDocumentation ?? ""
|
|
20314
21079
|
),
|
|
20315
21080
|
(() => {
|
|
20316
|
-
const d =
|
|
21081
|
+
const d = desc2("SearchWixHeadlessDocumentation");
|
|
20317
21082
|
return {
|
|
20318
21083
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20319
21084
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(17).optional().default(17)
|
|
@@ -20323,7 +21088,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20323
21088
|
async ({ searchTerm, maxResults }, { panorama }) => {
|
|
20324
21089
|
try {
|
|
20325
21090
|
logger.log(`Searching for ${searchTerm} in Headless`);
|
|
20326
|
-
const result = await
|
|
21091
|
+
const result = await runSearch({
|
|
20327
21092
|
toolName: "WIX_HEADLESS",
|
|
20328
21093
|
toolParams: {
|
|
20329
21094
|
searchTerm
|
|
@@ -20368,7 +21133,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20368
21133
|
defaultToolDescriptions.SearchWixVeloDocumentation ?? ""
|
|
20369
21134
|
),
|
|
20370
21135
|
(() => {
|
|
20371
|
-
const d =
|
|
21136
|
+
const d = desc2("SearchWixVeloDocumentation");
|
|
20372
21137
|
return {
|
|
20373
21138
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20374
21139
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15)
|
|
@@ -20379,7 +21144,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20379
21144
|
try {
|
|
20380
21145
|
logger.log(`Searching for ${searchTerm} in Velo`);
|
|
20381
21146
|
panorama.transaction("SearchWixVeloDocumentation").start({ maxResults });
|
|
20382
|
-
const result = await
|
|
21147
|
+
const result = await runSearch({
|
|
20383
21148
|
toolName: "VELO",
|
|
20384
21149
|
toolParams: {
|
|
20385
21150
|
searchTerm
|
|
@@ -20417,6 +21182,72 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20417
21182
|
}
|
|
20418
21183
|
);
|
|
20419
21184
|
}
|
|
21185
|
+
if (allowedTools.includes("HELP_CENTER") && !disableTools?.includes("SearchWixHelpCenter")) {
|
|
21186
|
+
server.tool(
|
|
21187
|
+
"SearchWixHelpCenter",
|
|
21188
|
+
getDescription(
|
|
21189
|
+
"SearchWixHelpCenter",
|
|
21190
|
+
defaultToolDescriptions.SearchWixHelpCenter ?? ""
|
|
21191
|
+
),
|
|
21192
|
+
(() => {
|
|
21193
|
+
const d = desc2("SearchWixHelpCenter");
|
|
21194
|
+
return {
|
|
21195
|
+
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
21196
|
+
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15),
|
|
21197
|
+
reason: external_exports.string().describe(d.reason)
|
|
21198
|
+
};
|
|
21199
|
+
})(),
|
|
21200
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
21201
|
+
async ({ searchTerm, maxResults, reason }, { panorama }) => {
|
|
21202
|
+
try {
|
|
21203
|
+
logger.log(
|
|
21204
|
+
`[SearchWixHelpCenter] searchTerm="${searchTerm}", reason="${reason}"`
|
|
21205
|
+
);
|
|
21206
|
+
const result = await runSemanticSearchAndFormat({
|
|
21207
|
+
toolName: "HELP_CENTER",
|
|
21208
|
+
toolParams: {
|
|
21209
|
+
searchTerm
|
|
21210
|
+
},
|
|
21211
|
+
maxResults: Math.max(1, Math.min(maxResults ?? 15, 15)),
|
|
21212
|
+
linesInEachResult: 60,
|
|
21213
|
+
usePlatformizedSearch
|
|
21214
|
+
});
|
|
21215
|
+
return {
|
|
21216
|
+
content: [
|
|
21217
|
+
{
|
|
21218
|
+
type: "text",
|
|
21219
|
+
text: `${HELP_CENTER_IS_FOR_PEOPLE}
|
|
21220
|
+
|
|
21221
|
+
${result}`
|
|
21222
|
+
}
|
|
21223
|
+
]
|
|
21224
|
+
};
|
|
21225
|
+
} catch (error) {
|
|
21226
|
+
panorama.errorMonitor().reportError(error);
|
|
21227
|
+
captureException(error, {
|
|
21228
|
+
tags: {
|
|
21229
|
+
componentId: "SearchWixHelpCenter",
|
|
21230
|
+
toolName: "SearchWixHelpCenter"
|
|
21231
|
+
}
|
|
21232
|
+
});
|
|
21233
|
+
logger.error(
|
|
21234
|
+
`Error searching for ${searchTerm} in the Wix Help Center: ${error}`
|
|
21235
|
+
);
|
|
21236
|
+
return {
|
|
21237
|
+
isError: true,
|
|
21238
|
+
content: [
|
|
21239
|
+
{
|
|
21240
|
+
type: "text",
|
|
21241
|
+
text: `Error: ${error.message}
|
|
21242
|
+
|
|
21243
|
+
${HELP_CENTER_IS_FOR_PEOPLE}`
|
|
21244
|
+
}
|
|
21245
|
+
]
|
|
21246
|
+
};
|
|
21247
|
+
}
|
|
21248
|
+
}
|
|
21249
|
+
);
|
|
21250
|
+
}
|
|
20420
21251
|
if (allowedTools.includes("CLI") && !disableTools?.includes("SearchWixCLIDocumentation")) {
|
|
20421
21252
|
server.tool(
|
|
20422
21253
|
"SearchWixCLIDocumentation",
|
|
@@ -20425,7 +21256,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20425
21256
|
defaultToolDescriptions.SearchWixCLIDocumentation ?? ""
|
|
20426
21257
|
),
|
|
20427
21258
|
(() => {
|
|
20428
|
-
const d =
|
|
21259
|
+
const d = desc2("SearchWixCLIDocumentation");
|
|
20429
21260
|
return {
|
|
20430
21261
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
20431
21262
|
maxResults: external_exports.number().describe(d.maxResults).min(1).max(15).optional().default(15),
|
|
@@ -20438,7 +21269,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20438
21269
|
logger.log(
|
|
20439
21270
|
`[SearchWixCLIDocumentation] searchTerm="${searchTerm}", reason="${reason}"`
|
|
20440
21271
|
);
|
|
20441
|
-
const result = await
|
|
21272
|
+
const result = await runSearch({
|
|
20442
21273
|
toolName: "CLI",
|
|
20443
21274
|
toolParams: {
|
|
20444
21275
|
searchTerm
|
|
@@ -20482,14 +21313,39 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20482
21313
|
defaultToolDescriptions.ReadFullDocsArticle
|
|
20483
21314
|
),
|
|
20484
21315
|
(() => {
|
|
20485
|
-
const d =
|
|
21316
|
+
const d = desc2("ReadFullDocsArticle");
|
|
20486
21317
|
return {
|
|
20487
21318
|
articleUrl: external_exports.string().url().describe(d.articleUrl)
|
|
20488
21319
|
};
|
|
20489
21320
|
})(),
|
|
20490
21321
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
20491
21322
|
async ({ articleUrl }, { httpClient }) => {
|
|
20492
|
-
const
|
|
21323
|
+
const source = parseSourceContentUrl(articleUrl);
|
|
21324
|
+
if (source) {
|
|
21325
|
+
try {
|
|
21326
|
+
const text = await fetchSourceContent(
|
|
21327
|
+
httpClient,
|
|
21328
|
+
source.repo,
|
|
21329
|
+
source.path,
|
|
21330
|
+
source.commitHash
|
|
21331
|
+
);
|
|
21332
|
+
return { content: [{ type: "text", text }] };
|
|
21333
|
+
} catch (error) {
|
|
21334
|
+
logger.error(
|
|
21335
|
+
`Error fetching source content for ${articleUrl}: ${error}`
|
|
21336
|
+
);
|
|
21337
|
+
return {
|
|
21338
|
+
isError: true,
|
|
21339
|
+
content: [
|
|
21340
|
+
{
|
|
21341
|
+
type: "text",
|
|
21342
|
+
text: formatDocsReadError("source content", error)
|
|
21343
|
+
}
|
|
21344
|
+
]
|
|
21345
|
+
};
|
|
21346
|
+
}
|
|
21347
|
+
}
|
|
21348
|
+
const validPrefixes = [...docsUrlPrefixes, "https://support.wix.com/"];
|
|
20493
21349
|
if (skillsPortalUrl) validPrefixes.push(skillsPortalUrl);
|
|
20494
21350
|
const isSkillsPage = isSkillsRegistryUrl(articleUrl);
|
|
20495
21351
|
if (!isSkillsPage && !validPrefixes.some((p) => articleUrl.startsWith(p))) {
|
|
@@ -20498,7 +21354,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20498
21354
|
content: [
|
|
20499
21355
|
{
|
|
20500
21356
|
type: "text",
|
|
20501
|
-
text: `Invalid article URL: must start with https://dev.wix.com/docs/, or be a Wix skills page (https://www.wix.com/skills/..., https://www.wix.com/headless
|
|
21357
|
+
text: `Invalid article URL: must start with https://dev.wix.com/docs/ or https://support.wix.com/, or be a Wix skills page (https://www.wix.com/skills/..., https://www.wix.com/headless/..., or that file's github.com/wix/skills source). Got: "${articleUrl}"`
|
|
20502
21358
|
}
|
|
20503
21359
|
]
|
|
20504
21360
|
};
|
|
@@ -20506,6 +21362,27 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20506
21362
|
if (!isSkillsPage && articleUrl.startsWith("https://dev.wix.com/docs/")) {
|
|
20507
21363
|
articleUrl = articleUrl.replace(/\.md$/, "");
|
|
20508
21364
|
}
|
|
21365
|
+
if (mintlifyDocs && !isSkillsPage) {
|
|
21366
|
+
try {
|
|
21367
|
+
const page = await readMintlifyMarkdown(articleUrl);
|
|
21368
|
+
if (page) {
|
|
21369
|
+
return {
|
|
21370
|
+
content: [
|
|
21371
|
+
{
|
|
21372
|
+
type: "text",
|
|
21373
|
+
text: isQueryOrSearchEndpoint(page.url) ? [page.markdown, ARTICLE_CAPABILITY_NOTE].join("\n\n") : page.markdown
|
|
21374
|
+
},
|
|
21375
|
+
...sourceNote(`Mintlify site: ${page.url}.md`)
|
|
21376
|
+
]
|
|
21377
|
+
};
|
|
21378
|
+
}
|
|
21379
|
+
} catch (error) {
|
|
21380
|
+
logger.error(
|
|
21381
|
+
`[mintlify] reading ${articleUrl} from Mintlify failed, falling back: ${error}`
|
|
21382
|
+
);
|
|
21383
|
+
}
|
|
21384
|
+
articleUrl = toBackendUrl(articleUrl);
|
|
21385
|
+
}
|
|
20509
21386
|
try {
|
|
20510
21387
|
const articleContent = await fetchArticleContent(
|
|
20511
21388
|
httpClient,
|
|
@@ -20526,7 +21403,10 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20526
21403
|
// The article's own URL, never its body — a filter/sort reference page links query
|
|
20527
21404
|
// methods in its prose but isn't one.
|
|
20528
21405
|
text: isQueryOrSearchEndpoint(articleUrl) ? [articleContent, ARTICLE_CAPABILITY_NOTE].join("\n\n") : articleContent
|
|
20529
|
-
}
|
|
21406
|
+
},
|
|
21407
|
+
...sourceNote(
|
|
21408
|
+
isSkillsPage ? `skills registry: ${articleUrl}` : `today's backend (dev.wix.com/rawdocs, ingvar), not on the Mintlify site: ${articleUrl}`
|
|
21409
|
+
)
|
|
20530
21410
|
]
|
|
20531
21411
|
};
|
|
20532
21412
|
} catch (error) {
|
|
@@ -20551,6 +21431,16 @@ ${text}`
|
|
|
20551
21431
|
} catch {
|
|
20552
21432
|
}
|
|
20553
21433
|
}
|
|
21434
|
+
if (getErrorMessage(error).includes("404") && !isKnownArticleError(error)) {
|
|
21435
|
+
const nearest = await nearestToMissingPage(
|
|
21436
|
+
httpClient,
|
|
21437
|
+
articleUrl,
|
|
21438
|
+
isSkillsPage
|
|
21439
|
+
);
|
|
21440
|
+
if (nearest) {
|
|
21441
|
+
return { content: [{ type: "text", text: nearest }] };
|
|
21442
|
+
}
|
|
21443
|
+
}
|
|
20554
21444
|
return {
|
|
20555
21445
|
isError: true,
|
|
20556
21446
|
content: [
|
|
@@ -20572,7 +21462,7 @@ ${text}`
|
|
|
20572
21462
|
defaultToolDescriptions.ReadFullDocsMethodSchema
|
|
20573
21463
|
),
|
|
20574
21464
|
(() => {
|
|
20575
|
-
const d =
|
|
21465
|
+
const d = desc2("ReadFullDocsMethodSchema");
|
|
20576
21466
|
return {
|
|
20577
21467
|
articleUrl: external_exports.string().url().describe(d.articleUrl),
|
|
20578
21468
|
reason: external_exports.string().describe(d.reason)
|
|
@@ -20580,7 +21470,7 @@ ${text}`
|
|
|
20580
21470
|
})(),
|
|
20581
21471
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
20582
21472
|
async ({ articleUrl, reason }, { httpClient }) => {
|
|
20583
|
-
const validPrefixes = [
|
|
21473
|
+
const validPrefixes = [...docsUrlPrefixes];
|
|
20584
21474
|
if (skillsPortalUrl) validPrefixes.push(skillsPortalUrl);
|
|
20585
21475
|
if (!validPrefixes.some((p) => articleUrl.startsWith(p))) {
|
|
20586
21476
|
return {
|
|
@@ -20596,6 +21486,7 @@ ${text}`
|
|
|
20596
21486
|
if (articleUrl.startsWith("https://dev.wix.com/docs/")) {
|
|
20597
21487
|
articleUrl = articleUrl.replace(/\.md$/, "");
|
|
20598
21488
|
}
|
|
21489
|
+
articleUrl = toBackendUrl(articleUrl);
|
|
20599
21490
|
try {
|
|
20600
21491
|
logger.log(
|
|
20601
21492
|
`[ReadFullDocsMethodSchema] url="${articleUrl}", reason="${reason}"`
|
|
@@ -20613,12 +21504,18 @@ ${text}`
|
|
|
20613
21504
|
if (!articleContent) {
|
|
20614
21505
|
throw new Error("Method schema is empty");
|
|
20615
21506
|
}
|
|
21507
|
+
const schemaText = mintlifyDocs ? (await rewriteDocsLinks(articleContent, {
|
|
21508
|
+
label: "schema ReadFullDocsMethodSchema"
|
|
21509
|
+
})).text : articleContent;
|
|
20616
21510
|
return {
|
|
20617
21511
|
content: [
|
|
20618
21512
|
{
|
|
20619
21513
|
type: "text",
|
|
20620
|
-
text:
|
|
20621
|
-
}
|
|
21514
|
+
text: schemaText
|
|
21515
|
+
},
|
|
21516
|
+
...sourceNote(
|
|
21517
|
+
`PAPI, through today's backend (dev.wix.com/rawdocs, ingvar): ${articleUrl}`
|
|
21518
|
+
)
|
|
20622
21519
|
]
|
|
20623
21520
|
};
|
|
20624
21521
|
} catch (error) {
|
|
@@ -20664,7 +21561,7 @@ ${text}`
|
|
|
20664
21561
|
defaultToolDescriptions.BrowseWixRESTDocsMenu
|
|
20665
21562
|
),
|
|
20666
21563
|
(() => {
|
|
20667
|
-
const d =
|
|
21564
|
+
const d = desc2("BrowseWixRESTDocsMenu");
|
|
20668
21565
|
return {
|
|
20669
21566
|
menuUrl: external_exports.string().optional().describe(d.menuUrl),
|
|
20670
21567
|
reason: external_exports.string().describe(d.reason)
|
|
@@ -20673,7 +21570,7 @@ ${text}`
|
|
|
20673
21570
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
20674
21571
|
async ({ menuUrl, reason }, { httpClient }) => {
|
|
20675
21572
|
try {
|
|
20676
|
-
if (menuUrl && !menuUrl.startsWith(
|
|
21573
|
+
if (menuUrl && !docsUrlPrefixes.some((p) => menuUrl.startsWith(p))) {
|
|
20677
21574
|
return {
|
|
20678
21575
|
content: [
|
|
20679
21576
|
{
|
|
@@ -20737,6 +21634,16 @@ var getRetryHint = (errorDetails) => {
|
|
|
20737
21634
|
}
|
|
20738
21635
|
return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
|
|
20739
21636
|
};
|
|
21637
|
+
var NO_SITE_IN_REQUEST = /META_SITE_NOT_FOUND|MetaSite(?:Id)? (?:context )?(?:is missing|was not found|not found)|NO_METASITE_IN_CALL_CONTEXT|MISSING_METASITE_ID|MISSING_REQUEST_SITE_CONTEXT|metasiteId from the request context/i;
|
|
21638
|
+
function missingSiteContext(status, responseData) {
|
|
21639
|
+
const details = typeof responseData === "string" ? responseData : JSON.stringify(responseData ?? "");
|
|
21640
|
+
if (NO_SITE_IN_REQUEST.test(details)) return "certain";
|
|
21641
|
+
const parsed = typeof responseData === "string" ? safeParseJSON(responseData) : responseData;
|
|
21642
|
+
if (status === 403 && parsed && typeof parsed === "object" && !parsed.message) {
|
|
21643
|
+
return "likely";
|
|
21644
|
+
}
|
|
21645
|
+
return null;
|
|
21646
|
+
}
|
|
20740
21647
|
var safeParseJSON = (text) => {
|
|
20741
21648
|
try {
|
|
20742
21649
|
return JSON.parse(text);
|
|
@@ -20748,7 +21655,8 @@ var buildErrorMessage = ({
|
|
|
20748
21655
|
status,
|
|
20749
21656
|
statusText,
|
|
20750
21657
|
requestId,
|
|
20751
|
-
responseData
|
|
21658
|
+
responseData,
|
|
21659
|
+
retryHint
|
|
20752
21660
|
}) => {
|
|
20753
21661
|
const responseDataObject = typeof responseData === "string" ? safeParseJSON(responseData) : responseData;
|
|
20754
21662
|
const errorDetails = typeof responseDataObject === "object" ? JSON.stringify(responseDataObject) : responseDataObject;
|
|
@@ -20760,7 +21668,7 @@ var buildErrorMessage = ({
|
|
|
20760
21668
|
].filter((str) => !!str).join("\n");
|
|
20761
21669
|
return `${baseMessage}
|
|
20762
21670
|
|
|
20763
|
-
${getRetryHint(String(errorDetails))}`;
|
|
21671
|
+
${retryHint ?? getRetryHint(String(errorDetails))}`;
|
|
20764
21672
|
};
|
|
20765
21673
|
var handleWixAPIResponse = async (response) => {
|
|
20766
21674
|
const requestId = response.headers?.["x-wix-request-id"];
|
|
@@ -20956,7 +21864,7 @@ function resolveBase64Image(imageBase64, mimeType) {
|
|
|
20956
21864
|
function errorResponse(text) {
|
|
20957
21865
|
return { isError: true, content: [{ type: "text", text }] };
|
|
20958
21866
|
}
|
|
20959
|
-
async function uploadFromUrl(item, authHeaders, displayName) {
|
|
21867
|
+
async function uploadFromUrl(item, authHeaders, displayName, imagePickerToolName) {
|
|
20960
21868
|
if (!item.download_url) {
|
|
20961
21869
|
return {
|
|
20962
21870
|
success: false,
|
|
@@ -20966,7 +21874,7 @@ async function uploadFromUrl(item, authHeaders, displayName) {
|
|
|
20966
21874
|
if (!/^https?:\/\//i.test(item.download_url)) {
|
|
20967
21875
|
return {
|
|
20968
21876
|
success: false,
|
|
20969
|
-
error: `
|
|
21877
|
+
error: `This server can't read files on your machine or sandbox (got: "${item.download_url}"). Send this image as imageBase64 (the whole file, one per call) or as a public URL in imageUrls${imagePickerToolName ? `, or call ${imagePickerToolName} so the user picks it` : ""}.`
|
|
20970
21878
|
};
|
|
20971
21879
|
}
|
|
20972
21880
|
try {
|
|
@@ -21064,7 +21972,10 @@ function formatBatchResults(results) {
|
|
|
21064
21972
|
};
|
|
21065
21973
|
}
|
|
21066
21974
|
function addUploadImageTool(server, strategy, options) {
|
|
21067
|
-
const
|
|
21975
|
+
const imagePickerToolName = options?.imagePickerToolName;
|
|
21976
|
+
const baseDescription = options?.description || defaultToolDescriptions.UploadImageToWixSite;
|
|
21977
|
+
const description = imagePickerToolName ? `${baseDescription}
|
|
21978
|
+
If you can't deliver the image any of these ways, call ${imagePickerToolName} so the user picks it.` : baseDescription;
|
|
21068
21979
|
const hints = {
|
|
21069
21980
|
readOnlyHint: options?.hints?.readOnlyHint ?? false,
|
|
21070
21981
|
destructiveHint: options?.hints?.destructiveHint ?? false,
|
|
@@ -21131,7 +22042,9 @@ function addUploadImageTool(server, strategy, options) {
|
|
|
21131
22042
|
try {
|
|
21132
22043
|
const authHeaders = await strategy.getSiteAuthHeaders(siteId);
|
|
21133
22044
|
const urlResults = await Promise.all(
|
|
21134
|
-
urlItems.map(
|
|
22045
|
+
urlItems.map(
|
|
22046
|
+
(item) => uploadFromUrl(item, authHeaders, displayName, imagePickerToolName)
|
|
22047
|
+
)
|
|
21135
22048
|
);
|
|
21136
22049
|
let base64Result = null;
|
|
21137
22050
|
if (imageBase64) {
|
|
@@ -21167,6 +22080,9 @@ function addUploadImageTool(server, strategy, options) {
|
|
|
21167
22080
|
var MAX_ALLOWED_RESPONSE_CHARS = 2e6;
|
|
21168
22081
|
|
|
21169
22082
|
// src/api-call/index.ts
|
|
22083
|
+
var siteToolCall = (executeServed) => executeServed ? "through ExecuteWixAPI with that site's `siteId` (`scope: \"site\"`), loading ExecuteWixAPI through your client's tool search if it isn't loaded" : "through CallWixSiteAPI with that site's `siteId`";
|
|
22084
|
+
var manageNoSiteHint = (executeServed) => `This is a site-level API: it acts inside a site, and ManageWixSite sends no site, so the same call fails here every time. Call the same URL, method and body ${siteToolCall(executeServed)}.`;
|
|
22085
|
+
var manageEmpty403Hint = (executeServed) => `If this endpoint acts inside a site (its products, data, bookings, members, pages, publishing), it is site-level, and ManageWixSite sends no site: call the same URL, method and body ${siteToolCall(executeServed)}. If it acts on the account or on sites as objects in it, the account lacks the permission; don't retry.`;
|
|
21170
22086
|
var DEFAULT_MAX_RESPONSE_SIZE = 5e4;
|
|
21171
22087
|
function extractReturnedRows(responseData) {
|
|
21172
22088
|
if (responseData === null || typeof responseData !== "object") {
|
|
@@ -21198,7 +22114,8 @@ function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_
|
|
|
21198
22114
|
}
|
|
21199
22115
|
return { text: responseText, isError: false, originalLength, returnedRows };
|
|
21200
22116
|
}
|
|
21201
|
-
function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "ListWixSites", "ManageWixSite"], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize) {
|
|
22117
|
+
function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "ListWixSites", "ManageWixSite"], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize, options) {
|
|
22118
|
+
const executeAdvice = (text, reconnect, plain) => options?.executeWixAPIServed ? text : text.replace(reconnect, plain);
|
|
21202
22119
|
const effectiveAllowedTools = [
|
|
21203
22120
|
.../* @__PURE__ */ new Set([...allowedTools, ...enableTools])
|
|
21204
22121
|
].filter((tool) => !disableTools.includes(tool));
|
|
@@ -21211,7 +22128,14 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21211
22128
|
if (effectiveAllowedTools.includes("CallWixSiteAPI") && !disableTools.includes("CallWixSiteAPI")) {
|
|
21212
22129
|
server.tool(
|
|
21213
22130
|
"CallWixSiteAPI",
|
|
21214
|
-
|
|
22131
|
+
executeAdvice(
|
|
22132
|
+
getDescription(
|
|
22133
|
+
"CallWixSiteAPI",
|
|
22134
|
+
defaultToolDescriptions.CallWixSiteAPI
|
|
22135
|
+
),
|
|
22136
|
+
CALL_WIX_SITE_API_RECONNECT_LINE,
|
|
22137
|
+
CALL_WIX_SITE_API_NO_EXECUTE_LINE
|
|
22138
|
+
),
|
|
21215
22139
|
(() => {
|
|
21216
22140
|
const d = desc("CallWixSiteAPI");
|
|
21217
22141
|
return {
|
|
@@ -21220,6 +22144,13 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21220
22144
|
method: external_exports.string().describe(d.method),
|
|
21221
22145
|
body: external_exports.union([external_exports.object({}).passthrough(), external_exports.array(external_exports.unknown())]).optional().describe(d.body),
|
|
21222
22146
|
reason: external_exports.string().describe(d.reason),
|
|
22147
|
+
whyNotExecuteWixAPI: external_exports.string().optional().describe(
|
|
22148
|
+
executeAdvice(
|
|
22149
|
+
d.whyNotExecuteWixAPI,
|
|
22150
|
+
WHY_NOT_EXECUTE_RECONNECT,
|
|
22151
|
+
WHY_NOT_EXECUTE_NO_EXECUTE
|
|
22152
|
+
)
|
|
22153
|
+
),
|
|
21223
22154
|
sourceDocUrl: external_exports.string().describe(d.sourceDocUrl)
|
|
21224
22155
|
};
|
|
21225
22156
|
})(),
|
|
@@ -21230,9 +22161,17 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21230
22161
|
destructiveHint: true,
|
|
21231
22162
|
openWorldHint: true
|
|
21232
22163
|
}),
|
|
21233
|
-
async ({
|
|
22164
|
+
async ({
|
|
22165
|
+
url,
|
|
22166
|
+
body,
|
|
22167
|
+
method,
|
|
22168
|
+
siteId,
|
|
22169
|
+
reason,
|
|
22170
|
+
whyNotExecuteWixAPI,
|
|
22171
|
+
sourceDocUrl
|
|
22172
|
+
}, { httpClient, setToolBiParams }) => {
|
|
21234
22173
|
logger.log(
|
|
21235
|
-
`[CallWixSiteAPI] siteId=${siteId}, method=${method}, url=${url}, sourceDocUrl=${sourceDocUrl}, reason="${reason}", body=${JSON.stringify(body)}`
|
|
22174
|
+
`[CallWixSiteAPI] siteId=${siteId}, method=${method}, url=${url}, sourceDocUrl=${sourceDocUrl}, reason="${reason}", whyNotExecuteWixAPI="${whyNotExecuteWixAPI ?? ""}", body=${JSON.stringify(body)}`
|
|
21236
22175
|
);
|
|
21237
22176
|
const requestHeaders = {
|
|
21238
22177
|
// Ask the Wix API edge for JSON error bodies (it serves a branded HTML
|
|
@@ -21467,6 +22406,18 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21467
22406
|
if (!response) {
|
|
21468
22407
|
throw new Error(`Failed to call Wix Account API: ${error}`);
|
|
21469
22408
|
}
|
|
22409
|
+
const noSite = missingSiteContext(response.status, response.data);
|
|
22410
|
+
if (noSite) {
|
|
22411
|
+
throw new Error(
|
|
22412
|
+
buildErrorMessage({
|
|
22413
|
+
status: response.status,
|
|
22414
|
+
statusText: response.statusText,
|
|
22415
|
+
requestId: response.headers?.["x-wix-request-id"] ?? null,
|
|
22416
|
+
responseData: response.data,
|
|
22417
|
+
retryHint: (noSite === "certain" ? manageNoSiteHint : manageEmpty403Hint)(!!options?.executeWixAPIServed)
|
|
22418
|
+
})
|
|
22419
|
+
);
|
|
22420
|
+
}
|
|
21470
22421
|
return handleWixAPIResponse(response);
|
|
21471
22422
|
}
|
|
21472
22423
|
}
|
|
@@ -21475,7 +22426,8 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
21475
22426
|
if (effectiveAllowedTools.includes("UploadImageToWixSite") && !disableTools.includes("UploadImageToWixSite")) {
|
|
21476
22427
|
addUploadImageTool(server, strategy, {
|
|
21477
22428
|
description: getDescription("UploadImageToWixSite", ""),
|
|
21478
|
-
hints: toolHints?.["UploadImageToWixSite"]
|
|
22429
|
+
hints: toolHints?.["UploadImageToWixSite"],
|
|
22430
|
+
imagePickerToolName: options?.imagePickerToolName
|
|
21479
22431
|
});
|
|
21480
22432
|
}
|
|
21481
22433
|
}
|
|
@@ -21493,10 +22445,14 @@ function truncateResponse(content, maxChars = DEFAULT_MAX_CHARS) {
|
|
|
21493
22445
|
const truncated = text.slice(0, effectiveMax);
|
|
21494
22446
|
const estimatedTokens = Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
21495
22447
|
const limitTokens = Math.ceil(effectiveMax / CHARS_PER_TOKEN);
|
|
21496
|
-
|
|
22448
|
+
const cutChars = text.length - effectiveMax;
|
|
22449
|
+
const note = `Response was ~${estimatedTokens.toLocaleString()} tokens (limit: ${limitTokens.toLocaleString()}). Showing the first ${effectiveMax.toLocaleString()} of ${text.length.toLocaleString()} characters; the last ${cutChars.toLocaleString()} were cut. To get the rest, run again and return less per call \u2014 only the fields you need, the next page (cursor or offset), or the next slice of a large array \u2014 and repeat until you have it all.`;
|
|
22450
|
+
return `[TRUNCATED: ${note}]
|
|
22451
|
+
|
|
22452
|
+
${truncated}
|
|
21497
22453
|
|
|
21498
22454
|
--- TRUNCATED ---
|
|
21499
|
-
|
|
22455
|
+
${note}`;
|
|
21500
22456
|
}
|
|
21501
22457
|
|
|
21502
22458
|
// src/graphql/constants.ts
|
|
@@ -22257,7 +23213,7 @@ ${text.slice(0, 2e3)}`
|
|
|
22257
23213
|
|
|
22258
23214
|
// src/code-mode/attachments.ts
|
|
22259
23215
|
var MAX_ATTACHMENTS_BYTES = 10 * 1024 * 1024;
|
|
22260
|
-
var
|
|
23216
|
+
var MAX_REDIRECTS2 = 4;
|
|
22261
23217
|
var OAI_USERCONTENT_HOST = /^[a-z0-9-]+\.oaiusercontent\.com$/;
|
|
22262
23218
|
var OAI_BLOB_ACCOUNTS = new Set(
|
|
22263
23219
|
[
|
|
@@ -22288,15 +23244,18 @@ var OAI_BLOB_ACCOUNTS = new Set(
|
|
|
22288
23244
|
"westus3"
|
|
22289
23245
|
].map((region) => `oaisdmntpr${region}.blob.core.windows.net`)
|
|
22290
23246
|
);
|
|
22291
|
-
|
|
23247
|
+
var WIX_AI_ASSISTANT_ATTACHMENT_HOSTS = /* @__PURE__ */ new Set([
|
|
23248
|
+
"wixmp-a85a29aaf55baa3e5171a82d.wixmp.com"
|
|
23249
|
+
]);
|
|
23250
|
+
function isAllowedAttachmentHost(hostname, extraHosts) {
|
|
22292
23251
|
const h = hostname.toLowerCase();
|
|
22293
|
-
return OAI_USERCONTENT_HOST.test(h) || OAI_BLOB_ACCOUNTS.has(h);
|
|
23252
|
+
return OAI_USERCONTENT_HOST.test(h) || OAI_BLOB_ACCOUNTS.has(h) || !!extraHosts?.has(h);
|
|
22294
23253
|
}
|
|
22295
|
-
function checkAttachmentUrl(url) {
|
|
23254
|
+
function checkAttachmentUrl(url, extraHosts) {
|
|
22296
23255
|
if (url.protocol !== "https:") return "is not an https URL";
|
|
22297
23256
|
if (url.port) return "uses a non-default port";
|
|
22298
|
-
if (!isAllowedAttachmentHost(url.hostname)) {
|
|
22299
|
-
return `host "${url.hostname}" is not a
|
|
23257
|
+
if (!isAllowedAttachmentHost(url.hostname, extraHosts)) {
|
|
23258
|
+
return `host "${url.hostname}" is not a chat attachment host`;
|
|
22300
23259
|
}
|
|
22301
23260
|
return void 0;
|
|
22302
23261
|
}
|
|
@@ -22323,7 +23282,7 @@ function toBase64(bytes) {
|
|
|
22323
23282
|
}
|
|
22324
23283
|
return btoa(binary);
|
|
22325
23284
|
}
|
|
22326
|
-
async function fetchAllowed(start, fetchImpl, label) {
|
|
23285
|
+
async function fetchAllowed(start, fetchImpl, label, extraHosts) {
|
|
22327
23286
|
let url = start;
|
|
22328
23287
|
for (let hop = 0; ; hop++) {
|
|
22329
23288
|
const res = await fetchImpl(url.toString(), { redirect: "manual" });
|
|
@@ -22331,7 +23290,7 @@ async function fetchAllowed(start, fetchImpl, label) {
|
|
|
22331
23290
|
const isRedirect = res.status >= 300 && res.status < 400 && location;
|
|
22332
23291
|
if (!isRedirect) return res;
|
|
22333
23292
|
await res.body?.cancel().catch(() => void 0);
|
|
22334
|
-
if (hop >=
|
|
23293
|
+
if (hop >= MAX_REDIRECTS2) {
|
|
22335
23294
|
throw new AttachmentError(`${label}: too many redirects.`);
|
|
22336
23295
|
}
|
|
22337
23296
|
let next;
|
|
@@ -22340,7 +23299,7 @@ async function fetchAllowed(start, fetchImpl, label) {
|
|
|
22340
23299
|
} catch {
|
|
22341
23300
|
throw new AttachmentError(`${label}: redirect to an invalid URL.`);
|
|
22342
23301
|
}
|
|
22343
|
-
const problem = checkAttachmentUrl(next);
|
|
23302
|
+
const problem = checkAttachmentUrl(next, extraHosts);
|
|
22344
23303
|
if (problem) {
|
|
22345
23304
|
throw new AttachmentError(
|
|
22346
23305
|
`${label}: the download redirected to a URL that ${problem}; refused.`
|
|
@@ -22378,7 +23337,7 @@ async function readWithin(res, budget, overBudget) {
|
|
|
22378
23337
|
}
|
|
22379
23338
|
return out;
|
|
22380
23339
|
}
|
|
22381
|
-
async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
23340
|
+
async function resolveAttachments(attachments, fetchImpl = fetch, extraHosts) {
|
|
22382
23341
|
if (!Array.isArray(attachments) || attachments.length === 0) return [];
|
|
22383
23342
|
const items = [];
|
|
22384
23343
|
attachments.forEach((raw, i) => {
|
|
@@ -22397,10 +23356,10 @@ async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
|
22397
23356
|
)}). \`attachments\` works only in ChatGPT, which swaps each file for a download URL before the call reaches Wix. Here it arrived as written, and Wix cannot read a path on your machine, so retrying the same value fails the same way. Upload another way. With a shell that reaches the internet, send the file from there (for a site, the StartDeployWebsite recipe's curl drop). Otherwise leave \`attachments\` out and put the files in \`files\`: text as is, and a binary as a \`=== FILE: <path> base64 ===\` entry only when it is a few KB (an icon, a logo), since a larger base64 payload does not survive your own output. A larger image goes in by public URL or through the user (the Media Manager).`
|
|
22398
23357
|
);
|
|
22399
23358
|
}
|
|
22400
|
-
const problem = checkAttachmentUrl(url);
|
|
23359
|
+
const problem = checkAttachmentUrl(url, extraHosts);
|
|
22401
23360
|
if (problem) {
|
|
22402
23361
|
throw new AttachmentError(
|
|
22403
|
-
`attachments[${i}].download_url ${problem}.
|
|
23362
|
+
`attachments[${i}].download_url ${problem}. \`attachments\` takes only files the chat platform attached and resolved; leave it out. For a file at a URL, import it in \`code\` with \`POST https://www.wixapis.com/site-media/v1/files/import\` and \`{ url }\` (Wix fetches it); for a small file you have as text, use the \`files\` bundle.`
|
|
22404
23363
|
);
|
|
22405
23364
|
}
|
|
22406
23365
|
items.push({ a, url });
|
|
@@ -22412,7 +23371,7 @@ async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
|
22412
23371
|
const label = `attachments[${i}] (${path})`;
|
|
22413
23372
|
let res;
|
|
22414
23373
|
try {
|
|
22415
|
-
res = await fetchAllowed(url, fetchImpl, label);
|
|
23374
|
+
res = await fetchAllowed(url, fetchImpl, label, extraHosts);
|
|
22416
23375
|
} catch (e) {
|
|
22417
23376
|
if (e instanceof AttachmentError) throw e;
|
|
22418
23377
|
throw new AttachmentError(
|
|
@@ -22497,7 +23456,13 @@ async function unzip(z) {
|
|
|
22497
23456
|
let p = u32(end + 16);
|
|
22498
23457
|
let total = 0;
|
|
22499
23458
|
const decoder = new TextDecoder();
|
|
22500
|
-
|
|
23459
|
+
const n = u16(end + 10);
|
|
23460
|
+
if (n > MAX_SITE_FILES) {
|
|
23461
|
+
throw new Error(
|
|
23462
|
+
`zip holds ${n} entries; a site upload is at most ${MAX_SITE_FILES} files`
|
|
23463
|
+
);
|
|
23464
|
+
}
|
|
23465
|
+
for (let i = 0; i < n; i++) {
|
|
22501
23466
|
const path = decoder.decode(z.slice(p + 46, p + 46 + u16(p + 28)));
|
|
22502
23467
|
const method = u16(p + 10);
|
|
22503
23468
|
const size = u32(p + 24);
|
|
@@ -22533,20 +23498,106 @@ async function unzip(z) {
|
|
|
22533
23498
|
}
|
|
22534
23499
|
return out;
|
|
22535
23500
|
}
|
|
23501
|
+
var CRC_TABLE = (() => {
|
|
23502
|
+
const t = new Uint32Array(256);
|
|
23503
|
+
for (let n = 0; n < 256; n++) {
|
|
23504
|
+
let c = n;
|
|
23505
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
23506
|
+
t[n] = c >>> 0;
|
|
23507
|
+
}
|
|
23508
|
+
return t;
|
|
23509
|
+
})();
|
|
23510
|
+
function crc32(bytes) {
|
|
23511
|
+
let c = 4294967295;
|
|
23512
|
+
for (let i = 0; i < bytes.length; i++)
|
|
23513
|
+
c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
|
|
23514
|
+
return (c ^ 4294967295) >>> 0;
|
|
23515
|
+
}
|
|
23516
|
+
function zipStored(files) {
|
|
23517
|
+
const encoder = new TextEncoder();
|
|
23518
|
+
const entries = files.map((f) => ({
|
|
23519
|
+
name: encoder.encode(f.path),
|
|
23520
|
+
bytes: f.bytes,
|
|
23521
|
+
crc: crc32(f.bytes)
|
|
23522
|
+
}));
|
|
23523
|
+
const localSize = entries.reduce(
|
|
23524
|
+
(sum, e) => sum + 30 + e.name.length + e.bytes.length,
|
|
23525
|
+
0
|
|
23526
|
+
);
|
|
23527
|
+
const centralSize = entries.reduce((sum, e) => sum + 46 + e.name.length, 0);
|
|
23528
|
+
const out = new Uint8Array(localSize + centralSize + 22);
|
|
23529
|
+
const view = new DataView(out.buffer);
|
|
23530
|
+
const UTF8_FLAG = 2048;
|
|
23531
|
+
let p = 0;
|
|
23532
|
+
const offsets = [];
|
|
23533
|
+
for (const e of entries) {
|
|
23534
|
+
offsets.push(p);
|
|
23535
|
+
view.setUint32(p, 67324752, true);
|
|
23536
|
+
view.setUint16(p + 4, 20, true);
|
|
23537
|
+
view.setUint16(p + 6, UTF8_FLAG, true);
|
|
23538
|
+
view.setUint16(p + 8, 0, true);
|
|
23539
|
+
view.setUint32(p + 10, 0, true);
|
|
23540
|
+
view.setUint32(p + 14, e.crc, true);
|
|
23541
|
+
view.setUint32(p + 18, e.bytes.length, true);
|
|
23542
|
+
view.setUint32(p + 22, e.bytes.length, true);
|
|
23543
|
+
view.setUint16(p + 26, e.name.length, true);
|
|
23544
|
+
view.setUint16(p + 28, 0, true);
|
|
23545
|
+
out.set(e.name, p + 30);
|
|
23546
|
+
out.set(e.bytes, p + 30 + e.name.length);
|
|
23547
|
+
p += 30 + e.name.length + e.bytes.length;
|
|
23548
|
+
}
|
|
23549
|
+
const centralStart = p;
|
|
23550
|
+
entries.forEach((e, i) => {
|
|
23551
|
+
view.setUint32(p, 33639248, true);
|
|
23552
|
+
view.setUint16(p + 4, 20, true);
|
|
23553
|
+
view.setUint16(p + 6, 20, true);
|
|
23554
|
+
view.setUint16(p + 8, UTF8_FLAG, true);
|
|
23555
|
+
view.setUint16(p + 10, 0, true);
|
|
23556
|
+
view.setUint32(p + 12, 0, true);
|
|
23557
|
+
view.setUint32(p + 16, e.crc, true);
|
|
23558
|
+
view.setUint32(p + 20, e.bytes.length, true);
|
|
23559
|
+
view.setUint32(p + 24, e.bytes.length, true);
|
|
23560
|
+
view.setUint16(p + 28, e.name.length, true);
|
|
23561
|
+
view.setUint32(p + 42, offsets[i], true);
|
|
23562
|
+
out.set(e.name, p + 46);
|
|
23563
|
+
p += 46 + e.name.length;
|
|
23564
|
+
});
|
|
23565
|
+
view.setUint32(p, 101010256, true);
|
|
23566
|
+
view.setUint16(p + 8, entries.length, true);
|
|
23567
|
+
view.setUint16(p + 10, entries.length, true);
|
|
23568
|
+
view.setUint32(p + 12, p - centralStart, true);
|
|
23569
|
+
view.setUint32(p + 16, centralStart, true);
|
|
23570
|
+
return out;
|
|
23571
|
+
}
|
|
22536
23572
|
var isZip = (f) => f.bytes.length >= 4 && f.bytes[0] === 80 && f.bytes[1] === 75 && (f.bytes[2] === 3 && f.bytes[3] === 4 || f.bytes[2] === 5 && f.bytes[3] === 6) || /\.zip$/i.test(f.path);
|
|
22537
23573
|
var isRootIndex = (p) => /^index\.html?$/i.test(p);
|
|
22538
|
-
async function
|
|
22539
|
-
|
|
23574
|
+
async function expandSiteFiles(input) {
|
|
23575
|
+
const list = [];
|
|
23576
|
+
let bytes = 0;
|
|
22540
23577
|
for (const f of input) {
|
|
22541
|
-
|
|
22542
|
-
|
|
23578
|
+
const added = isZip(f) ? await unzip(f.bytes) : [f];
|
|
23579
|
+
list.push(...added);
|
|
23580
|
+
bytes += added.reduce((sum, a) => sum + a.bytes.byteLength, 0);
|
|
23581
|
+
if (list.length > MAX_SITE_FILES) {
|
|
23582
|
+
throw new Error(
|
|
23583
|
+
`Too many files (more than ${MAX_SITE_FILES}); a site upload is at most ${MAX_SITE_FILES}.`
|
|
23584
|
+
);
|
|
23585
|
+
}
|
|
23586
|
+
if (bytes > MAX_UNZIP_BYTES) {
|
|
23587
|
+
throw new Error(
|
|
23588
|
+
`The upload expands past ${MAX_UNZIP_BYTES} bytes; send only the site's files.`
|
|
23589
|
+
);
|
|
23590
|
+
}
|
|
22543
23591
|
}
|
|
22544
|
-
|
|
23592
|
+
return list.map((f) => ({
|
|
22545
23593
|
...f,
|
|
22546
23594
|
path: f.path.replace(/\\/g, "/").replace(/^\.?\//, "")
|
|
22547
23595
|
})).filter(
|
|
22548
23596
|
(f) => f.path && !f.path.startsWith("__MACOSX/") && !/(^|\/)\.DS_Store$/.test(f.path) && !f.path.split("/").includes("..")
|
|
22549
23597
|
);
|
|
23598
|
+
}
|
|
23599
|
+
function staticSiteFiles(expanded) {
|
|
23600
|
+
let list = expanded;
|
|
22550
23601
|
if (list.length === 0) {
|
|
22551
23602
|
throw new Error(
|
|
22552
23603
|
"No files to upload: pass the site in `files` or `attachments`."
|
|
@@ -22626,21 +23677,281 @@ function buildFormData(files) {
|
|
|
22626
23677
|
return fd;
|
|
22627
23678
|
}
|
|
22628
23679
|
|
|
23680
|
+
// src/api-call/headless-release.ts
|
|
23681
|
+
var BUILD_METADATA_SUFFIX = ".wix/build-metadata.json";
|
|
23682
|
+
function withoutEmptyStatics(files, build) {
|
|
23683
|
+
const dir = build.clientDir;
|
|
23684
|
+
if (dir === void 0) return { files, dropped: [] };
|
|
23685
|
+
const dropped = [];
|
|
23686
|
+
const kept = files.filter((f) => {
|
|
23687
|
+
const path = normalizePath(f.path);
|
|
23688
|
+
if (f.bytes.byteLength === 0 && path.startsWith(dir)) {
|
|
23689
|
+
dropped.push(path.slice(dir.length));
|
|
23690
|
+
return false;
|
|
23691
|
+
}
|
|
23692
|
+
return true;
|
|
23693
|
+
});
|
|
23694
|
+
return { files: kept, dropped };
|
|
23695
|
+
}
|
|
23696
|
+
var normalizePath = (p) => p.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
23697
|
+
function findHeadlessBuild(files) {
|
|
23698
|
+
const entries = new Map(files.map((f) => [normalizePath(f.path), f.bytes]));
|
|
23699
|
+
const metadataEntry = [...entries.keys()].find(
|
|
23700
|
+
(name) => name === BUILD_METADATA_SUFFIX || name.endsWith(`/${BUILD_METADATA_SUFFIX}`)
|
|
23701
|
+
);
|
|
23702
|
+
if (metadataEntry === void 0) return null;
|
|
23703
|
+
const zipRoot = metadataEntry.slice(
|
|
23704
|
+
0,
|
|
23705
|
+
metadataEntry.length - BUILD_METADATA_SUFFIX.length
|
|
23706
|
+
);
|
|
23707
|
+
let metadata;
|
|
23708
|
+
try {
|
|
23709
|
+
metadata = JSON.parse(
|
|
23710
|
+
new TextDecoder().decode(entries.get(metadataEntry))
|
|
23711
|
+
);
|
|
23712
|
+
} catch {
|
|
23713
|
+
throw new Error(`${BUILD_METADATA_SUFFIX} is not valid JSON.`);
|
|
23714
|
+
}
|
|
23715
|
+
if (typeof metadata.appManifestPath !== "string") {
|
|
23716
|
+
throw new Error(
|
|
23717
|
+
`${BUILD_METADATA_SUFFIX} has no appManifestPath; send the build as the Wix hosting adapter wrote it (.wix/ and dist/ zipped from the project root).`
|
|
23718
|
+
);
|
|
23719
|
+
}
|
|
23720
|
+
const manifestPath = resolveZipPath(
|
|
23721
|
+
metadata.appManifestPath,
|
|
23722
|
+
zipRoot,
|
|
23723
|
+
entries
|
|
23724
|
+
);
|
|
23725
|
+
if (manifestPath === void 0) {
|
|
23726
|
+
throw new Error(
|
|
23727
|
+
`The build's app manifest (${metadata.appManifestPath}) is not in the upload; zip .wix/ and dist/ together from the project root after \`astro build\`.`
|
|
23728
|
+
);
|
|
23729
|
+
}
|
|
23730
|
+
const builderRoot = metadata.appManifestPath.slice(
|
|
23731
|
+
0,
|
|
23732
|
+
metadata.appManifestPath.length - (manifestPath.length - zipRoot.length)
|
|
23733
|
+
);
|
|
23734
|
+
const clientDir = metadata.clientDir === void 0 ? void 0 : toZipDir(metadata.clientDir, builderRoot, zipRoot);
|
|
23735
|
+
const serverDir = metadata.serverDir === void 0 ? void 0 : toZipDir(metadata.serverDir, builderRoot, zipRoot);
|
|
23736
|
+
const clientFiles = [];
|
|
23737
|
+
const serverFiles = [];
|
|
23738
|
+
for (const [name, bytes] of entries) {
|
|
23739
|
+
if (name === manifestPath) continue;
|
|
23740
|
+
if (serverDir !== void 0 && name.startsWith(serverDir)) {
|
|
23741
|
+
serverFiles.push({ path: name.slice(serverDir.length), bytes });
|
|
23742
|
+
} else if (clientDir !== void 0 && name.startsWith(clientDir)) {
|
|
23743
|
+
clientFiles.push({ path: name.slice(clientDir.length), bytes });
|
|
23744
|
+
}
|
|
23745
|
+
}
|
|
23746
|
+
if (clientFiles.length + serverFiles.length === 0) {
|
|
23747
|
+
throw new Error(
|
|
23748
|
+
"The build has no client or server output files; run `astro build` first and zip .wix/ and dist/ from the project root."
|
|
23749
|
+
);
|
|
23750
|
+
}
|
|
23751
|
+
if (serverFiles.length > 0 && !serverFiles.some((f) => f.path === "entry.mjs")) {
|
|
23752
|
+
throw new Error(
|
|
23753
|
+
"The server build has no entry.mjs, which the Wix hosting adapter (@wix/astro-wix-hosting-adapter) writes into the server folder on every build. Build with the project's own `astro build` (the adapter in astro.config) and zip .wix/ and dist/ from the project root as the build left them."
|
|
23754
|
+
);
|
|
23755
|
+
}
|
|
23756
|
+
const manifestText = new TextDecoder().decode(entries.get(manifestPath));
|
|
23757
|
+
return {
|
|
23758
|
+
metadata,
|
|
23759
|
+
root: zipRoot,
|
|
23760
|
+
manifestText,
|
|
23761
|
+
clientFiles,
|
|
23762
|
+
serverFiles,
|
|
23763
|
+
clientDir
|
|
23764
|
+
};
|
|
23765
|
+
}
|
|
23766
|
+
var NOT_SOURCE_TOP = /* @__PURE__ */ new Set(["dist", ".wix", ".astro", ".agents"]);
|
|
23767
|
+
var isSourcePath = (relative) => {
|
|
23768
|
+
const segments = relative.split("/");
|
|
23769
|
+
return !NOT_SOURCE_TOP.has(segments[0]) && !segments.some((s) => s === "node_modules" || s.startsWith(".env"));
|
|
23770
|
+
};
|
|
23771
|
+
function buildSourceFiles(files, build) {
|
|
23772
|
+
return files.flatMap((f) => {
|
|
23773
|
+
const path = normalizePath(f.path);
|
|
23774
|
+
if (!path.startsWith(build.root)) return [];
|
|
23775
|
+
const relative = path.slice(build.root.length);
|
|
23776
|
+
return relative && isSourcePath(relative) ? [{ path: relative, bytes: f.bytes }] : [];
|
|
23777
|
+
});
|
|
23778
|
+
}
|
|
23779
|
+
function resolveZipPath(builderPath, zipRoot, entries) {
|
|
23780
|
+
const segments = normalizePath(builderPath).split("/").filter((s) => s.length > 0);
|
|
23781
|
+
for (let k = 0; k < segments.length; k++) {
|
|
23782
|
+
const candidate = zipRoot + segments.slice(k).join("/");
|
|
23783
|
+
if (entries.has(candidate)) return candidate;
|
|
23784
|
+
}
|
|
23785
|
+
return void 0;
|
|
23786
|
+
}
|
|
23787
|
+
function toZipDir(builderDir, builderRoot, zipRoot) {
|
|
23788
|
+
const normalized = normalizePath(builderDir);
|
|
23789
|
+
const normalizedRoot = normalizePath(builderRoot);
|
|
23790
|
+
const relative = builderDir.startsWith(builderRoot) ? builderDir.slice(builderRoot.length) : normalized.startsWith(normalizedRoot) ? normalized.slice(normalizedRoot.length) : normalized;
|
|
23791
|
+
const dir = zipRoot + normalizePath(relative);
|
|
23792
|
+
return dir.endsWith("/") ? dir : `${dir}/`;
|
|
23793
|
+
}
|
|
23794
|
+
|
|
23795
|
+
// src/infra/redact-personal-data.ts
|
|
23796
|
+
var FREE_TEXT_INFO_PARAMS = [
|
|
23797
|
+
"userRequest",
|
|
23798
|
+
"siteDescription",
|
|
23799
|
+
"siteForm",
|
|
23800
|
+
"siteFunctionality",
|
|
23801
|
+
"agentIdentity",
|
|
23802
|
+
"wixAccess",
|
|
23803
|
+
"reason",
|
|
23804
|
+
"whyNotExecuteWixAPI",
|
|
23805
|
+
"editorRequest",
|
|
23806
|
+
"templateRequest"
|
|
23807
|
+
];
|
|
23808
|
+
var EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9-]+(?:\.[A-Z0-9-]+)*\.[A-Z]{2,}/gi;
|
|
23809
|
+
var NAMED_SECRET = /\b(passwords?|passwd|pwd|passcodes?|secrets?|tokens?|api[ _-]?keys?|access[ _-]?keys?|credentials?)(\s*[:=]\s*|\s+(?:is|was|to)\s+)(?:"[^"]*"|'[^']*'|`[^`]*`|[^\s"'`,;][^,;\n]*?(?=[.!?]\s|[,;\n]|$))/gi;
|
|
23810
|
+
var BEARER = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
23811
|
+
var JWT = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g;
|
|
23812
|
+
var KEY_PREFIXES = /\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_\w{20,}|xox[abprs]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})\b/g;
|
|
23813
|
+
var BACKTICK = "`";
|
|
23814
|
+
var UNIX_ROOT = String.raw`(?<![\w.:/~-])(?:~|/(?:Users|home|root|mnt|tmp|private|var|Volumes|workspace|work))`;
|
|
23815
|
+
var WINDOWS_ROOT = String.raw`(?<!\w)[A-Za-z]:(?=\\\w)`;
|
|
23816
|
+
var PATH_ROOT = `(?:${UNIX_ROOT}|${WINDOWS_ROOT})`;
|
|
23817
|
+
var QUOTED_PATH = new RegExp(
|
|
23818
|
+
String.raw`(?<![\w])(["'${BACKTICK}])${PATH_ROOT}[^"'${BACKTICK}\n]*\1`,
|
|
23819
|
+
"g"
|
|
23820
|
+
);
|
|
23821
|
+
var FOLDER = String.raw`[^\s/\\"'${BACKTICK},;()<>]+(?: [^\s/\\"'${BACKTICK},;()<>]+)*`;
|
|
23822
|
+
var LOCAL_PATH = new RegExp(
|
|
23823
|
+
String.raw`${PATH_ROOT}(?:[/\\]${FOLDER}(?=[/\\]))*[/\\][^\s"'${BACKTICK},;()<>]*`,
|
|
23824
|
+
"g"
|
|
23825
|
+
);
|
|
23826
|
+
var PHONE_CANDIDATE = /(?<![\w-])\+?\(?\d[\d\s().-]{7,}\d(?![\w-])/g;
|
|
23827
|
+
var DATE_START = /^\d{4}-\d{2}-\d{2}/;
|
|
23828
|
+
function redactPhone(match, offset, text) {
|
|
23829
|
+
if (DATE_START.test(match) || /^:\d{2}\b/.test(text.slice(offset + match.length))) {
|
|
23830
|
+
return match;
|
|
23831
|
+
}
|
|
23832
|
+
const digits = match.replace(/\D/g, "").length;
|
|
23833
|
+
return digits >= 9 && digits <= 15 ? "[phone]" : match;
|
|
23834
|
+
}
|
|
23835
|
+
function redactPersonalData(text) {
|
|
23836
|
+
return text.replace(EMAIL, "[email]").replace(BEARER, "Bearer [secret]").replace(JWT, "[secret]").replace(KEY_PREFIXES, "[secret]").replace(NAMED_SECRET, "$1$2[secret]").replace(QUOTED_PATH, "$1[path]$1").replace(LOCAL_PATH, "[path]").replace(PHONE_CANDIDATE, redactPhone);
|
|
23837
|
+
}
|
|
23838
|
+
function redactInfoParams(value) {
|
|
23839
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23840
|
+
return value;
|
|
23841
|
+
}
|
|
23842
|
+
const record = value;
|
|
23843
|
+
if (!FREE_TEXT_INFO_PARAMS.some((key) => typeof record[key] === "string")) {
|
|
23844
|
+
return value;
|
|
23845
|
+
}
|
|
23846
|
+
const redacted = { ...record };
|
|
23847
|
+
for (const key of FREE_TEXT_INFO_PARAMS) {
|
|
23848
|
+
if (typeof redacted[key] === "string") {
|
|
23849
|
+
redacted[key] = redactPersonalData(redacted[key]);
|
|
23850
|
+
}
|
|
23851
|
+
}
|
|
23852
|
+
return redacted;
|
|
23853
|
+
}
|
|
23854
|
+
var MAX_LISTED_PATHS_CHARS = 2e3;
|
|
23855
|
+
function summarizeFileBundle(bundle) {
|
|
23856
|
+
let sections;
|
|
23857
|
+
try {
|
|
23858
|
+
sections = parseFileBundle(bundle).map((file) => ({
|
|
23859
|
+
path: file.path,
|
|
23860
|
+
bytes: file.bytes.length
|
|
23861
|
+
}));
|
|
23862
|
+
} catch {
|
|
23863
|
+
sections = [];
|
|
23864
|
+
}
|
|
23865
|
+
if (sections.length === 0) {
|
|
23866
|
+
return `${new TextEncoder().encode(bundle).length} bytes, no file sections`;
|
|
23867
|
+
}
|
|
23868
|
+
return formatFileSummary(sections);
|
|
23869
|
+
}
|
|
23870
|
+
function formatFileSummary(files) {
|
|
23871
|
+
const total = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
23872
|
+
let listed = "";
|
|
23873
|
+
let shown = 0;
|
|
23874
|
+
for (const file of files) {
|
|
23875
|
+
const entry = `${shown ? ", " : ""}${file.path} (${file.bytes})`;
|
|
23876
|
+
if (listed.length + entry.length > MAX_LISTED_PATHS_CHARS) break;
|
|
23877
|
+
listed += entry;
|
|
23878
|
+
shown++;
|
|
23879
|
+
}
|
|
23880
|
+
const more = files.length - shown;
|
|
23881
|
+
return `${files.length} file(s), ${total} bytes: ${listed}${more ? `${shown ? ", " : ""}and ${more} more` : ""}`;
|
|
23882
|
+
}
|
|
23883
|
+
function compactFileParams(value) {
|
|
23884
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23885
|
+
return value;
|
|
23886
|
+
}
|
|
23887
|
+
const record = value;
|
|
23888
|
+
const files = record.files;
|
|
23889
|
+
if (typeof files === "string") {
|
|
23890
|
+
return { ...record, files: summarizeFileBundle(files) };
|
|
23891
|
+
}
|
|
23892
|
+
if (Array.isArray(files) && files.length > 0 && files.every(
|
|
23893
|
+
(entry) => entry && typeof entry === "object" && typeof entry.content === "string"
|
|
23894
|
+
)) {
|
|
23895
|
+
return {
|
|
23896
|
+
...record,
|
|
23897
|
+
files: formatFileSummary(
|
|
23898
|
+
files.map((entry) => ({
|
|
23899
|
+
path: entry.path ?? "?",
|
|
23900
|
+
bytes: new TextEncoder().encode(entry.content).length
|
|
23901
|
+
}))
|
|
23902
|
+
)
|
|
23903
|
+
};
|
|
23904
|
+
}
|
|
23905
|
+
return value;
|
|
23906
|
+
}
|
|
23907
|
+
function prepareForBi(value) {
|
|
23908
|
+
return compactFileParams(redactInfoParams(value));
|
|
23909
|
+
}
|
|
23910
|
+
function serializeToolParamsForBi(args) {
|
|
23911
|
+
return JSON.stringify(
|
|
23912
|
+
Array.isArray(args) ? args.map(prepareForBi) : prepareForBi(args)
|
|
23913
|
+
);
|
|
23914
|
+
}
|
|
23915
|
+
|
|
22629
23916
|
// src/api-call/upload-headless-website-files.ts
|
|
22630
23917
|
var UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME = "UploadHeadlessWebsiteFiles";
|
|
22631
23918
|
var HEADLESS_BUSINESS = "https://www.wixapis.com/headless-business-setup/v1/headless-business";
|
|
23919
|
+
var MAX_BUNDLE_BYTES = 100 * 1024 * 1024;
|
|
23920
|
+
var MAX_RELEASE_STATIC_FILES = 2500;
|
|
23921
|
+
var MAX_SOURCE_ZIP_BYTES = 64 * 1024 * 1024;
|
|
22632
23922
|
function dropAttribution(clientName) {
|
|
22633
23923
|
const slug = (clientName ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
22634
23924
|
return `?campaign=mcp&agent=${encodeURIComponent(slug || "wix-mcp")}`;
|
|
22635
23925
|
}
|
|
22636
23926
|
var UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS = {
|
|
22637
|
-
files: "The site files as text, so file bytes never pass through a code string. Sections delimited by a line `=== FILE: <path> ===` (text, raw) or `=== FILE: <path> base64 ===` (binary, base64); `<path>` is relative to the site root
|
|
22638
|
-
attachments:
|
|
22639
|
-
|
|
23927
|
+
files: "The site files as text, so file bytes never pass through a code string. Sections delimited by a line `=== FILE: <path> ===` (text, raw) or `=== FILE: <path> base64 ===` (binary, base64); `<path>` is relative to the site root; a static site must include `index.html`. Use base64 for small binaries (icons, logos, fonts); large images/video belong behind a URL in the HTML, not in the bundle. Optional when `attachments` carries the files. A Wix Astro build (`.wix/build-metadata.json` and `dist/` as `astro build` with the Wix hosting adapter wrote them, zipped from the project root) needs no `index.html`: it is released to the site's Wix hosting instead of dropped, and the project's other files in the upload are stored as its source when the site's repository accepts it.",
|
|
23928
|
+
attachments: fileParamNote(
|
|
23929
|
+
"for a static site use `files`, or drop the files as the StartDeployWebsite recipe shows; for a Wix Astro build from a shell that reaches the internet, zip it, call with `prepare: true` and its `size` and PUT the zips with curl"
|
|
23930
|
+
) + " Any type: a zip of the whole site, HTML, CSS, JS, images. A zip is unpacked; a top-level folder wrapping the site is stripped. A Wix Astro build zipped with the whole project (minus `node_modules`) is released with its source stored when the site's repository accepts it. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.",
|
|
23931
|
+
prepare: "Phase one of the two-call upload of a Wix Astro build, for a shell that reaches the internet and no file handle (a sandbox whose network is limited to a few hosts cannot upload to the URLs): zip the build first, then `true` with its `size` returns presigned upload URLs for the build zip and, when the site's repository takes it, the project source zip, as `curl` commands to run verbatim, with the exact second call. The two calls: {siteId, prepare: true, size}, then {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. No `files`, `attachments`, `uploadKey` or `sourceUploadId` on the first; `expectedHeadRevisionId` and `releaseNotes` given on the first are echoed into the second. Not for a static site: send its files in `files` or `attachments`, or drop them as the StartDeployWebsite recipe shows.",
|
|
23932
|
+
size: `Phase one, with \`prepare: true\` only, and required there: the build zip's length in bytes (\`wc -c < build.zip\`), 1 to ${MAX_BUNDLE_BYTES} (100 MB). The upload URL takes a body of exactly this length; a zip changed after the prepare needs a new prepare.`,
|
|
23933
|
+
uploadKey: "Phase two of the two-call upload: the `uploadKey` the `prepare` call returned, after the build zip was PUT to its URL. Releases that build to the site: {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. Excludes `files` and `attachments`.",
|
|
23934
|
+
sourceUploadId: "Phase two: the `sourceUploadId` the `prepare` call returned, after the source zip was PUT to its URL. Stores the project source with the release. Needs `uploadKey`; leave it out when the prepare returned no source upload.",
|
|
23935
|
+
expectedHeadRevisionId: "The repository revision your project started from, so the source replaces only that and a commit made since (another chat, a Dev Machine) is never dropped unseen: the revision you downloaded the project at; for a project started here, the `headRevisionId` the `prepare` call returned. Leave it out only for a repository with nothing in it yet. Goes with `uploadKey`, or with `files`/`attachments` holding a build.",
|
|
23936
|
+
releaseNotes: "Optional: one line on what this release changes, at most 200 characters, kept with the deployment of a Wix Astro build, whether it comes in `files`/`attachments` or as the second call with `uploadKey` (given on the `prepare` call it is echoed into the second). A static drop keeps no notes.",
|
|
23937
|
+
siteId: "The metaSiteId of the Wix Headless site to upload into: the one returned by the provision call of the StartDeployWebsite recipe for a new site, or of a site deployed earlier to replace its files. Every upload replaces the whole file set, so send all files; the URL stays the same. A build released here replaces static pages dropped earlier, at the same URL.",
|
|
22640
23938
|
calledStartDeployWebsite: "Whether you called StartDeployWebsite earlier in this conversation and are following the recipe it returned. Answer truthfully; it is recorded, not checked.",
|
|
22641
23939
|
agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
|
|
22642
|
-
reason:
|
|
23940
|
+
reason: `One sentence: the user request that led here and what is being published. ${NO_PERSONAL_DATA}`
|
|
22643
23941
|
};
|
|
23942
|
+
var UPLOAD_RECIPE_SKILL_URL = "https://www.wix.com/skills/wix-manage/references/sites/upload-static-site.md";
|
|
23943
|
+
var DOMAIN_RECIPE_SKILL_URL = "https://www.wix.com/skills/wix-manage/references/domains/domain-search-purchase-and-connect.md";
|
|
23944
|
+
function formatInlineGuide(guide) {
|
|
23945
|
+
if (!guide || !guide.trim()) return "";
|
|
23946
|
+
return [
|
|
23947
|
+
"",
|
|
23948
|
+
"---",
|
|
23949
|
+
"",
|
|
23950
|
+
`Wix's headless guide, the build of a site that needs Wix business features (also at https://www.wix.com/headless/skill.md):`,
|
|
23951
|
+
"",
|
|
23952
|
+
guide.trim()
|
|
23953
|
+
].join("\n");
|
|
23954
|
+
}
|
|
22644
23955
|
function formatDropResult(r) {
|
|
22645
23956
|
const shown = r.files.slice(0, 60);
|
|
22646
23957
|
return [
|
|
@@ -22650,10 +23961,203 @@ function formatDropResult(r) {
|
|
|
22650
23961
|
`Dashboard: https://manage.wix.com/dashboard/${r.metaSiteId}`,
|
|
22651
23962
|
`${r.files.length} file(s): ${shown.join(", ")}${r.files.length > shown.length ? ", \u2026" : ""}`,
|
|
22652
23963
|
"",
|
|
22653
|
-
|
|
22654
|
-
|
|
23964
|
+
`Give the user both links, each on its own line and named for what it is: the live site, and the site's dashboard. Then offer a custom domain, bought through Wix or one they already own (${DOMAIN_RECIPE_SKILL_URL}).`,
|
|
23965
|
+
"",
|
|
23966
|
+
`- To change this site later: "Change it later" in ${UPLOAD_RECIPE_SKILL_URL}, with this metaSiteId (${r.metaSiteId}). The same site every time, never a new one.`,
|
|
23967
|
+
`- When the site needs Wix business features (${WIX_BUSINESS_FEATURES}), a drop of static pages is not its build: a full Wix Headless site has those features run by Wix and its pages reading from them. That build is Wix's headless guide, https://www.wix.com/headless/skill.md (what StartHeadlessBuild returns; call it with startingPoint "an existing Wix Headless site", this site is one): it installs the solution on this site, seeds its content through the API, and puts up pages that fetch it, uploaded here again. A site with no business features is done.`
|
|
23968
|
+
].join("\n");
|
|
23969
|
+
}
|
|
23970
|
+
function curlPut(zipName, put, fallback) {
|
|
23971
|
+
const headers = Object.entries(
|
|
23972
|
+
put.headers && Object.keys(put.headers).length ? put.headers : fallback
|
|
23973
|
+
);
|
|
23974
|
+
return [
|
|
23975
|
+
"curl -X PUT",
|
|
23976
|
+
...headers.map(([k, v]) => `-H "${k}: ${v}"`),
|
|
23977
|
+
`--data-binary @${zipName} "${put.putUrl}"`
|
|
23978
|
+
].join(" ");
|
|
23979
|
+
}
|
|
23980
|
+
var BUILD_ZIP_CONTENTS = "Build zip, from the project root after `astro build` with the Wix hosting adapter: `dist/` (with `dist/_wix/app-manifest.json`) and `.wix/build-metadata.json`.";
|
|
23981
|
+
var SOURCE_ZIP_CONTENTS = "Source zip, from the project root: the project minus `node_modules`, `dist`, `.wix`, `.astro`, `.env*` and `.agents`.";
|
|
23982
|
+
function formatPrepareResult(siteId, urls, size, given = {}) {
|
|
23983
|
+
const expiry = urls.expiresAt ? ` They expire at ${urls.expiresAt}.` : "";
|
|
23984
|
+
const secondCall = {
|
|
23985
|
+
siteId,
|
|
23986
|
+
uploadKey: urls.uploadKey
|
|
23987
|
+
};
|
|
23988
|
+
const zipHeaders = {
|
|
23989
|
+
"content-type": "application/zip",
|
|
23990
|
+
...size ? { "content-length": String(size) } : {}
|
|
23991
|
+
};
|
|
23992
|
+
const lines = [
|
|
23993
|
+
`Upload URLs ready for site ${siteId}.${expiry} Two steps, then call this tool again.`,
|
|
23994
|
+
"",
|
|
23995
|
+
`1. ${BUILD_ZIP_CONTENTS} Its client folder (\`dist/client\`) holds at most ${MAX_RELEASE_STATIC_FILES} files (\`find dist/client -type f | wc -l\`); a larger build fails the release. Upload the zip you measured${size ? ` (${size} bytes)` : ""}; the URL takes a body of exactly that length, so a zip changed since needs a new prepare with its size:`,
|
|
23996
|
+
` ${curlPut("build.zip", urls, zipHeaders)}`
|
|
23997
|
+
];
|
|
23998
|
+
if (given.expectedHeadRevisionId)
|
|
23999
|
+
secondCall.expectedHeadRevisionId = given.expectedHeadRevisionId;
|
|
24000
|
+
if (urls.source) {
|
|
24001
|
+
const s = urls.source;
|
|
24002
|
+
secondCall.sourceUploadId = s.uploadId;
|
|
24003
|
+
if (!secondCall.expectedHeadRevisionId && s.headRevisionId)
|
|
24004
|
+
secondCall.expectedHeadRevisionId = s.headRevisionId;
|
|
24005
|
+
lines.push(
|
|
24006
|
+
"",
|
|
24007
|
+
`2. ${SOURCE_ZIP_CONTENTS} At most 64 MiB (\`wc -c < source.zip\`): a larger one fails the whole release. Leave large media and generated files out of it; if it is still larger, skip this upload and leave \`sourceUploadId\` out of the second call, and the site is released without its source. Upload it:`,
|
|
24008
|
+
` ${curlPut("source.zip", s, { "content-type": "application/zip" })}`,
|
|
24009
|
+
...s.expiresAt && s.expiresAt !== urls.expiresAt ? [` This URL expires at ${s.expiresAt}.`] : [],
|
|
24010
|
+
...s.headRevisionId ? [
|
|
24011
|
+
` The repository's head revision is ${s.headRevisionId}. The second call passes \`expectedHeadRevisionId\`: this head for a project started here; if you downloaded the project from the repository, the revision you downloaded it at instead, so a commit made since is never dropped.`
|
|
24012
|
+
] : []
|
|
24013
|
+
);
|
|
24014
|
+
} else {
|
|
24015
|
+
lines.push(
|
|
24016
|
+
"",
|
|
24017
|
+
"2. The site's repository takes no source this time: the project's source is not uploaded, and the second call carries no `sourceUploadId`."
|
|
24018
|
+
);
|
|
24019
|
+
}
|
|
24020
|
+
if (given.releaseNotes)
|
|
24021
|
+
secondCall.releaseNotes = releaseNotesForService(given.releaseNotes);
|
|
24022
|
+
lines.push(
|
|
24023
|
+
"",
|
|
24024
|
+
`3. Call UploadHeadlessWebsiteFiles again with ${JSON.stringify(secondCall)} (plus \`calledStartDeployWebsite\`, \`agentIdentity\`, \`reason\`). No \`files\` or \`attachments\` on that call.`
|
|
24025
|
+
);
|
|
24026
|
+
if (secondCall.sourceUploadId) {
|
|
24027
|
+
const withoutSource = { ...secondCall };
|
|
24028
|
+
delete withoutSource.sourceUploadId;
|
|
24029
|
+
lines.push(
|
|
24030
|
+
` If you skipped the source upload in step 2, call with ${JSON.stringify(withoutSource)} instead: a \`sourceUploadId\` whose zip was never uploaded fails the release.`
|
|
24031
|
+
);
|
|
24032
|
+
}
|
|
24033
|
+
return lines.join("\n");
|
|
24034
|
+
}
|
|
24035
|
+
function formatBundleReleaseResult(siteId, r, notes = []) {
|
|
24036
|
+
return [
|
|
24037
|
+
"Released; the site is live.",
|
|
24038
|
+
`Live URL: ${r.siteUrl ?? "(not returned)"}`,
|
|
24039
|
+
`metaSiteId: ${siteId} \u2014 pass it as \`siteId\` to release this site again, the same way. The URL stays.`,
|
|
24040
|
+
`Dashboard: https://manage.wix.com/dashboard/${siteId}`,
|
|
24041
|
+
`Deployment ${r.deploymentId ?? "(id not returned)"}.${r.revision?.id ? ` Source stored as revision ${r.revision.id}.` : ""}`,
|
|
24042
|
+
...notes,
|
|
24043
|
+
"",
|
|
24044
|
+
"This release replaced whatever the site served before, at the same URL. A change to the site is a change to the project: edit, build if it is an Astro project, upload again.",
|
|
24045
|
+
"Give the user both links, each on its own line and named for what it is: the live site, and the site's dashboard."
|
|
22655
24046
|
].join("\n");
|
|
22656
24047
|
}
|
|
24048
|
+
var startOver = (retry) => retry === "prepare" ? "call with `prepare: true` and the zip's `size` again, upload the zips to the new URLs, and release with the new `uploadKey`" : "call this tool again with the same files";
|
|
24049
|
+
var apiBody = (err) => err.response.data ? ` (${err.response.data.slice(0, 300)})` : "";
|
|
24050
|
+
var SITE_ACCESS_PERMISSION = "OAUTH_APP.APP_CREATE";
|
|
24051
|
+
function describeSiteAccessDenied(err, siteId, stagedKey) {
|
|
24052
|
+
if (!(err instanceof WixFetchError) || err.status !== 403 || !err.response.data.includes(SITE_ACCESS_PERMISSION)) {
|
|
24053
|
+
return null;
|
|
24054
|
+
}
|
|
24055
|
+
const site = siteId ? `site ${siteId}` : "this site";
|
|
24056
|
+
const outcome = stagedKey ? `Nothing was released; the build you uploaded is still staged under uploadKey ${stagedKey} and counts against the site's unreleased uploads, so do not call \`prepare\` again and do not send the files again. The same call fails the same way until the login changes; once connected as the owning account, release that staged upload by calling with \`uploadKey: "${stagedKey}"\` before preparing a new one.` : "Nothing was uploaded, and the same call fails the same way, so do not retry and do not stage another upload.";
|
|
24057
|
+
return `The connected Wix login has no rights on ${site}: Wix refused it the \`${SITE_ACCESS_PERMISSION}\` permission there. That means the site belongs to another Wix account, or this login's role on the owning account is too low (a Basic Team Member cannot upload). ${outcome} Ask the user which Wix account owns the site. Reconnecting Wix helps only if they pick that account, as its owner or an admin, at the consent screen; otherwise the owner has to give this login a higher role on the account, or move the site to it.${apiBody(err)}`;
|
|
24058
|
+
}
|
|
24059
|
+
function describeBundleUploadUrlError(err, siteId) {
|
|
24060
|
+
if (!(err instanceof WixFetchError)) {
|
|
24061
|
+
return err instanceof Error ? err.message : String(err);
|
|
24062
|
+
}
|
|
24063
|
+
const denied = describeSiteAccessDenied(err, siteId);
|
|
24064
|
+
if (denied) return denied;
|
|
24065
|
+
const body = apiBody(err);
|
|
24066
|
+
switch (err.status) {
|
|
24067
|
+
case 400:
|
|
24068
|
+
return `The upload URL was refused: \`size\` is the build zip's length in bytes, an integer from 1 to ${MAX_BUNDLE_BYTES} (100 MB). A larger build: leave out what the site does not serve (source maps, unused assets).${body}`;
|
|
24069
|
+
case 429:
|
|
24070
|
+
return `Too many unreleased uploads are waiting for this site: release one with its \`uploadKey\`, or wait for a running release to finish, then ask again.${body}`;
|
|
24071
|
+
default:
|
|
24072
|
+
return err.message;
|
|
24073
|
+
}
|
|
24074
|
+
}
|
|
24075
|
+
function deployAuthorization(siteHeaders) {
|
|
24076
|
+
const authorization = siteHeaders.Authorization ?? siteHeaders.authorization;
|
|
24077
|
+
return authorization ? { deployAuthorization: authorization } : {};
|
|
24078
|
+
}
|
|
24079
|
+
var RELEASE_NOTES_MAX_CHARS = 200;
|
|
24080
|
+
function releaseNotesForService(notes) {
|
|
24081
|
+
const chars = [...notes.replace(/\s+/g, " ").trim()];
|
|
24082
|
+
return chars.length <= RELEASE_NOTES_MAX_CHARS ? chars.join("") : `${chars.slice(0, RELEASE_NOTES_MAX_CHARS - 1).join("").trimEnd()}\u2026`;
|
|
24083
|
+
}
|
|
24084
|
+
function describeBundleReleaseError(err, uploadKey, retry = "prepare", siteId) {
|
|
24085
|
+
if (!(err instanceof WixFetchError)) {
|
|
24086
|
+
return err instanceof Error ? err.message : String(err);
|
|
24087
|
+
}
|
|
24088
|
+
const denied = describeSiteAccessDenied(err, siteId, uploadKey);
|
|
24089
|
+
if (denied) return denied;
|
|
24090
|
+
const body = apiBody(err);
|
|
24091
|
+
const code = err.response.data ?? "";
|
|
24092
|
+
const sameCall = `calling with \`uploadKey: "${uploadKey}"\`${retry === "resend" ? " (plus the `expectedHeadRevisionId` and `releaseNotes` you passed, if any; the source is stored with the next release)" : " and the same ids and `releaseNotes`"}`;
|
|
24093
|
+
const afterDrop = "If an earlier call with this `uploadKey` ended in a dropped connection, that release went through: the site is live with this build, so do not upload it again.";
|
|
24094
|
+
if (err.status >= 502 && err.status <= 504 && !code.includes("RELEASE_NOT_HELD") && !code.includes("{")) {
|
|
24095
|
+
return `The connection to Wix dropped while the release ran (HTTP ${err.status}); the release most likely went through. Do not call \`prepare\` again and do not send the files again. Wait two minutes, then check once by ${sameCall}: an answer that nothing is staged for this key, or that the repository moved, means the first release went through and the site is live. Any other answer is about the release itself.${body}`;
|
|
24096
|
+
}
|
|
24097
|
+
switch (err.status) {
|
|
24098
|
+
case 404:
|
|
24099
|
+
return `Nothing uploaded for uploadKey ${uploadKey}: the build zip was not PUT to its URL, the URL expired, or a release of it already went through (a staged zip is deleted once released). ${afterDrop} Otherwise, to try again, ${startOver(retry)}.${body}`;
|
|
24100
|
+
case 409:
|
|
24101
|
+
return `The project's source repository moved since the upload URLs were made; nothing was released by this call. ${afterDrop} Otherwise: ${retry === "prepare" ? "call with `prepare: true` and the zip's `size` again, re-upload the source zip to the new URL, and release with the new `uploadKey`, `sourceUploadId` and `expectedHeadRevisionId`." : "download the project again from the repository, carry your change over, and upload with `expectedHeadRevisionId` set to the revision you downloaded."}${body}`;
|
|
24102
|
+
case 403:
|
|
24103
|
+
return `Wix refused to deploy this build onto the site's project; nothing was released, and the same call will be refused again. Do not call \`prepare\` again. Tell the user the release is blocked on Wix's side and give them the request id from the error.${body}`;
|
|
24104
|
+
case 413:
|
|
24105
|
+
case 428:
|
|
24106
|
+
return `The build's server code is larger than the release can carry from here; nothing was released. Do not call \`prepare\` again. Tell the user the release is blocked on Wix's side and give them the request id from the error.${body}`;
|
|
24107
|
+
case 400:
|
|
24108
|
+
if (code.includes("EXPECTED_HEAD_REQUIRED")) {
|
|
24109
|
+
return `The site's repository already holds work, and the source replaces only what you started from; nothing was released. ${retry === "prepare" ? "Call with `prepare: true` and the zip's `size` again and pass the `headRevisionId` it returns as `expectedHeadRevisionId` (with its new `uploadKey` and `sourceUploadId`)." : "Call again with `expectedHeadRevisionId`: the revision you downloaded the project at, so the source replaces only that. To replace the repository with this upload on purpose, pass its current head."}${body}`;
|
|
24110
|
+
}
|
|
24111
|
+
if (code.includes("BUNDLE_TOO_LARGE")) {
|
|
24112
|
+
return `The build zip is over ${MAX_BUNDLE_BYTES} bytes (100 MB): leave out what the site does not serve (source maps, unused assets), then ${startOver(retry)}.${body}`;
|
|
24113
|
+
}
|
|
24114
|
+
if (code.includes("INVALID_RELEASE")) {
|
|
24115
|
+
return `The release was refused by Wix's deployment service; nothing was released. The staged upload is kept: call again with the same \`uploadKey\` and the field the message names fixed (release notes are cut to ${RELEASE_NOTES_MAX_CHARS} characters here).${body}`;
|
|
24116
|
+
}
|
|
24117
|
+
if (code.includes("INVALID_UPLOAD_KEY")) {
|
|
24118
|
+
return `\`uploadKey\` is the lowercase UUID the \`prepare\` call returned; pass it as it came.${body}`;
|
|
24119
|
+
}
|
|
24120
|
+
return `The uploaded zip was not accepted. Zip \`dist/\` and \`.wix/build-metadata.json\` from the project root after \`astro build\` with the Wix hosting adapter, then ${startOver(retry)}. A static site goes in \`files\` or \`attachments\` instead, or is dropped as the StartDeployWebsite recipe shows.${body}`;
|
|
24121
|
+
default:
|
|
24122
|
+
if (err.status >= 500) {
|
|
24123
|
+
const what = code.includes("RELEASE_NOT_HELD") ? "The release could not hold its upload" : `Wix's release step failed on its side (HTTP ${err.status})`;
|
|
24124
|
+
const limits = retry === "prepare" && !code.includes("RELEASE_NOT_HELD") ? ` A build over Wix's limits fails here with no reason given: more than ${MAX_RELEASE_STATIC_FILES} files in its client folder (\`find dist/client -type f | wc -l\`), or a source zip over 64 MiB (\`wc -c < source.zip\`). If either is over, a retry fails the same way: make the build or the source zip smaller, then call with \`prepare: true\` again.` : "";
|
|
24125
|
+
return `${what}; nothing was released.${limits} The staged upload is kept under uploadKey ${uploadKey}, so do not call \`prepare\` again${limits ? " for the same zips" : ""} and do not send the files again: every upload staged and not released counts against the site, which refuses new ones after a few. Retry once, after a minute, by ${sameCall}. If that fails too, stop and tell the user the release is blocked on Wix's side, with the request id from the error.${body}`;
|
|
24126
|
+
}
|
|
24127
|
+
return err.message;
|
|
24128
|
+
}
|
|
24129
|
+
}
|
|
24130
|
+
function validateBundleArgs(a) {
|
|
24131
|
+
const hasContent = !!a.files?.trim() || !!(a.attachments && a.attachments.length);
|
|
24132
|
+
if (a.prepare) {
|
|
24133
|
+
if (hasContent || a.uploadKey || a.sourceUploadId) {
|
|
24134
|
+
return "`prepare: true` goes with `siteId` and `size`: no `files`, `attachments`, `uploadKey` or `sourceUploadId` on that call. Example: {siteId, prepare: true, size}, then {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}.";
|
|
24135
|
+
}
|
|
24136
|
+
if (a.size === void 0) {
|
|
24137
|
+
return "`prepare: true` needs `size`: zip the build first and pass the zip's length in bytes (`wc -c < build.zip`).";
|
|
24138
|
+
}
|
|
24139
|
+
if (!Number.isInteger(a.size) || a.size < 1 || a.size > MAX_BUNDLE_BYTES) {
|
|
24140
|
+
return `\`size\` is the build zip's length in bytes, an integer from 1 to ${MAX_BUNDLE_BYTES} (100 MB); got ${a.size}.`;
|
|
24141
|
+
}
|
|
24142
|
+
return null;
|
|
24143
|
+
}
|
|
24144
|
+
if (a.size !== void 0) {
|
|
24145
|
+
return "`size` goes only with `prepare: true`: it is the length of the build zip that call makes an upload URL for.";
|
|
24146
|
+
}
|
|
24147
|
+
if (a.uploadKey) {
|
|
24148
|
+
if (hasContent) {
|
|
24149
|
+
return "`uploadKey` releases a build zip already uploaded to its URL: pass no `files` or `attachments` with it.";
|
|
24150
|
+
}
|
|
24151
|
+
return null;
|
|
24152
|
+
}
|
|
24153
|
+
if (a.sourceUploadId) {
|
|
24154
|
+
return "`sourceUploadId` goes with the `uploadKey` the `prepare` call returned.";
|
|
24155
|
+
}
|
|
24156
|
+
if (a.expectedHeadRevisionId && !hasContent) {
|
|
24157
|
+
return "`expectedHeadRevisionId` goes with `uploadKey`, or with `files`/`attachments` holding a build.";
|
|
24158
|
+
}
|
|
24159
|
+
return null;
|
|
24160
|
+
}
|
|
22657
24161
|
async function readJson(res, what) {
|
|
22658
24162
|
const text = await res.text();
|
|
22659
24163
|
if (!res.ok)
|
|
@@ -22664,6 +24168,65 @@ async function readJson(res, what) {
|
|
|
22664
24168
|
throw new Error(`${what} returned a non-JSON body: ${text.slice(0, 200)}`);
|
|
22665
24169
|
}
|
|
22666
24170
|
}
|
|
24171
|
+
async function putZip(fetchImpl, put, bytes, what) {
|
|
24172
|
+
const headers = Object.fromEntries(
|
|
24173
|
+
Object.entries(put.headers ?? { "content-type": "application/zip" }).filter(
|
|
24174
|
+
([k]) => k.toLowerCase() !== "content-length"
|
|
24175
|
+
)
|
|
24176
|
+
);
|
|
24177
|
+
const res = await fetchImpl(put.putUrl, {
|
|
24178
|
+
method: "PUT",
|
|
24179
|
+
headers,
|
|
24180
|
+
body: bytes
|
|
24181
|
+
});
|
|
24182
|
+
if (!res.ok) {
|
|
24183
|
+
const text = await res.text().catch(() => "");
|
|
24184
|
+
throw new Error(
|
|
24185
|
+
`The ${what} upload was refused (${res.status}${text ? `: ${text.slice(0, 200)}` : ""}); call this tool again with the same files.`
|
|
24186
|
+
);
|
|
24187
|
+
}
|
|
24188
|
+
}
|
|
24189
|
+
async function releaseBuildByUrl(o) {
|
|
24190
|
+
const log = o.log ?? (() => {
|
|
24191
|
+
});
|
|
24192
|
+
const headers = { ...o.siteHeaders, "Content-Type": "application/json" };
|
|
24193
|
+
const urls = await readJson(
|
|
24194
|
+
await o.fetchImpl(`${HEADLESS_BUSINESS}/${o.siteId}/bundle-upload-url`, {
|
|
24195
|
+
method: "POST",
|
|
24196
|
+
headers,
|
|
24197
|
+
body: JSON.stringify({ size: o.siteZip.byteLength })
|
|
24198
|
+
}),
|
|
24199
|
+
"bundle-upload-url"
|
|
24200
|
+
);
|
|
24201
|
+
if (!urls.uploadKey || !urls.putUrl) {
|
|
24202
|
+
throw new Error("bundle-upload-url returned no uploadKey or putUrl.");
|
|
24203
|
+
}
|
|
24204
|
+
await putZip(o.fetchImpl, urls, o.siteZip, "site zip");
|
|
24205
|
+
o.onStaged?.(urls.uploadKey);
|
|
24206
|
+
log(
|
|
24207
|
+
`staged ${urls.uploadKey} (${o.siteZip.byteLength} bytes), source ${urls.source ? `offered, ${o.sourceFiles.length} file(s)` : "not offered"}`
|
|
24208
|
+
);
|
|
24209
|
+
const source = urls.source && o.sourceFiles.length > 0 ? urls.source : void 0;
|
|
24210
|
+
if (source) {
|
|
24211
|
+
await putZip(o.fetchImpl, source, zipStored(o.sourceFiles), "source zip");
|
|
24212
|
+
}
|
|
24213
|
+
return await readJson(
|
|
24214
|
+
await o.fetchImpl(`${HEADLESS_BUSINESS}/${o.siteId}/release-bundle`, {
|
|
24215
|
+
method: "POST",
|
|
24216
|
+
headers,
|
|
24217
|
+
body: JSON.stringify({
|
|
24218
|
+
uploadKey: urls.uploadKey,
|
|
24219
|
+
...deployAuthorization(o.siteHeaders),
|
|
24220
|
+
...o.releaseNotes ? { releaseNotes: releaseNotesForService(o.releaseNotes) } : {},
|
|
24221
|
+
...source ? {
|
|
24222
|
+
sourceUploadId: source.uploadId,
|
|
24223
|
+
...o.expectedHeadRevisionId ? { expectedHeadRevisionId: o.expectedHeadRevisionId } : {}
|
|
24224
|
+
} : {}
|
|
24225
|
+
})
|
|
24226
|
+
}),
|
|
24227
|
+
"release-bundle"
|
|
24228
|
+
);
|
|
24229
|
+
}
|
|
22667
24230
|
function errorResult2(message) {
|
|
22668
24231
|
return {
|
|
22669
24232
|
isError: true,
|
|
@@ -22679,6 +24242,18 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22679
24242
|
const { authStrategy, toolDescriptions } = options;
|
|
22680
24243
|
const clientName = () => options.clientName ?? server.clientName?.() ?? server.server?.getClientVersion?.()?.name;
|
|
22681
24244
|
const fetchImpl = options.fetchImpl ?? ((u, i) => fetch(u, i));
|
|
24245
|
+
const guideFetch = options.guideFetch ?? (async (httpClient) => {
|
|
24246
|
+
if (!httpClient) return void 0;
|
|
24247
|
+
return fetchArticleContent(
|
|
24248
|
+
httpClient,
|
|
24249
|
+
HEADLESS_GUIDE_URL,
|
|
24250
|
+
"article",
|
|
24251
|
+
true,
|
|
24252
|
+
options.guide?.skillsPortalUrl,
|
|
24253
|
+
options.guide?.repo,
|
|
24254
|
+
options.guide?.commitHash
|
|
24255
|
+
);
|
|
24256
|
+
});
|
|
22682
24257
|
const d = UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS;
|
|
22683
24258
|
const registered = server.tool(
|
|
22684
24259
|
UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME,
|
|
@@ -22693,6 +24268,12 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22693
24268
|
mime_type: external_exports.string().optional()
|
|
22694
24269
|
})
|
|
22695
24270
|
).optional().describe(d.attachments),
|
|
24271
|
+
prepare: external_exports.boolean().optional().describe(d.prepare),
|
|
24272
|
+
size: external_exports.number().int().optional().describe(d.size),
|
|
24273
|
+
uploadKey: external_exports.string().min(1).optional().describe(d.uploadKey),
|
|
24274
|
+
sourceUploadId: external_exports.string().min(1).optional().describe(d.sourceUploadId),
|
|
24275
|
+
expectedHeadRevisionId: external_exports.string().min(1).optional().describe(d.expectedHeadRevisionId),
|
|
24276
|
+
releaseNotes: external_exports.string().optional().describe(d.releaseNotes),
|
|
22696
24277
|
siteId: external_exports.string().uuid("siteId must be a Wix metaSiteId (UUID)").describe(d.siteId),
|
|
22697
24278
|
calledStartDeployWebsite: external_exports.boolean().describe(d.calledStartDeployWebsite),
|
|
22698
24279
|
agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
|
|
@@ -22705,31 +24286,185 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22705
24286
|
const {
|
|
22706
24287
|
files,
|
|
22707
24288
|
attachments,
|
|
24289
|
+
prepare,
|
|
24290
|
+
size,
|
|
24291
|
+
uploadKey,
|
|
24292
|
+
sourceUploadId,
|
|
24293
|
+
expectedHeadRevisionId,
|
|
24294
|
+
releaseNotes,
|
|
22708
24295
|
siteId,
|
|
22709
24296
|
calledStartDeployWebsite,
|
|
22710
24297
|
agentIdentity,
|
|
22711
24298
|
reason
|
|
22712
24299
|
} = rawArgs;
|
|
24300
|
+
const phase = prepare ? "prepare" : uploadKey ? "release-bundle" : "files";
|
|
22713
24301
|
logger.log(
|
|
22714
|
-
`[UploadHeadlessWebsiteFiles] siteId=${siteId} calledStartDeployWebsite=${calledStartDeployWebsite} agent="${agentIdentity}" files=${files?.length ?? 0} chars attachments=${attachments?.length ?? 0} reason="${reason}"`
|
|
24302
|
+
`[UploadHeadlessWebsiteFiles] siteId=${siteId} phase=${phase} calledStartDeployWebsite=${calledStartDeployWebsite} agent="${redactPersonalData(agentIdentity)}" files=${files?.length ?? 0} chars attachments=${attachments?.length ?? 0} reason="${redactPersonalData(reason)}"`
|
|
22715
24303
|
);
|
|
24304
|
+
const invalid = validateBundleArgs(rawArgs);
|
|
24305
|
+
if (invalid) return errorResult2(invalid);
|
|
24306
|
+
if (prepare || uploadKey) {
|
|
24307
|
+
const siteHeaders = await authStrategy.getSiteAuthHeaders(siteId);
|
|
24308
|
+
const headers = {
|
|
24309
|
+
...siteHeaders,
|
|
24310
|
+
"Content-Type": "application/json"
|
|
24311
|
+
};
|
|
24312
|
+
try {
|
|
24313
|
+
extra?.setToolBiParams?.({ siteId });
|
|
24314
|
+
if (prepare) {
|
|
24315
|
+
const urls = await readJson(
|
|
24316
|
+
await fetchImpl(
|
|
24317
|
+
`${HEADLESS_BUSINESS}/${siteId}/bundle-upload-url`,
|
|
24318
|
+
{
|
|
24319
|
+
method: "POST",
|
|
24320
|
+
headers,
|
|
24321
|
+
body: JSON.stringify({ size })
|
|
24322
|
+
}
|
|
24323
|
+
),
|
|
24324
|
+
"bundle-upload-url"
|
|
24325
|
+
);
|
|
24326
|
+
if (!urls.uploadKey || !urls.putUrl) {
|
|
24327
|
+
throw new Error(
|
|
24328
|
+
"bundle-upload-url returned no uploadKey or putUrl."
|
|
24329
|
+
);
|
|
24330
|
+
}
|
|
24331
|
+
extra?.setToolBiParams?.({
|
|
24332
|
+
siteId,
|
|
24333
|
+
upload: { path: "prepare", sourceOffered: Boolean(urls.source) }
|
|
24334
|
+
});
|
|
24335
|
+
return {
|
|
24336
|
+
content: [
|
|
24337
|
+
{
|
|
24338
|
+
type: "text",
|
|
24339
|
+
text: formatPrepareResult(siteId, urls, size, {
|
|
24340
|
+
expectedHeadRevisionId,
|
|
24341
|
+
releaseNotes
|
|
24342
|
+
})
|
|
24343
|
+
}
|
|
24344
|
+
]
|
|
24345
|
+
};
|
|
24346
|
+
}
|
|
24347
|
+
const released = await readJson(
|
|
24348
|
+
await fetchImpl(`${HEADLESS_BUSINESS}/${siteId}/release-bundle`, {
|
|
24349
|
+
method: "POST",
|
|
24350
|
+
headers,
|
|
24351
|
+
body: JSON.stringify({
|
|
24352
|
+
uploadKey,
|
|
24353
|
+
...deployAuthorization(siteHeaders),
|
|
24354
|
+
...sourceUploadId ? { sourceUploadId } : {},
|
|
24355
|
+
...expectedHeadRevisionId ? { expectedHeadRevisionId } : {},
|
|
24356
|
+
...releaseNotes ? { releaseNotes: releaseNotesForService(releaseNotes) } : {}
|
|
24357
|
+
})
|
|
24358
|
+
}),
|
|
24359
|
+
"release-bundle"
|
|
24360
|
+
);
|
|
24361
|
+
extra?.setToolBiParams?.({
|
|
24362
|
+
siteId,
|
|
24363
|
+
upload: {
|
|
24364
|
+
path: "release-bundle",
|
|
24365
|
+
sourceStored: Boolean(released.revision?.id)
|
|
24366
|
+
}
|
|
24367
|
+
});
|
|
24368
|
+
return {
|
|
24369
|
+
content: [
|
|
24370
|
+
{
|
|
24371
|
+
type: "text",
|
|
24372
|
+
text: formatBundleReleaseResult(siteId, released)
|
|
24373
|
+
}
|
|
24374
|
+
]
|
|
24375
|
+
};
|
|
24376
|
+
} catch (err) {
|
|
24377
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
24378
|
+
logger.log(`[UploadHeadlessWebsiteFiles] ${phase} error: ${message}`);
|
|
24379
|
+
rethrowIfUnauthorized(err);
|
|
24380
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
24381
|
+
return errorResult2(
|
|
24382
|
+
uploadKey ? describeBundleReleaseError(err, uploadKey, "prepare", siteId) : describeBundleUploadUrlError(err, siteId)
|
|
24383
|
+
);
|
|
24384
|
+
}
|
|
24385
|
+
}
|
|
22716
24386
|
if (!files?.trim() && !(attachments && attachments.length)) {
|
|
22717
24387
|
return errorResult2(
|
|
22718
|
-
"Nothing to publish: pass the site in `files` (text bundle) or `attachments` (files from this chat or your sandbox)
|
|
24388
|
+
"Nothing to publish: pass the site in `files` (text bundle) or `attachments` (files from this chat or your sandbox), or, for a Wix Astro build from a shell that reaches the internet, zip it and call with `prepare: true` and its `size`."
|
|
22719
24389
|
);
|
|
22720
24390
|
}
|
|
22721
|
-
let siteFiles;
|
|
24391
|
+
let siteFiles = [];
|
|
24392
|
+
let uploaded = [];
|
|
24393
|
+
let given = [];
|
|
24394
|
+
let build = null;
|
|
22722
24395
|
try {
|
|
22723
24396
|
const fromAttachments = (await resolveAttachments(attachments, fetchImpl)).map((a) => ({ path: a.path, bytes: base64ToBytes(a.content) }));
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
|
|
24397
|
+
given = [...parseFileBundle(files), ...fromAttachments];
|
|
24398
|
+
uploaded = await expandSiteFiles(given);
|
|
24399
|
+
build = findHeadlessBuild(uploaded);
|
|
24400
|
+
if (!build) siteFiles = staticSiteFiles(uploaded);
|
|
22728
24401
|
} catch (e) {
|
|
22729
24402
|
if (e instanceof AttachmentError || e instanceof Error)
|
|
22730
24403
|
return errorResult2(e.message);
|
|
22731
24404
|
throw e;
|
|
22732
24405
|
}
|
|
24406
|
+
if (!build && expectedHeadRevisionId) {
|
|
24407
|
+
return errorResult2(
|
|
24408
|
+
"`expectedHeadRevisionId` applies to a Wix Astro build, whose source is stored in the site's repository; these files are a static site, which has none. Call again without it."
|
|
24409
|
+
);
|
|
24410
|
+
}
|
|
24411
|
+
if (build) {
|
|
24412
|
+
const siteHeaders = await authStrategy.getSiteAuthHeaders(siteId);
|
|
24413
|
+
let stagedKey;
|
|
24414
|
+
try {
|
|
24415
|
+
extra?.setToolBiParams?.({ siteId });
|
|
24416
|
+
const sourceFiles = buildSourceFiles(uploaded, build);
|
|
24417
|
+
const statics = withoutEmptyStatics(uploaded, build);
|
|
24418
|
+
const released = await releaseBuildByUrl({
|
|
24419
|
+
siteId,
|
|
24420
|
+
// One zip as given goes as is; anything else is zipped from the expanded files.
|
|
24421
|
+
siteZip: given.length === 1 && isZip(given[0]) && statics.dropped.length === 0 ? given[0].bytes : zipStored(statics.files),
|
|
24422
|
+
sourceFiles,
|
|
24423
|
+
expectedHeadRevisionId,
|
|
24424
|
+
releaseNotes,
|
|
24425
|
+
siteHeaders,
|
|
24426
|
+
fetchImpl,
|
|
24427
|
+
onStaged: (key) => {
|
|
24428
|
+
stagedKey = key;
|
|
24429
|
+
},
|
|
24430
|
+
log: (line) => logger.log(`[UploadHeadlessWebsiteFiles] release ${line}`)
|
|
24431
|
+
});
|
|
24432
|
+
const sourceStored = Boolean(released.revision?.id);
|
|
24433
|
+
extra?.setToolBiParams?.({
|
|
24434
|
+
siteId,
|
|
24435
|
+
upload: {
|
|
24436
|
+
path: "build",
|
|
24437
|
+
sourceStored,
|
|
24438
|
+
sourceFiles: sourceFiles.length,
|
|
24439
|
+
emptyStaticsDropped: statics.dropped.length
|
|
24440
|
+
}
|
|
24441
|
+
});
|
|
24442
|
+
const notes = [
|
|
24443
|
+
...statics.dropped.length ? [
|
|
24444
|
+
`Left out, empty: ${statics.dropped.slice(0, 20).join(", ")} (the hosting serves no file of size 0).`
|
|
24445
|
+
] : [],
|
|
24446
|
+
...sourceStored ? [] : [
|
|
24447
|
+
sourceFiles.length ? "The project's source was not stored: the site's repository took none this time." : "The project's source was not stored: the upload held only the build. A later chat can continue this site only from its source; next time zip the whole project from its root, leaving out node_modules, .env files and the caches."
|
|
24448
|
+
]
|
|
24449
|
+
];
|
|
24450
|
+
return {
|
|
24451
|
+
content: [
|
|
24452
|
+
{
|
|
24453
|
+
type: "text",
|
|
24454
|
+
text: formatBundleReleaseResult(siteId, released, notes)
|
|
24455
|
+
}
|
|
24456
|
+
]
|
|
24457
|
+
};
|
|
24458
|
+
} catch (err) {
|
|
24459
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
24460
|
+
logger.log(`[UploadHeadlessWebsiteFiles] release error: ${message}`);
|
|
24461
|
+
rethrowIfUnauthorized(err);
|
|
24462
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
24463
|
+
return errorResult2(
|
|
24464
|
+
stagedKey ? describeBundleReleaseError(err, stagedKey, "resend", siteId) : describeBundleUploadUrlError(err, siteId)
|
|
24465
|
+
);
|
|
24466
|
+
}
|
|
24467
|
+
}
|
|
22733
24468
|
const authHeaders = await authStrategy.getAccountAuthHeaders();
|
|
22734
24469
|
try {
|
|
22735
24470
|
const metaSiteId = siteId;
|
|
@@ -22746,6 +24481,18 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22746
24481
|
),
|
|
22747
24482
|
"drop"
|
|
22748
24483
|
);
|
|
24484
|
+
extra?.setToolBiParams?.({
|
|
24485
|
+
siteId: metaSiteId,
|
|
24486
|
+
upload: { path: "drop", files: siteFiles.length }
|
|
24487
|
+
});
|
|
24488
|
+
let guide;
|
|
24489
|
+
try {
|
|
24490
|
+
guide = await guideFetch(extra?.httpClient);
|
|
24491
|
+
} catch (e) {
|
|
24492
|
+
logger.log(
|
|
24493
|
+
`[UploadHeadlessWebsiteFiles] guide fetch failed: ${e instanceof Error ? e.message : String(e)}`
|
|
24494
|
+
);
|
|
24495
|
+
}
|
|
22749
24496
|
return {
|
|
22750
24497
|
content: [
|
|
22751
24498
|
{
|
|
@@ -22755,7 +24502,7 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22755
24502
|
siteUrl: dropped.siteUrl,
|
|
22756
24503
|
uploadId: dropped.uploadId,
|
|
22757
24504
|
files: siteFiles.map((f) => f.path)
|
|
22758
|
-
})
|
|
24505
|
+
}) + (releaseNotes ? "\n\nRelease notes are kept for a Wix Astro build only; a static drop has none, so yours were not stored." : "") + formatInlineGuide(guide)
|
|
22759
24506
|
}
|
|
22760
24507
|
]
|
|
22761
24508
|
};
|
|
@@ -22764,7 +24511,7 @@ function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
|
22764
24511
|
logger.log(`[UploadHeadlessWebsiteFiles] Error: ${message}`);
|
|
22765
24512
|
rethrowIfUnauthorized(err);
|
|
22766
24513
|
await authStrategy.enforceValidWixToken?.(err);
|
|
22767
|
-
return errorResult2(message);
|
|
24514
|
+
return errorResult2(describeSiteAccessDenied(err, siteId) ?? message);
|
|
22768
24515
|
}
|
|
22769
24516
|
}
|
|
22770
24517
|
);
|
|
@@ -22784,7 +24531,8 @@ function addSearchCodeModeTool(server, options) {
|
|
|
22784
24531
|
authStrategy,
|
|
22785
24532
|
disableTools = [],
|
|
22786
24533
|
toolDescriptions,
|
|
22787
|
-
maxResponseSize
|
|
24534
|
+
maxResponseSize,
|
|
24535
|
+
docsSite
|
|
22788
24536
|
} = options;
|
|
22789
24537
|
if (disableTools.includes(SEARCH_TOOL_NAME)) {
|
|
22790
24538
|
return;
|
|
@@ -22796,7 +24544,8 @@ function addSearchCodeModeTool(server, options) {
|
|
|
22796
24544
|
description,
|
|
22797
24545
|
maxResponseSize,
|
|
22798
24546
|
authStrategy,
|
|
22799
|
-
codeModeFetch
|
|
24547
|
+
codeModeFetch,
|
|
24548
|
+
docsSite
|
|
22800
24549
|
);
|
|
22801
24550
|
}
|
|
22802
24551
|
function addExecuteCodeModeTool(server, options) {
|
|
@@ -22822,6 +24571,7 @@ function addExecuteCodeModeTool(server, options) {
|
|
|
22822
24571
|
});
|
|
22823
24572
|
}
|
|
22824
24573
|
var SEARCH_MAX_RESPONSE_CHARS = 64e3;
|
|
24574
|
+
var EXECUTE_MAX_RESPONSE_CHARS = 1e5;
|
|
22825
24575
|
async function resolveLoopbackAuthorization(authStrategy) {
|
|
22826
24576
|
try {
|
|
22827
24577
|
return (await authStrategy?.getAccountAuthHeaders())?.Authorization;
|
|
@@ -22829,7 +24579,7 @@ async function resolveLoopbackAuthorization(authStrategy) {
|
|
|
22829
24579
|
return void 0;
|
|
22830
24580
|
}
|
|
22831
24581
|
}
|
|
22832
|
-
function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch = defaultCodeModeFetch) {
|
|
24582
|
+
function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch = defaultCodeModeFetch, docsSite) {
|
|
22833
24583
|
const searchUrl = `${codeModeBaseUrl}/api/code-mode/search`;
|
|
22834
24584
|
server.tool(
|
|
22835
24585
|
SEARCH_TOOL_NAME,
|
|
@@ -22855,7 +24605,8 @@ function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSiz
|
|
|
22855
24605
|
"Content-Type": "application/json",
|
|
22856
24606
|
...authorization ? { Authorization: authorization } : {}
|
|
22857
24607
|
},
|
|
22858
|
-
|
|
24608
|
+
// docsSite only when set: without it the request is as before.
|
|
24609
|
+
body: JSON.stringify(docsSite ? { code, docsSite } : { code })
|
|
22859
24610
|
});
|
|
22860
24611
|
if (!resp.ok) {
|
|
22861
24612
|
const errorBody = await resp.text();
|
|
@@ -22882,8 +24633,14 @@ function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSiz
|
|
|
22882
24633
|
]
|
|
22883
24634
|
};
|
|
22884
24635
|
}
|
|
24636
|
+
const result = isMintlifyDocsSite(docsSite) && data.result !== void 0 ? JSON.parse(
|
|
24637
|
+
(await rewriteDocsLinks(
|
|
24638
|
+
toDevWixHostText(JSON.stringify(data.result)),
|
|
24639
|
+
{ label: "search SearchWixAPISpec" }
|
|
24640
|
+
)).text
|
|
24641
|
+
) : data.result;
|
|
22885
24642
|
const text = truncateResponse(
|
|
22886
|
-
|
|
24643
|
+
result,
|
|
22887
24644
|
maxResponseSize ?? SEARCH_MAX_RESPONSE_CHARS
|
|
22888
24645
|
);
|
|
22889
24646
|
return {
|
|
@@ -22916,10 +24673,13 @@ async function postExecute(executeUrl, body, authorization, codeModeFetch = defa
|
|
|
22916
24673
|
}
|
|
22917
24674
|
return await resp.json();
|
|
22918
24675
|
}
|
|
22919
|
-
function executeErrorResult(message) {
|
|
24676
|
+
function executeErrorResult(message, failedCalls) {
|
|
22920
24677
|
return {
|
|
22921
24678
|
isError: true,
|
|
22922
|
-
content: [{ type: "text", text: `Execute error: ${message}` }]
|
|
24679
|
+
content: [{ type: "text", text: `Execute error: ${message}` }],
|
|
24680
|
+
// The failed calls as fields, request ids included. The text carries the
|
|
24681
|
+
// ids too, for clients that don't read structured results.
|
|
24682
|
+
...failedCalls?.length ? { structuredContent: { failedCalls } } : {}
|
|
22923
24683
|
};
|
|
22924
24684
|
}
|
|
22925
24685
|
function executeTextResult(message) {
|
|
@@ -22929,7 +24689,7 @@ function executeTextResult(message) {
|
|
|
22929
24689
|
}
|
|
22930
24690
|
function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
22931
24691
|
if (data.error) {
|
|
22932
|
-
return executeErrorResult(data.error);
|
|
24692
|
+
return executeErrorResult(data.error, data.failedCalls);
|
|
22933
24693
|
}
|
|
22934
24694
|
reportBi?.({
|
|
22935
24695
|
originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
|
|
@@ -22949,7 +24709,13 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
|
22949
24709
|
}
|
|
22950
24710
|
return {
|
|
22951
24711
|
content: [
|
|
22952
|
-
{
|
|
24712
|
+
{
|
|
24713
|
+
type: "text",
|
|
24714
|
+
text: truncateResponse(
|
|
24715
|
+
data.result,
|
|
24716
|
+
maxResponseSize ?? EXECUTE_MAX_RESPONSE_CHARS
|
|
24717
|
+
)
|
|
24718
|
+
}
|
|
22953
24719
|
]
|
|
22954
24720
|
};
|
|
22955
24721
|
}
|
|
@@ -23011,7 +24777,9 @@ function addExecuteTool(server, options) {
|
|
|
23011
24777
|
mime_type: external_exports.string().optional()
|
|
23012
24778
|
})
|
|
23013
24779
|
).optional().describe(
|
|
23014
|
-
|
|
24780
|
+
fileParamNote(
|
|
24781
|
+
"use `files`, or upload from your shell if it reaches the internet"
|
|
24782
|
+
) + ' Any type: a zip of a site, HTML, images, PDFs. The bytes are fetched here and appended to the `files` global as { path, content, encoding: "base64" } entries (path = file_name), so `wix.multipart()` and `wix.unzip()` work on them unchanged. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.'
|
|
23015
24783
|
),
|
|
23016
24784
|
// Visitor mode takes an agent-supplied token and pins the site; account/site
|
|
23017
24785
|
// mode lets the agent pick the site.
|
|
@@ -23052,7 +24820,11 @@ function addExecuteTool(server, options) {
|
|
|
23052
24820
|
}
|
|
23053
24821
|
let attachmentFiles = [];
|
|
23054
24822
|
try {
|
|
23055
|
-
attachmentFiles = await resolveAttachments(
|
|
24823
|
+
attachmentFiles = await resolveAttachments(
|
|
24824
|
+
attachments,
|
|
24825
|
+
void 0,
|
|
24826
|
+
WIX_AI_ASSISTANT_ATTACHMENT_HOSTS
|
|
24827
|
+
);
|
|
23056
24828
|
} catch (e) {
|
|
23057
24829
|
if (e instanceof AttachmentError) return executeErrorResult(e.message);
|
|
23058
24830
|
throw e;
|
|
@@ -23084,6 +24856,7 @@ function addExecuteTool(server, options) {
|
|
|
23084
24856
|
sessionId,
|
|
23085
24857
|
toolInvocationId,
|
|
23086
24858
|
clientName: server.server?.getClientVersion?.()?.name,
|
|
24859
|
+
...visitor ? { visitorMode: true } : {},
|
|
23087
24860
|
// A visitor token is a site-scoped bearer token: sent as siteToken
|
|
23088
24861
|
// (→ Authorization + wix-site-id) and as accountToken only to satisfy
|
|
23089
24862
|
// the runtime's required field. Account/site mode uses strategy tokens.
|
|
@@ -23358,6 +25131,7 @@ function createWixToolkit(options) {
|
|
|
23358
25131
|
readmeDocs,
|
|
23359
25132
|
authStrategy,
|
|
23360
25133
|
dynamicContextEnabled: true,
|
|
25134
|
+
includeCompanionApp: true,
|
|
23361
25135
|
// README-free defaults first so consumer overrides still win.
|
|
23362
25136
|
toolDescriptions: {
|
|
23363
25137
|
...descriptionsWithoutReadme,
|
|
@@ -23368,6 +25142,7 @@ function createWixToolkit(options) {
|
|
|
23368
25142
|
addDocsTools(server, void 0, {
|
|
23369
25143
|
authStrategy,
|
|
23370
25144
|
dynamicContextEnabled: true,
|
|
25145
|
+
includeCompanionApp: true,
|
|
23371
25146
|
toolDescriptions: descriptionsWithoutReadme
|
|
23372
25147
|
});
|
|
23373
25148
|
}
|
|
@@ -23408,7 +25183,9 @@ function createWixToolkit(options) {
|
|
|
23408
25183
|
authStrategy,
|
|
23409
25184
|
disableTools,
|
|
23410
25185
|
toolDescriptions: effectiveToolDescriptions,
|
|
23411
|
-
maxResponseSize
|
|
25186
|
+
maxResponseSize,
|
|
25187
|
+
// The connection's docs site (`docs.docsSite`) applies to spec search too.
|
|
25188
|
+
docsSite: docs?.docsSite
|
|
23412
25189
|
});
|
|
23413
25190
|
}
|
|
23414
25191
|
if (requestedTools.has("ExecuteWixAPI") && !disableTools?.includes("ExecuteWixAPI")) {
|
|
@@ -23425,7 +25202,13 @@ function createWixToolkit(options) {
|
|
|
23425
25202
|
addUploadHeadlessWebsiteFilesTool(server, {
|
|
23426
25203
|
authStrategy,
|
|
23427
25204
|
toolDescriptions: effectiveToolDescriptions,
|
|
23428
|
-
clientName
|
|
25205
|
+
clientName,
|
|
25206
|
+
// The headless guide the drop reply carries is read the way the docs tools read it.
|
|
25207
|
+
guide: {
|
|
25208
|
+
skillsPortalUrl: docs?.skillsPortalUrl,
|
|
25209
|
+
repo: docs?.repo ?? docs?.skillsRepo,
|
|
25210
|
+
commitHash: docs?.commitHash ?? docs?.skillsPr
|
|
25211
|
+
}
|
|
23429
25212
|
});
|
|
23430
25213
|
}
|
|
23431
25214
|
const requestedGraphqlTools = GRAPHQL_TOOL_NAMES.filter(
|
|
@@ -23488,7 +25271,7 @@ function createWixToolkit(options) {
|
|
|
23488
25271
|
biLogger.updateDefaults({ _uuid: "" });
|
|
23489
25272
|
panorama.transaction(toolName).start({
|
|
23490
25273
|
toolInvocationId,
|
|
23491
|
-
params:
|
|
25274
|
+
params: serializeToolParamsForBi(mergedArgs)
|
|
23492
25275
|
});
|
|
23493
25276
|
}
|
|
23494
25277
|
const startTime = performance.now();
|
|
@@ -23508,7 +25291,8 @@ function createWixToolkit(options) {
|
|
|
23508
25291
|
cbResult,
|
|
23509
25292
|
{
|
|
23510
25293
|
returnedRows: toolBiParams.returnedRows,
|
|
23511
|
-
originalLength: toolBiParams.originalLength
|
|
25294
|
+
originalLength: toolBiParams.originalLength,
|
|
25295
|
+
upload: toolBiParams.upload
|
|
23512
25296
|
},
|
|
23513
25297
|
{ name: clientName }
|
|
23514
25298
|
);
|
|
@@ -23523,7 +25307,7 @@ function createWixToolkit(options) {
|
|
|
23523
25307
|
context: {
|
|
23524
25308
|
toolName,
|
|
23525
25309
|
toolInvocationId,
|
|
23526
|
-
params:
|
|
25310
|
+
params: serializeToolParamsForBi(mergedArgs),
|
|
23527
25311
|
error: JSON.stringify(cbResult, null, 2)
|
|
23528
25312
|
}
|
|
23529
25313
|
}
|
|
@@ -23541,7 +25325,7 @@ function createWixToolkit(options) {
|
|
|
23541
25325
|
context: {
|
|
23542
25326
|
toolName,
|
|
23543
25327
|
toolInvocationId,
|
|
23544
|
-
params:
|
|
25328
|
+
params: serializeToolParamsForBi(mergedArgs),
|
|
23545
25329
|
error: errorBody
|
|
23546
25330
|
}
|
|
23547
25331
|
});
|
|
@@ -23569,7 +25353,7 @@ function createWixToolkit(options) {
|
|
|
23569
25353
|
errorBody,
|
|
23570
25354
|
toolInvocationId,
|
|
23571
25355
|
toolName,
|
|
23572
|
-
params:
|
|
25356
|
+
params: serializeToolParamsForBi(mergedArgs),
|
|
23573
25357
|
origin: nodeEnv,
|
|
23574
25358
|
sessionId,
|
|
23575
25359
|
executionResult: toolSucceeded ? "Success" : "Error",
|
|
@@ -23617,7 +25401,8 @@ function createWixToolkit(options) {
|
|
|
23617
25401
|
const httpClient = new import_http_client3.HttpClient();
|
|
23618
25402
|
return await getDynamicContext(authStrategy, httpClient, defaults, {
|
|
23619
25403
|
partnerContextEnabled: true,
|
|
23620
|
-
includeAccountContext: true
|
|
25404
|
+
includeAccountContext: true,
|
|
25405
|
+
includeCompanionApp: true
|
|
23621
25406
|
}) || null;
|
|
23622
25407
|
} catch {
|
|
23623
25408
|
return null;
|