@wix/mcp 1.0.92 → 1.0.94
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/build/bin-standalone.js +180 -212
- package/build/cjs/index.cjs +757 -216
- package/build/cjs/toolkit.cjs +725 -209
- package/build/dts/api-call/index.js +6 -3
- package/build/dts/api-call/site-files.d.ts +28 -0
- package/build/dts/api-call/site-files.js +194 -0
- package/build/dts/api-call/upload-headless-website-files.d.ts +35 -0
- package/build/dts/api-call/upload-headless-website-files.js +171 -0
- package/build/dts/bin.js +3 -6
- package/build/dts/code-mode/attachments.d.ts +37 -0
- package/build/dts/code-mode/attachments.js +218 -0
- package/build/dts/code-mode/index.js +53 -6
- package/build/dts/config/default-config.d.ts +0 -4
- package/build/dts/config/default-config.js +48 -56
- package/build/dts/config/param-descriptions.js +5 -5
- package/build/dts/docs/docs.d.ts +4 -2
- package/build/dts/docs/docs.js +25 -15
- package/build/dts/docs/fetch-article.js +4 -1
- package/build/dts/docs/get-to-know.js +1 -1
- package/build/dts/docs/semanticSearch.d.ts +2 -1
- package/build/dts/docs/semanticSearch.js +12 -5
- package/build/dts/docs/start-headless-build.d.ts +19 -0
- package/build/dts/docs/start-headless-build.js +74 -0
- package/build/dts/graphql/index.js +3 -1
- package/build/dts/import-claude-design-tool/index.js +2 -2
- package/build/dts/index.d.ts +1 -0
- package/build/dts/index.js +1 -0
- package/build/dts/site-widget-tools/search-site-templates-tool/index.js +17 -7
- package/build/dts/toolkit-entry.d.cts +4 -2
- package/build/dts/toolkit-entry.d.ts +4 -2
- package/build/dts/toolkit.js +9 -0
- package/build/dts/wix-mcp-server.d.ts +2 -0
- package/build/dts/wix-mcp-server.js +4 -0
- package/build/esm/index.js +755 -216
- package/build/esm/toolkit.js +725 -209
- package/package.json +2 -2
- package/build/dts/docs/create-site-guide.d.ts +0 -3
- package/build/dts/docs/create-site-guide.js +0 -109
package/build/esm/toolkit.js
CHANGED
|
@@ -10714,9 +10714,12 @@ var runSemanticSearch = async (toolName, toolParams, maxResults = 20, rerank = f
|
|
|
10714
10714
|
);
|
|
10715
10715
|
return data?.results;
|
|
10716
10716
|
};
|
|
10717
|
-
var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult) => {
|
|
10717
|
+
var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult, extraDocumentTypes) => {
|
|
10718
10718
|
const startTime = Date.now();
|
|
10719
|
-
|
|
10719
|
+
const documentTypes = extraDocumentTypes?.length ? [toolName, ...extraDocumentTypes] : void 0;
|
|
10720
|
+
logger.log(
|
|
10721
|
+
`[SemanticSearch/v2] Tool: ${documentTypes ? documentTypes.join("+") : toolName}, Query: "${searchTerm}"`
|
|
10722
|
+
);
|
|
10720
10723
|
const response = await fetch(
|
|
10721
10724
|
"https://www.wixapis.com/mcp-docs-search/v1/docs/search/markdown",
|
|
10722
10725
|
{
|
|
@@ -10727,7 +10730,7 @@ var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20
|
|
|
10727
10730
|
},
|
|
10728
10731
|
body: JSON.stringify({
|
|
10729
10732
|
search_term: searchTerm,
|
|
10730
|
-
document_type: toolName,
|
|
10733
|
+
...documentTypes ? { document_types: documentTypes } : { document_type: toolName },
|
|
10731
10734
|
maximum_results: maxResults,
|
|
10732
10735
|
lines_in_each_result: linesInEachResult
|
|
10733
10736
|
})
|
|
@@ -10756,14 +10759,16 @@ var runSemanticSearchAndFormat = async ({
|
|
|
10756
10759
|
maxResults = DEFAULT_MAX_RESULTS,
|
|
10757
10760
|
rerank,
|
|
10758
10761
|
linesInEachResult,
|
|
10759
|
-
usePlatformizedSearch = true
|
|
10762
|
+
usePlatformizedSearch = true,
|
|
10763
|
+
extraDocumentTypes
|
|
10760
10764
|
}) => {
|
|
10761
10765
|
if (usePlatformizedSearch) {
|
|
10762
10766
|
return runPlatformizedSemanticSearch(
|
|
10763
10767
|
toolName,
|
|
10764
10768
|
toolParams.searchTerm,
|
|
10765
10769
|
maxResults,
|
|
10766
|
-
linesInEachResult
|
|
10770
|
+
linesInEachResult,
|
|
10771
|
+
extraDocumentTypes
|
|
10767
10772
|
);
|
|
10768
10773
|
} else {
|
|
10769
10774
|
const results = await runSemanticSearch(
|
|
@@ -11697,7 +11702,7 @@ async function fetchArticle(articleUrl) {
|
|
|
11697
11702
|
}
|
|
11698
11703
|
var DOCS_URL_PREFIX = "https://dev.wix.com/docs/";
|
|
11699
11704
|
var SKILLS_REGISTRY_URL = new RegExp(
|
|
11700
|
-
"^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)"
|
|
11705
|
+
"^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)|^https://www\\.wix\\.com/headless/"
|
|
11701
11706
|
);
|
|
11702
11707
|
function isSkillsRegistryUrl(url) {
|
|
11703
11708
|
return SKILLS_REGISTRY_URL.test(url.trim());
|
|
@@ -12122,10 +12127,8 @@ function buildSystemReminderGuidelines(includeReadme, includeImageUploadGuidance
|
|
|
12122
12127
|
if (includeReadme) {
|
|
12123
12128
|
lines.push(
|
|
12124
12129
|
"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.",
|
|
12125
|
-
"**
|
|
12126
|
-
"
|
|
12127
|
-
"If the user \u2014 or you, earlier in this conversation \u2014 already have a finished page or site in any form (HTML/CSS/JS files, a static build, a zip, another builder's export, a design you can export to HTML) and want it live on Wix, call StartDeployWebsite right away \u2014 not a site-creation tool, not the docs.",
|
|
12128
|
-
"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 normal site creation through ManageWixSite, CallWixSiteAPI, or ExecuteWixAPI."
|
|
12130
|
+
"**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.",
|
|
12131
|
+
"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."
|
|
12129
12132
|
);
|
|
12130
12133
|
}
|
|
12131
12134
|
lines.push(
|
|
@@ -12173,9 +12176,8 @@ function buildSystemReminder(includeReadme, includeImageUploadGuidance = true) {
|
|
|
12173
12176
|
);
|
|
12174
12177
|
const flows = buildSystemReminderFlows(includeReadme);
|
|
12175
12178
|
return dedent_default`
|
|
12176
|
-
<
|
|
12177
|
-
|
|
12178
|
-
A FAILURE TO DO SO WILL RESULT IN ERRORS AND CRITICAL ISSUES.
|
|
12179
|
+
<wix-usage-guide>
|
|
12180
|
+
How to work with Wix through these tools. Following these steps avoids failed or incorrect API calls.
|
|
12179
12181
|
<goal>
|
|
12180
12182
|
${AGENT_GOAL}
|
|
12181
12183
|
</goal>
|
|
@@ -12185,7 +12187,7 @@ function buildSystemReminder(includeReadme, includeImageUploadGuidance = true) {
|
|
|
12185
12187
|
<flow-description>
|
|
12186
12188
|
${flows}
|
|
12187
12189
|
</flow-description>
|
|
12188
|
-
</
|
|
12190
|
+
</wix-usage-guide>
|
|
12189
12191
|
`;
|
|
12190
12192
|
}
|
|
12191
12193
|
var SYSTEM_REMINDER = buildSystemReminder(true);
|
|
@@ -12197,18 +12199,14 @@ var SITE_CREATION_COPY = {
|
|
|
12197
12199
|
unstatedMethod: {
|
|
12198
12200
|
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",
|
|
12199
12201
|
widget: "you MUST call `WixSiteCreation` and let them choose"
|
|
12200
|
-
},
|
|
12201
|
-
normalCreationRoute: {
|
|
12202
|
-
nonWidget: "route those through `WixSiteBuilder` or `SearchSiteTemplates`",
|
|
12203
|
-
widget: "route those through `WixSiteCreation`"
|
|
12204
12202
|
}
|
|
12205
12203
|
};
|
|
12206
12204
|
var SITE_GUIDANCE = dedent_default`
|
|
12207
|
-
First decide
|
|
12208
|
-
-
|
|
12209
|
-
-
|
|
12205
|
+
First decide who keeps building the site:
|
|
12206
|
+
- **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.
|
|
12207
|
+
- **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.
|
|
12210
12208
|
|
|
12211
|
-
For a new site 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.
|
|
12209
|
+
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.
|
|
12212
12210
|
|
|
12213
12211
|
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.
|
|
12214
12212
|
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.
|
|
@@ -12228,6 +12226,7 @@ var toWidget = (text) => Object.values(SITE_CREATION_COPY).reduce(
|
|
|
12228
12226
|
);
|
|
12229
12227
|
var EXECUTE_WIX_API_INTRO = dedent_default`
|
|
12230
12228
|
Execute JavaScript code against the Wix REST API when code is useful for chaining related calls, pagination, loops, branching, or in-memory transformation.
|
|
12229
|
+
Putting a site live or changing a headless site's files starts with StartDeployWebsite; building a new site in code with StartHeadlessBuild.
|
|
12231
12230
|
|
|
12232
12231
|
CODE CONTRACT:
|
|
12233
12232
|
- Pass an async function expression: \`async function() { ... }\` or \`async () => { ... }\`. Put every declaration, \`await\`, and \`return\` inside it, and do not invoke it yourself.
|
|
@@ -12305,13 +12304,14 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
12305
12304
|
unzip(input: string | Uint8Array): Promise<Array<{ path: string; bytes: Uint8Array; text(): string }>>;
|
|
12306
12305
|
};
|
|
12307
12306
|
declare const siteId: string | undefined;
|
|
12308
|
-
// Files from the ExecuteWixAPI \`files\` param
|
|
12309
|
-
//
|
|
12310
|
-
//
|
|
12307
|
+
// Files from the ExecuteWixAPI \`files\` param, followed by the resolved
|
|
12308
|
+
// \`attachments\`. Text entries have \`content\`; an entry tagged
|
|
12309
|
+
// \`=== FILE: <path> base64 ===\` — and every attachment, path = its file name —
|
|
12310
|
+
// has encoding:'base64' and base64 \`content\`.
|
|
12311
12311
|
declare const files: Array<{ path: string; content: string; encoding?: "base64" }>;
|
|
12312
12312
|
\`\`\`
|
|
12313
12313
|
|
|
12314
|
-
FILE UPLOADS:
|
|
12314
|
+
FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments\` works in ChatGPT only: pass files attached in the chat or created in your sandbox (any type, up to 10 MB per call: a zip of a whole site, HTML, images, PDFs) as you have them, by file id or /mnt/data path, all in one call. ChatGPT swaps each for \`{ download_url, file_id, file_name?, mime_type? }\` before the call reaches Wix, and the bytes arrive as base64 entries named by file name. Other clients send the value unchanged and the call fails before running; there, leave \`attachments\` out, or upload from your shell if it reaches the internet. 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):
|
|
12315
12315
|
\`\`\`javascript
|
|
12316
12316
|
// files param:
|
|
12317
12317
|
// === FILE: index.html ===
|
|
@@ -12326,6 +12326,12 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
12326
12326
|
return res.data;
|
|
12327
12327
|
}
|
|
12328
12328
|
\`\`\`
|
|
12329
|
+
A site zip passed through \`attachments\` is one base64 entry; unzip it and upload the entries:
|
|
12330
|
+
\`\`\`javascript
|
|
12331
|
+
// attachments: [site.zip] → files = [{ path: "site.zip", content: "<base64>", encoding: "base64" }]
|
|
12332
|
+
const entries = await wix.unzip(files[0].content); // [{ path, bytes, text() }]
|
|
12333
|
+
const mp = wix.multipart(entries);
|
|
12334
|
+
\`\`\`
|
|
12329
12335
|
DOWNLOAD + EDIT + RE-UPLOAD: fetch the zip as base64, \`wix.unzip()\` it, edit text via \`entry.text()\`, keep binary as bytes, and hand the entries back to \`wix.multipart()\`:
|
|
12330
12336
|
\`\`\`javascript
|
|
12331
12337
|
const r = await wix.request({ scope: "account", method: "GET", responseType: "base64", url: zipUrl });
|
|
@@ -12466,7 +12472,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
12466
12472
|
ReadFullDocsArticle: dedent_default`
|
|
12467
12473
|
Fetches the full Wix docs article or method article with code examples for using the method.
|
|
12468
12474
|
Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
|
|
12469
|
-
Also reads Wix skills pages — https://www.wix.com/skills/<skill>, https://dev.wix.com/docs/skills/<skill>, and the files they link (e.g. .../<skill>/references/<file>.md) — exactly as served.
|
|
12475
|
+
Also reads Wix skills pages — https://www.wix.com/skills/<skill>, https://dev.wix.com/docs/skills/<skill>, and the files they link (e.g. .../<skill>/references/<file>.md) — and Wix Headless agent pages under https://www.wix.com/headless/ (e.g. https://www.wix.com/headless/skill.md), exactly as served.
|
|
12470
12476
|
Take the URL from the user's message or an earlier tool result. To reach an article you have no URL for, search or browse the docs first.
|
|
12471
12477
|
For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
|
|
12472
12478
|
${reminder}
|
|
@@ -12514,7 +12520,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
12514
12520
|
${reminder}
|
|
12515
12521
|
`,
|
|
12516
12522
|
ManageWixSite: dedent_default`
|
|
12517
|
-
Use account level API in order to
|
|
12523
|
+
Use account level API in order to update a site.
|
|
12518
12524
|
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\`.
|
|
12519
12525
|
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"}]).
|
|
12520
12526
|
The API endpoint url param MUST ALWAYS be taken from the conversation context.
|
|
@@ -12530,12 +12536,12 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
12530
12536
|
NEVER call this tool with a missing, empty, or guessed siteId — ask first instead.
|
|
12531
12537
|
|
|
12532
12538
|
Parameters — provide image input via image and/or imageUrls, OR imageBase64:
|
|
12533
|
-
• image (array): chat file ATTACHMENTS. Each item is an object with download_url and file_id (both required), plus optional file_name and mime_type. Pass ALL attachments in one call.
|
|
12539
|
+
• image (array): chat file ATTACHMENTS — files the user attached, or files you created in your own file sandbox. Each item is an object with download_url and file_id (both required), plus optional file_name and mime_type. Pass ALL attachments in one call.
|
|
12534
12540
|
• 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.
|
|
12535
12541
|
• imageBase64 (string): base64-encoded image + mimeType. One image at a time. Do NOT combine with image/imageUrls.
|
|
12536
12542
|
|
|
12537
12543
|
Delivering the bytes — this is where uploads most often fail:
|
|
12538
|
-
•
|
|
12544
|
+
• Pass a file as a file, in image: the platform resolves it into download_url and file_id before it reaches this tool — this includes files you created in your sandbox. This server does not share your filesystem, so a bare path string (e.g. /mnt/..., /home/user/..., /Users/...) is not a file. Without a file to pass, use a public http(s) URL (imageUrls) or the complete base64 (imageBase64).
|
|
12539
12545
|
• 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.
|
|
12540
12546
|
`,
|
|
12541
12547
|
SupportAndFeedback: dedent_default`
|
|
@@ -12593,14 +12599,15 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
12593
12599
|
|
|
12594
12600
|
Do not call this if the user explicitly asks to build with AI; call \`WixSiteBuilder\` instead.
|
|
12595
12601
|
Do not call this if the user asks for templates or Wix Studio; call \`SearchSiteTemplates\` instead.
|
|
12602
|
+
Not for a site the user wants you to build in code, a design you worked out together, or an app: call \`StartHeadlessBuild\`.
|
|
12596
12603
|
|
|
12597
12604
|
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).
|
|
12598
12605
|
|
|
12599
12606
|
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.
|
|
12600
12607
|
`,
|
|
12601
12608
|
"import-claude-design-from-url": dedent_default`
|
|
12602
|
-
Import a design created in Claude's design tool into Wix.
|
|
12603
|
-
NOT for
|
|
12609
|
+
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.
|
|
12610
|
+
NOT for claude.ai share or artifact links, GitHub, Drive, chatgpt.com or any other URL, and not for files: those go to StartDeployWebsite, which returns the recipe to publish the files yourself.
|
|
12604
12611
|
`,
|
|
12605
12612
|
RunWixCliCommand: dedent_default`
|
|
12606
12613
|
Executes Wix CLI commands for Wix app development and management.
|
|
@@ -13037,23 +13044,23 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
13037
13044
|
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.
|
|
13038
13045
|
Never default to this tool: if the user hasn't explicitly named AI, don't call this tool — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.
|
|
13039
13046
|
|
|
13040
|
-
For templates or Wix Studio, call \`SearchSiteTemplates
|
|
13047
|
+
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\`.
|
|
13041
13048
|
|
|
13042
13049
|
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.)
|
|
13043
13050
|
`,
|
|
13044
13051
|
SearchSiteTemplates: dedent_default`
|
|
13045
13052
|
${SITE_GUIDANCE}
|
|
13046
13053
|
|
|
13047
|
-
Searches Wix templates and shows the template gallery, from the Harmony or the
|
|
13054
|
+
Searches Wix templates and shows the template gallery, from the Harmony, the Studio or the Classic Editor template source.
|
|
13048
13055
|
|
|
13049
|
-
Call this ONLY when the user's own words ask for a template
|
|
13056
|
+
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.
|
|
13050
13057
|
${SITE_CREATION_COPY.templatePick.nonWidget}
|
|
13051
13058
|
|
|
13052
13059
|
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}.`,
|
|
13053
13060
|
CreateSiteFromTemplate: dedent_default`
|
|
13054
13061
|
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.
|
|
13055
13062
|
|
|
13056
|
-
Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates
|
|
13063
|
+
Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates\` (\`builder: classic\` for a Classic Editor site).
|
|
13057
13064
|
|
|
13058
13065
|
`,
|
|
13059
13066
|
SearchWixGraphQLOperations: dedent_default`
|
|
@@ -13081,34 +13088,24 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
13081
13088
|
Compose: put independent reads in one document as several root fields, and use related-entity fields (e.g. \`inventoryItems\`, \`collections\`, \`fulfillments\`) instead of extra round-trips. Select only the fields you need; every object field needs a selection set. Variables: use them for \`JSON\` scalars (filter, sort) and plain scalars (String, Int, ID), declared in the document (e.g. \`query Q($f: JSON) { ... filter: $f ... }\`). Write mutation inputs and any object that contains an enum INLINE as GraphQL literals with enum values unquoted (e.g. \`input: { product: { name: "Mug", productType: PHYSICAL } }\`) — this endpoint fails with 503 when an enum value arrives inside an object-typed variable. For lists page with \`cursorPaging\` or \`paging\` exactly as the input type declares (never the other one), and request \`pageInfo { hasNext total }\`; \`total\` is only populated with offset \`paging\`, so for a cursor-paged list count by walking pages or use a count operation.
|
|
13082
13089
|
Results: each root field fails independently — \`errors[].path\` names the failing field and \`errors[].extensions.message\` the cause (e.g. PERMISSION_DENIED); \`data\` still holds the fields that succeeded. Mutations return the created/updated entity — read the IDs from it and do not re-query to verify. Large results are truncated: narrow the selection or page size instead of re-running.
|
|
13083
13090
|
Set \`hasMutations: true\` for any document containing a mutation root field.
|
|
13091
|
+
`,
|
|
13092
|
+
UploadHeadlessWebsiteFiles: dedent_default`
|
|
13093
|
+
The upload step of the StartDeployWebsite and StartHeadlessBuild guides, and nothing else: puts a finished static website's files — index.html plus its CSS/JS/images, a static build, or a zip of the site — 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.
|
|
13094
|
+
Pass the files themselves: in ChatGPT as \`attachments\` (any type, a zip is unpacked), elsewhere as the \`files\` text bundle (\`=== FILE: <path> ===\` sections, \`base64\` tag for small binaries). The site must contain index.html at its root.
|
|
13095
|
+
NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here (those are the site-creation tools, after their own questions). Also NOT for media uploads to an existing site (UploadImageToWixSite) or any other API call (ExecuteWixAPI).
|
|
13084
13096
|
`,
|
|
13085
13097
|
StartDeployWebsite: dedent_default`
|
|
13086
|
-
The user — or you, earlier in this conversation — ALREADY HAVE a finished page or site, in any form, and it should be live on Wix — or an already-deployed Wix Headless site whose files should be replaced. The site can be HTML/CSS/JS files, a static build (dist/, out/), a zip, another AI builder's export, or a page/design you created in this conversation (export it to HTML first — a finished design IS a finished page). Call this when the user says deploy, host,
|
|
13098
|
+
The user — or you, earlier in this conversation — ALREADY HAVE a finished page or site, in any form, and it should be live on Wix — or an already-deployed Wix Headless site whose files should be replaced. The site can be HTML/CSS/JS files, a static build (dist/, out/), a zip, another AI builder's export, or a page/design you created in this conversation (export it to HTML first — a finished design IS a finished page). Call this when the user says deploy, host, put online, make it live, get a URL, upload my site / index.html, or wants to update or redeploy the files of a site you or the user deployed as code.
|
|
13087
13099
|
Updates keep the same site and URL. Also for implementing a backend on such a site as a Wix Headless project — same site, same URL. Works with or without a Wix login (anonymous deploy with a save link), through the Wix MCP alone or from a shell.
|
|
13088
13100
|
Missing real functionality — a fake checkout, a form that goes nowhere, bookings — is NOT a reason to rebuild the site with a site-creation tool: deploy the page as is, then add the backend afterwards (the recipe covers it).
|
|
13089
|
-
Returns the full recipe; follow it and run the flow yourself. The drop page is only the fallback when you can't.
|
|
13090
|
-
NOT for: a screenshot
|
|
13101
|
+
Returns the full recipe; follow it and run the flow yourself. When the UploadHeadlessWebsiteFiles tool is available, use it for the recipe's upload step — after the site exists (the recipe's provision call, or a metaSiteId from an earlier deploy) — instead of building the multipart body in code; never before this tool. The drop page is only the fallback when you can't.
|
|
13102
|
+
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).
|
|
13091
13103
|
`,
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
Call
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
## What This Tool Contains
|
|
13098
|
-
|
|
13099
|
-
This tool provides complete documentation including:
|
|
13100
|
-
- Template selection (empty sites or designed templates)
|
|
13101
|
-
- Wix Editor vs Wix Studio options
|
|
13102
|
-
- Regular vs Headless site creation (CLI-based; returns setup instructions)
|
|
13103
|
-
- API endpoint details and parameters
|
|
13104
|
-
- Post-creation steps (OAuth for headless, publishing for regular sites)
|
|
13105
|
-
- App installation instructions
|
|
13106
|
-
|
|
13107
|
-
## After Reading This Documentation
|
|
13108
|
-
|
|
13109
|
-
**After reading this documentation:**
|
|
13110
|
-
1. Use ManageWixSite for account-level site creation API calls
|
|
13111
|
-
2. Use CallWixSiteAPI / ExecuteWixAPI for site-level follow-up API calls (like install apps if needed)`
|
|
13104
|
+
StartHeadlessBuild: dedent_default`
|
|
13105
|
+
The user wants a site or web app that you build and keep changing in code, here with them, and no code exists yet. Wix runs its business side (store, bookings, CMS, forms, members, payments) as a Wix Headless site. Call this when the user asks you to build it yourself rather than Wix's AI or a template; when the two of you worked out a design in this conversation and nothing is coded yet; when it is an app (a game, a dashboard, a tool); when the user asks for a headless site; when a prompt links \`wix.com/headless/skill.md\` or a project's \`download.zip\`; or to move a Wix site to code.
|
|
13106
|
+
Call it right away: it only returns the guide, and the site's purpose is not needed first (pass "not stated yet" for what you don't know). Returns the full guide; follow it and do the work yourself, choosing its route by what you can reach (a shell that reaches Wix, or this MCP alone).
|
|
13107
|
+
NOT for: files, a build, a zip or a page that already exist (StartDeployWebsite); a site the user will design in Wix's editor (the site-creation tools); a Claude Design URL (import-claude-design-from-url).
|
|
13108
|
+
`
|
|
13112
13109
|
};
|
|
13113
13110
|
}
|
|
13114
13111
|
var defaultToolDescriptions = buildToolDescriptions(true);
|
|
@@ -13120,9 +13117,9 @@ var recipesIndexDoc = {
|
|
|
13120
13117
|
action: "include"
|
|
13121
13118
|
};
|
|
13122
13119
|
var README_INTRO = dedent_default`
|
|
13123
|
-
<
|
|
13120
|
+
<wix-usage-guide>
|
|
13124
13121
|
|
|
13125
|
-
|
|
13122
|
+
How to work with Wix through these tools:
|
|
13126
13123
|
|
|
13127
13124
|
<source-of-truth>
|
|
13128
13125
|
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.
|
|
@@ -13174,7 +13171,7 @@ var README_BODY = dedent_default`
|
|
|
13174
13171
|
<other-uses>
|
|
13175
13172
|
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.
|
|
13176
13173
|
</other-uses>
|
|
13177
|
-
</
|
|
13174
|
+
</wix-usage-guide>
|
|
13178
13175
|
`;
|
|
13179
13176
|
var defaultReadmeDocs = [
|
|
13180
13177
|
{
|
|
@@ -13230,8 +13227,7 @@ var widgetSiteCreationConfig = {
|
|
|
13230
13227
|
"WixSiteBuilder",
|
|
13231
13228
|
"WixSiteCreation",
|
|
13232
13229
|
"SearchSiteTemplates",
|
|
13233
|
-
"CreateSiteFromTemplate"
|
|
13234
|
-
"CreateWixBusinessGuide"
|
|
13230
|
+
"CreateSiteFromTemplate"
|
|
13235
13231
|
].map((tool) => [tool, toWidget(defaultToolDescriptions[tool])])
|
|
13236
13232
|
)
|
|
13237
13233
|
};
|
|
@@ -13787,7 +13783,7 @@ var paramDescriptions = {
|
|
|
13787
13783
|
},
|
|
13788
13784
|
SearchWixRESTDocumentation: {
|
|
13789
13785
|
searchTerm: `The search term to look up in the Wix REST API Documentation \u2014 a natural-language description of the endpoint, resource, or action you need across any Wix domain (Stores, eCommerce, Bookings, CRM, Members, CMS, Events, Blog, Forms, Media, site settings, etc.), e.g. "create a catalog item with inventory", "book a service", "update data collection items". Match the wording to how many entities you will act on: a plural term ("update products") surfaces the resource's bulk methods, which a singular term ("update a product") ranks lower or leaves out. Results are ranked by relevance, so describe the goal rather than guessing an exact API name.`,
|
|
13790
|
-
maxResults: "The maximum number of results to return, default
|
|
13786
|
+
maxResults: "The maximum number of results to return (1\u201317, default 17). Results mix REST API documentation with matching recipes and skills.",
|
|
13791
13787
|
reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
|
|
13792
13788
|
},
|
|
13793
13789
|
WixBusinessFlowsDocumentation: {
|
|
@@ -13808,7 +13804,7 @@ var paramDescriptions = {
|
|
|
13808
13804
|
},
|
|
13809
13805
|
SearchWixHeadlessDocumentation: {
|
|
13810
13806
|
searchTerm: "The search term to search for in the Headless Documentation",
|
|
13811
|
-
maxResults: "The maximum number of results to return, default
|
|
13807
|
+
maxResults: "The maximum number of results to return (1\u201317, default 17). Results mix Headless documentation with matching recipes and skills."
|
|
13812
13808
|
},
|
|
13813
13809
|
SearchWixVeloDocumentation: {
|
|
13814
13810
|
searchTerm: "The search term to search for in the Velo Documentation",
|
|
@@ -13820,7 +13816,7 @@ var paramDescriptions = {
|
|
|
13820
13816
|
reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
|
|
13821
13817
|
},
|
|
13822
13818
|
ReadFullDocsArticle: {
|
|
13823
|
-
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/...) are read as-is, including their referenced files."
|
|
13819
|
+
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."
|
|
13824
13820
|
},
|
|
13825
13821
|
ReadFullDocsMethodSchema: {
|
|
13826
13822
|
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).",
|
|
@@ -13836,7 +13832,7 @@ var paramDescriptions = {
|
|
|
13836
13832
|
},
|
|
13837
13833
|
UploadImageToWixSite: {
|
|
13838
13834
|
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.",
|
|
13839
|
-
image: "Array of file ATTACHMENTS the user uploaded in the chat. Each item requires download_url and file_id, and may optionally include file_name and mime_type \u2014
|
|
13835
|
+
image: "Array of file ATTACHMENTS: files the user uploaded in the chat, or files you created in your own file sandbox. Each item requires download_url and file_id, and may optionally include file_name and mime_type \u2014 the platform resolves a file passed here into these fields automatically, so just include the files. For plain image URLs the user pasted, use imageUrls instead. ALWAYS pass ALL attachments together in a single call \u2014 NEVER call this tool once per image. Do NOT use together with imageBase64.",
|
|
13840
13836
|
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.",
|
|
13841
13837
|
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.',
|
|
13842
13838
|
mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
|
|
@@ -13860,7 +13856,7 @@ var paramDescriptions = {
|
|
|
13860
13856
|
operationName: "Operation to run when the document defines more than one named operation.",
|
|
13861
13857
|
siteId: "The Wix site to run against. Required for site-level operations (anything reading or writing inside a site \u2014 products, orders, contacts, bookings, CMS, blog, members ...). Resolve it with GetSiteContext or ListWixSites; never pass a guessed value.",
|
|
13862
13858
|
scope: '"site" (default when siteId is given) runs with a site-scoped credential; "account" runs account-level operations (sites, account settings) and ignores siteId.',
|
|
13863
|
-
hasMutations: "
|
|
13859
|
+
hasMutations: "True if the document contains a mutation root field in any of its operations; false if it contains only queries. A transient server error is retried only when this is false.",
|
|
13864
13860
|
reason: "One sentence explaining the original user request and why you are running this GraphQL operation to complete it."
|
|
13865
13861
|
},
|
|
13866
13862
|
RunWixCliCommand: {
|
|
@@ -14124,121 +14120,6 @@ ${skillsContent}`
|
|
|
14124
14120
|
);
|
|
14125
14121
|
}
|
|
14126
14122
|
|
|
14127
|
-
// src/docs/create-site-guide.ts
|
|
14128
|
-
function isHeadlessRequest(prompt) {
|
|
14129
|
-
return !!prompt && /headless/i.test(prompt);
|
|
14130
|
-
}
|
|
14131
|
-
var HEADLESS_NON_CLI_INSTRUCTIONS = `# Creating a Wix Headless Site
|
|
14132
|
-
|
|
14133
|
-
Wix Headless sites require the **Wix CLI** and cannot be created from the Wix dashboard or via REST API alone. Let the user know they need to set up a local development environment with the Wix CLI before proceeding.
|
|
14134
|
-
|
|
14135
|
-
## Prerequisites
|
|
14136
|
-
- Node.js 18+
|
|
14137
|
-
- A terminal / command-line environment
|
|
14138
|
-
|
|
14139
|
-
## Steps to Get Started
|
|
14140
|
-
|
|
14141
|
-
1. **Install and authenticate the Wix CLI:**
|
|
14142
|
-
\`\`\`
|
|
14143
|
-
npx @wix/cli login
|
|
14144
|
-
\`\`\`
|
|
14145
|
-
2. **Follow the quick-start guide:** https://dev.wix.com/docs/go-headless
|
|
14146
|
-
3. **For a complete orchestrated setup** (commerce, CMS, bookings, identity), use the Wix Headless skill: https://www.wix-headless.dev/skill.md`;
|
|
14147
|
-
var HEADLESS_CLI_INSTRUCTIONS = `# Creating a Wix Headless Site (CLI Flow)
|
|
14148
|
-
|
|
14149
|
-
Follow these steps using the \`RunWixCliCommand\` tool to scaffold and configure a Wix Headless site.
|
|
14150
|
-
|
|
14151
|
-
## Before You Start
|
|
14152
|
-
Ask the user:
|
|
14153
|
-
1. What is the brand/site name?
|
|
14154
|
-
2. Which local directory should the project be created in?
|
|
14155
|
-
|
|
14156
|
-
## Steps
|
|
14157
|
-
|
|
14158
|
-
### Step 1 \u2014 Authenticate (if not already done)
|
|
14159
|
-
Run: \`npx @wix/cli login\`
|
|
14160
|
-
Skip if the user is already authenticated.
|
|
14161
|
-
|
|
14162
|
-
### Step 2 \u2014 Scaffold the project
|
|
14163
|
-
Use a Wix headless starter template. Ask the user which starter they prefer, or use the default Next.js starter. Follow the scaffolding instructions at: https://dev.wix.com/docs/go-headless
|
|
14164
|
-
|
|
14165
|
-
### Step 3 \u2014 Install dependencies
|
|
14166
|
-
Run: \`npm install\` in the project directory.
|
|
14167
|
-
|
|
14168
|
-
### Step 4 \u2014 Pull environment variables
|
|
14169
|
-
Run: \`npx @wix/cli env pull\`
|
|
14170
|
-
This writes the \`.env.local\` file with the site's environment variables.
|
|
14171
|
-
|
|
14172
|
-
### Step 5 \u2014 Build and verify
|
|
14173
|
-
Run: \`npx @wix/cli build\`
|
|
14174
|
-
|
|
14175
|
-
### Step 6 \u2014 Release (optional)
|
|
14176
|
-
Run: \`npx @wix/cli release\` to deploy.
|
|
14177
|
-
|
|
14178
|
-
## Full Orchestration
|
|
14179
|
-
For a complete multi-wave headless setup (commerce, CMS, bookings, design system, content seeding), refer to the Wix Headless skill: https://www.wix-headless.dev/skill.md
|
|
14180
|
-
|
|
14181
|
-
Use \`RunWixCliCommand\` for each CLI step above.`;
|
|
14182
|
-
var CREATE_SITE_DOCS = [
|
|
14183
|
-
{
|
|
14184
|
-
url: "https://dev.wix.com/docs/kb-only/MCP_REST_RECIPES_KB_ID/TRAIN_create_a_new_site_from_template",
|
|
14185
|
-
title: "How to Create a New Wix Site",
|
|
14186
|
-
nextSteps: "\n\n**Next Steps:** Use the ManageWixSite tool to make the API call for creating the Wix site."
|
|
14187
|
-
},
|
|
14188
|
-
{
|
|
14189
|
-
url: "https://dev.wix.com/docs/kb-only/MCP_REST_RECIPES_KB_ID/TRAIN_wix.devcenter.apps.installer.v1.AppsInstallerService.InstallApp",
|
|
14190
|
-
title: "How to Install App on a Wix Site",
|
|
14191
|
-
nextSteps: "\n\n**Next Steps:** Use the CallWixSiteAPI tool to make the API call for installing apps on the Wix site."
|
|
14192
|
-
}
|
|
14193
|
-
];
|
|
14194
|
-
async function addCreateWixBusinessGuideTools(server, disableTools = [], toolDescriptions, cliEnabled = false) {
|
|
14195
|
-
if (disableTools.includes("CreateWixBusinessGuide")) {
|
|
14196
|
-
return;
|
|
14197
|
-
}
|
|
14198
|
-
server.tool(
|
|
14199
|
-
"CreateWixBusinessGuide",
|
|
14200
|
-
toolDescriptions?.["CreateWixBusinessGuide"] ?? defaultToolDescriptions.CreateWixBusinessGuide,
|
|
14201
|
-
{
|
|
14202
|
-
userPrompt: external_exports.string().optional().describe("The user prompt that triggered this tool")
|
|
14203
|
-
},
|
|
14204
|
-
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
14205
|
-
async (args, { httpClient }) => {
|
|
14206
|
-
if (isHeadlessRequest(args.userPrompt)) {
|
|
14207
|
-
const isCli = typeof cliEnabled === "function" ? cliEnabled() : cliEnabled;
|
|
14208
|
-
const text = isCli ? HEADLESS_CLI_INSTRUCTIONS : HEADLESS_NON_CLI_INSTRUCTIONS;
|
|
14209
|
-
return { content: [{ type: "text", text }] };
|
|
14210
|
-
}
|
|
14211
|
-
try {
|
|
14212
|
-
const contents = await Promise.all(
|
|
14213
|
-
CREATE_SITE_DOCS.map(async ({ url, title, nextSteps }) => {
|
|
14214
|
-
const articleContent = await fetchArticleContent(
|
|
14215
|
-
httpClient,
|
|
14216
|
-
url,
|
|
14217
|
-
"article",
|
|
14218
|
-
true
|
|
14219
|
-
);
|
|
14220
|
-
const fullContent = `# ${title}
|
|
14221
|
-
|
|
14222
|
-
${articleContent}${nextSteps}`;
|
|
14223
|
-
return { type: "text", text: fullContent };
|
|
14224
|
-
})
|
|
14225
|
-
);
|
|
14226
|
-
return { content: contents };
|
|
14227
|
-
} catch (error) {
|
|
14228
|
-
return {
|
|
14229
|
-
isError: true,
|
|
14230
|
-
content: [
|
|
14231
|
-
{
|
|
14232
|
-
type: "text",
|
|
14233
|
-
text: `Error fetching create site documentation: ${error.message}`
|
|
14234
|
-
}
|
|
14235
|
-
]
|
|
14236
|
-
};
|
|
14237
|
-
}
|
|
14238
|
-
}
|
|
14239
|
-
);
|
|
14240
|
-
}
|
|
14241
|
-
|
|
14242
14123
|
// src/docs/start-deploy-website.ts
|
|
14243
14124
|
var START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS = {
|
|
14244
14125
|
userRequest: "The user's request that led here, quoted if short, otherwise summarized in a sentence.",
|
|
@@ -14310,6 +14191,87 @@ function addStartDeployWebsiteTool(server, disableTools = [], toolDescriptions,
|
|
|
14310
14191
|
);
|
|
14311
14192
|
}
|
|
14312
14193
|
|
|
14194
|
+
// src/docs/start-headless-build.ts
|
|
14195
|
+
var START_HEADLESS_BUILD_TOOL_NAME = "StartHeadlessBuild";
|
|
14196
|
+
var HEADLESS_GUIDE_URL = "https://www.wix.com/skills/headless-cold-start/headless-kit.md";
|
|
14197
|
+
var START_HEADLESS_BUILD_STARTING_POINTS = [
|
|
14198
|
+
"nothing yet",
|
|
14199
|
+
"a design worked out in this conversation",
|
|
14200
|
+
"an existing Wix Headless site",
|
|
14201
|
+
"a project download link",
|
|
14202
|
+
"a Wix site to move to code"
|
|
14203
|
+
];
|
|
14204
|
+
var START_HEADLESS_BUILD_PARAM_DESCRIPTIONS = {
|
|
14205
|
+
userRequest: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.userRequest,
|
|
14206
|
+
siteDescription: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteDescription,
|
|
14207
|
+
startingPoint: "What the build starts from: nothing yet; a design worked out in this conversation; an existing Wix Headless site; a project download link (a download.zip from Wix); or a Wix site to move to code.",
|
|
14208
|
+
siteFunctionality: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteFunctionality,
|
|
14209
|
+
agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
|
|
14210
|
+
wixAccess: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.wixAccess
|
|
14211
|
+
};
|
|
14212
|
+
var PREFIX2 = "The following is Wix's headless guide. Follow it end to end and do the work yourself. If you find the user already has the site's code, call StartDeployWebsite instead.";
|
|
14213
|
+
var MOVE_TO_CODE_LINE = `Moving a Wix site to code starts from the site's dashboard (Headless settings, "Go Headless"), which hands the owner a prompt with the project's download link. Ask the user for that prompt, then follow the guide with it.`;
|
|
14214
|
+
function startHeadlessBuildResponse(guide, startingPoint) {
|
|
14215
|
+
const lines = [];
|
|
14216
|
+
if (startingPoint === "a Wix site to move to code") {
|
|
14217
|
+
lines.push(MOVE_TO_CODE_LINE);
|
|
14218
|
+
}
|
|
14219
|
+
if (guide && guide.trim()) {
|
|
14220
|
+
lines.push(PREFIX2, "---", guide.trim());
|
|
14221
|
+
} else {
|
|
14222
|
+
lines.push(
|
|
14223
|
+
`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.`
|
|
14224
|
+
);
|
|
14225
|
+
}
|
|
14226
|
+
return lines.join("\n\n");
|
|
14227
|
+
}
|
|
14228
|
+
function addStartHeadlessBuildTool(server, disableTools = [], toolDescriptions, fetchOptions = {}) {
|
|
14229
|
+
if (disableTools.includes(START_HEADLESS_BUILD_TOOL_NAME)) {
|
|
14230
|
+
return;
|
|
14231
|
+
}
|
|
14232
|
+
const d = START_HEADLESS_BUILD_PARAM_DESCRIPTIONS;
|
|
14233
|
+
server.tool(
|
|
14234
|
+
START_HEADLESS_BUILD_TOOL_NAME,
|
|
14235
|
+
toolDescriptions?.[START_HEADLESS_BUILD_TOOL_NAME] ?? defaultToolDescriptions[START_HEADLESS_BUILD_TOOL_NAME],
|
|
14236
|
+
{
|
|
14237
|
+
userRequest: external_exports.string().min(1).describe(d.userRequest),
|
|
14238
|
+
siteDescription: external_exports.string().min(1).describe(d.siteDescription),
|
|
14239
|
+
startingPoint: external_exports.enum(START_HEADLESS_BUILD_STARTING_POINTS).describe(d.startingPoint),
|
|
14240
|
+
siteFunctionality: external_exports.string().min(1).describe(d.siteFunctionality),
|
|
14241
|
+
agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
|
|
14242
|
+
wixAccess: external_exports.string().min(1).describe(d.wixAccess)
|
|
14243
|
+
},
|
|
14244
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
14245
|
+
async (args, { httpClient }) => {
|
|
14246
|
+
let guide;
|
|
14247
|
+
try {
|
|
14248
|
+
guide = await fetchArticleContent(
|
|
14249
|
+
httpClient,
|
|
14250
|
+
HEADLESS_GUIDE_URL,
|
|
14251
|
+
"article",
|
|
14252
|
+
true,
|
|
14253
|
+
fetchOptions.skillsPortalUrl,
|
|
14254
|
+
fetchOptions.repo,
|
|
14255
|
+
fetchOptions.commitHash
|
|
14256
|
+
);
|
|
14257
|
+
} catch (error) {
|
|
14258
|
+
console.warn(
|
|
14259
|
+
"[StartHeadlessBuild] guide fetch failed, returning the pointer:",
|
|
14260
|
+
error instanceof Error ? error.message : error
|
|
14261
|
+
);
|
|
14262
|
+
}
|
|
14263
|
+
return {
|
|
14264
|
+
content: [
|
|
14265
|
+
{
|
|
14266
|
+
type: "text",
|
|
14267
|
+
text: startHeadlessBuildResponse(guide, args.startingPoint)
|
|
14268
|
+
}
|
|
14269
|
+
]
|
|
14270
|
+
};
|
|
14271
|
+
}
|
|
14272
|
+
);
|
|
14273
|
+
}
|
|
14274
|
+
|
|
14313
14275
|
// src/docs/docs.ts
|
|
14314
14276
|
var REST_SEARCH_API_GROUNDING_REMINDER = dedent_default`
|
|
14315
14277
|
# Before calling a Wix API
|
|
@@ -14357,8 +14319,8 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14357
14319
|
], options = {}) => {
|
|
14358
14320
|
const {
|
|
14359
14321
|
getToKnowWixEnabled = false,
|
|
14360
|
-
createWixBusinessGuideEnabled = true,
|
|
14361
14322
|
startDeployWebsiteEnabled = true,
|
|
14323
|
+
startHeadlessBuildEnabled = true,
|
|
14362
14324
|
disableTools,
|
|
14363
14325
|
toolDescriptions,
|
|
14364
14326
|
readmeDocs,
|
|
@@ -14370,7 +14332,6 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14370
14332
|
commitHash,
|
|
14371
14333
|
skillsTags,
|
|
14372
14334
|
dynamicContextEnabled,
|
|
14373
|
-
cliEnabled = false,
|
|
14374
14335
|
usePlatformizedSearch = true,
|
|
14375
14336
|
usePlatformizedBrowse = true
|
|
14376
14337
|
} = options;
|
|
@@ -14416,14 +14377,6 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14416
14377
|
skillsTags
|
|
14417
14378
|
});
|
|
14418
14379
|
}
|
|
14419
|
-
if (createWixBusinessGuideEnabled && !disableTools?.includes("CreateWixBusinessGuide")) {
|
|
14420
|
-
addCreateWixBusinessGuideTools(
|
|
14421
|
-
server,
|
|
14422
|
-
disableTools || [],
|
|
14423
|
-
toolDescriptions,
|
|
14424
|
-
cliEnabled
|
|
14425
|
-
);
|
|
14426
|
-
}
|
|
14427
14380
|
if (startDeployWebsiteEnabled) {
|
|
14428
14381
|
addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
|
|
14429
14382
|
skillsPortalUrl,
|
|
@@ -14431,6 +14384,13 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14431
14384
|
commitHash: effectiveCommitHash
|
|
14432
14385
|
});
|
|
14433
14386
|
}
|
|
14387
|
+
if (startHeadlessBuildEnabled) {
|
|
14388
|
+
addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
|
|
14389
|
+
skillsPortalUrl,
|
|
14390
|
+
repo: effectiveRepo,
|
|
14391
|
+
commitHash: effectiveCommitHash
|
|
14392
|
+
});
|
|
14393
|
+
}
|
|
14434
14394
|
if (allowedTools.includes("WDS") && !disableTools?.includes("SearchWixWDSDocumentation")) {
|
|
14435
14395
|
server.tool(
|
|
14436
14396
|
"SearchWixWDSDocumentation",
|
|
@@ -14495,7 +14455,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14495
14455
|
const d = desc("SearchWixRESTDocumentation");
|
|
14496
14456
|
return {
|
|
14497
14457
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
14498
|
-
maxResults: external_exports.number().describe(d.maxResults).min(1).max(
|
|
14458
|
+
maxResults: external_exports.number().describe(d.maxResults).min(1).max(17).optional().default(17),
|
|
14499
14459
|
reason: external_exports.string().describe(d.reason)
|
|
14500
14460
|
};
|
|
14501
14461
|
})(),
|
|
@@ -14510,9 +14470,12 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14510
14470
|
toolParams: {
|
|
14511
14471
|
searchTerm
|
|
14512
14472
|
},
|
|
14513
|
-
maxResults: Math.max(1, Math.min(maxResults ??
|
|
14473
|
+
maxResults: Math.max(1, Math.min(maxResults ?? 17, 17)),
|
|
14514
14474
|
linesInEachResult: 30,
|
|
14515
|
-
usePlatformizedSearch
|
|
14475
|
+
usePlatformizedSearch,
|
|
14476
|
+
// Merge recipes/skills into the REST ranking — they live in a
|
|
14477
|
+
// separate corpus the REST document_type doesn't cover on its own.
|
|
14478
|
+
extraDocumentTypes: ["SKILLS"]
|
|
14516
14479
|
});
|
|
14517
14480
|
const grounding = result.includes(QUERY_CAPABILITY_MARKER) ? [
|
|
14518
14481
|
REST_SEARCH_API_GROUNDING_REMINDER,
|
|
@@ -14775,7 +14738,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14775
14738
|
const d = desc("SearchWixHeadlessDocumentation");
|
|
14776
14739
|
return {
|
|
14777
14740
|
searchTerm: external_exports.string().describe(d.searchTerm),
|
|
14778
|
-
maxResults: external_exports.number().describe(d.maxResults).min(1).max(
|
|
14741
|
+
maxResults: external_exports.number().describe(d.maxResults).min(1).max(17).optional().default(17)
|
|
14779
14742
|
};
|
|
14780
14743
|
})(),
|
|
14781
14744
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
@@ -14787,8 +14750,11 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14787
14750
|
toolParams: {
|
|
14788
14751
|
searchTerm
|
|
14789
14752
|
},
|
|
14790
|
-
maxResults: Math.max(1, Math.min(maxResults ??
|
|
14791
|
-
usePlatformizedSearch
|
|
14753
|
+
maxResults: Math.max(1, Math.min(maxResults ?? 17, 17)),
|
|
14754
|
+
usePlatformizedSearch,
|
|
14755
|
+
// Merge recipes/skills into the headless ranking — same separate
|
|
14756
|
+
// corpus the WIX_HEADLESS document_type doesn't cover on its own.
|
|
14757
|
+
extraDocumentTypes: ["SKILLS"]
|
|
14792
14758
|
});
|
|
14793
14759
|
return {
|
|
14794
14760
|
content: [
|
|
@@ -14954,7 +14920,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
14954
14920
|
content: [
|
|
14955
14921
|
{
|
|
14956
14922
|
type: "text",
|
|
14957
|
-
text: `Invalid article URL: must start with https://dev.wix.com/docs/, or be a Wix skills page (https://www.wix.com/skills/...). Got: "${articleUrl}"`
|
|
14923
|
+
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/...). Got: "${articleUrl}"`
|
|
14958
14924
|
}
|
|
14959
14925
|
]
|
|
14960
14926
|
};
|
|
@@ -15676,10 +15642,12 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
15676
15642
|
sourceDocUrl: external_exports.string().describe(d.sourceDocUrl)
|
|
15677
15643
|
};
|
|
15678
15644
|
})(),
|
|
15645
|
+
// openWorldHint true: a free-form URL into the whole Wix REST surface is an
|
|
15646
|
+
// open-ended target, even though auth is bounded to the user's account.
|
|
15679
15647
|
getHints("CallWixSiteAPI", {
|
|
15680
15648
|
readOnlyHint: false,
|
|
15681
15649
|
destructiveHint: true,
|
|
15682
|
-
openWorldHint:
|
|
15650
|
+
openWorldHint: true
|
|
15683
15651
|
}),
|
|
15684
15652
|
async ({ url, body, method, siteId, reason, sourceDocUrl }, { httpClient, setToolBiParams }) => {
|
|
15685
15653
|
logger.log(
|
|
@@ -15856,7 +15824,7 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
|
|
|
15856
15824
|
getHints("ManageWixSite", {
|
|
15857
15825
|
readOnlyHint: false,
|
|
15858
15826
|
destructiveHint: true,
|
|
15859
|
-
openWorldHint:
|
|
15827
|
+
openWorldHint: true
|
|
15860
15828
|
}),
|
|
15861
15829
|
async ({ url, body, method, reason }, { httpClient, setToolBiParams }) => {
|
|
15862
15830
|
logger.log(
|
|
@@ -16612,7 +16580,8 @@ function addExecuteGraphqlTool(server, description, authStrategy, options) {
|
|
|
16612
16580
|
hasMutations: external_exports.boolean().describe(d.hasMutations),
|
|
16613
16581
|
reason: external_exports.string().describe(d.reason)
|
|
16614
16582
|
},
|
|
16615
|
-
|
|
16583
|
+
// openWorldHint true: any operation in the Wix GraphQL schema, chosen at call time.
|
|
16584
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
16616
16585
|
async ({ query, variables, operationName, siteId, scope, hasMutations, reason }, extra) => {
|
|
16617
16586
|
const effectiveScope = scope ?? (siteId ? "site" : "account");
|
|
16618
16587
|
logger.log(
|
|
@@ -16705,6 +16674,189 @@ ${text.slice(0, 2e3)}`
|
|
|
16705
16674
|
);
|
|
16706
16675
|
}
|
|
16707
16676
|
|
|
16677
|
+
// src/code-mode/attachments.ts
|
|
16678
|
+
var MAX_ATTACHMENTS_BYTES = 10 * 1024 * 1024;
|
|
16679
|
+
var MAX_REDIRECTS = 4;
|
|
16680
|
+
var OAI_USERCONTENT_HOST = /^[a-z0-9-]+\.oaiusercontent\.com$/;
|
|
16681
|
+
var OAI_BLOB_ACCOUNTS = new Set(
|
|
16682
|
+
[
|
|
16683
|
+
"australiaeast",
|
|
16684
|
+
"brazilsouth",
|
|
16685
|
+
"cacentral",
|
|
16686
|
+
"centralindia",
|
|
16687
|
+
"centralus",
|
|
16688
|
+
"centralusdr",
|
|
16689
|
+
"denmarkeast",
|
|
16690
|
+
"eastus",
|
|
16691
|
+
"eastus2",
|
|
16692
|
+
"indiasocentral",
|
|
16693
|
+
"italynorth",
|
|
16694
|
+
"japaneast",
|
|
16695
|
+
"koreacentral",
|
|
16696
|
+
"northcentralus",
|
|
16697
|
+
"northeu",
|
|
16698
|
+
"nznorth",
|
|
16699
|
+
"polandcentral",
|
|
16700
|
+
"seasia",
|
|
16701
|
+
"southcentralus",
|
|
16702
|
+
"southeastus3",
|
|
16703
|
+
"ukwest",
|
|
16704
|
+
"westcentralus",
|
|
16705
|
+
"westus",
|
|
16706
|
+
"westus2",
|
|
16707
|
+
"westus3"
|
|
16708
|
+
].map((region) => `oaisdmntpr${region}.blob.core.windows.net`)
|
|
16709
|
+
);
|
|
16710
|
+
function isAllowedAttachmentHost(hostname) {
|
|
16711
|
+
const h = hostname.toLowerCase();
|
|
16712
|
+
return OAI_USERCONTENT_HOST.test(h) || OAI_BLOB_ACCOUNTS.has(h);
|
|
16713
|
+
}
|
|
16714
|
+
function checkAttachmentUrl(url) {
|
|
16715
|
+
if (url.protocol !== "https:") return "is not an https URL";
|
|
16716
|
+
if (url.port) return "uses a non-default port";
|
|
16717
|
+
if (!isAllowedAttachmentHost(url.hostname)) {
|
|
16718
|
+
return `host "${url.hostname}" is not a ChatGPT file host`;
|
|
16719
|
+
}
|
|
16720
|
+
return void 0;
|
|
16721
|
+
}
|
|
16722
|
+
var AttachmentError = class extends Error {
|
|
16723
|
+
};
|
|
16724
|
+
function attachmentPath(a, url) {
|
|
16725
|
+
let fromUrl = "";
|
|
16726
|
+
try {
|
|
16727
|
+
fromUrl = decodeURIComponent(
|
|
16728
|
+
url.pathname.split("/").filter(Boolean).pop() ?? ""
|
|
16729
|
+
);
|
|
16730
|
+
} catch {
|
|
16731
|
+
fromUrl = "";
|
|
16732
|
+
}
|
|
16733
|
+
const raw = (a.file_name?.trim() || fromUrl || a.file_id).replace(/\\/g, "/");
|
|
16734
|
+
const segments = raw.split("/").filter((s) => s && s !== "." && s !== "..");
|
|
16735
|
+
return segments.join("/") || a.file_id;
|
|
16736
|
+
}
|
|
16737
|
+
function toBase64(bytes) {
|
|
16738
|
+
let binary = "";
|
|
16739
|
+
const chunk = 32768;
|
|
16740
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
16741
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
16742
|
+
}
|
|
16743
|
+
return btoa(binary);
|
|
16744
|
+
}
|
|
16745
|
+
async function fetchAllowed(start, fetchImpl, label) {
|
|
16746
|
+
let url = start;
|
|
16747
|
+
for (let hop = 0; ; hop++) {
|
|
16748
|
+
const res = await fetchImpl(url.toString(), { redirect: "manual" });
|
|
16749
|
+
const location = res.headers.get("location");
|
|
16750
|
+
const isRedirect = res.status >= 300 && res.status < 400 && location;
|
|
16751
|
+
if (!isRedirect) return res;
|
|
16752
|
+
await res.body?.cancel().catch(() => void 0);
|
|
16753
|
+
if (hop >= MAX_REDIRECTS) {
|
|
16754
|
+
throw new AttachmentError(`${label}: too many redirects.`);
|
|
16755
|
+
}
|
|
16756
|
+
let next;
|
|
16757
|
+
try {
|
|
16758
|
+
next = new URL(location, url);
|
|
16759
|
+
} catch {
|
|
16760
|
+
throw new AttachmentError(`${label}: redirect to an invalid URL.`);
|
|
16761
|
+
}
|
|
16762
|
+
const problem = checkAttachmentUrl(next);
|
|
16763
|
+
if (problem) {
|
|
16764
|
+
throw new AttachmentError(
|
|
16765
|
+
`${label}: the download redirected to a URL that ${problem}; refused.`
|
|
16766
|
+
);
|
|
16767
|
+
}
|
|
16768
|
+
url = next;
|
|
16769
|
+
}
|
|
16770
|
+
}
|
|
16771
|
+
async function readWithin(res, budget, overBudget) {
|
|
16772
|
+
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
16773
|
+
if (declared > budget) throw overBudget();
|
|
16774
|
+
if (!res.body) {
|
|
16775
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
16776
|
+
if (bytes.byteLength > budget) throw overBudget();
|
|
16777
|
+
return bytes;
|
|
16778
|
+
}
|
|
16779
|
+
const reader = res.body.getReader();
|
|
16780
|
+
const chunks = [];
|
|
16781
|
+
let total = 0;
|
|
16782
|
+
for (; ; ) {
|
|
16783
|
+
const { done, value } = await reader.read();
|
|
16784
|
+
if (done) break;
|
|
16785
|
+
total += value.byteLength;
|
|
16786
|
+
if (total > budget) {
|
|
16787
|
+
await reader.cancel().catch(() => void 0);
|
|
16788
|
+
throw overBudget();
|
|
16789
|
+
}
|
|
16790
|
+
chunks.push(value);
|
|
16791
|
+
}
|
|
16792
|
+
const out = new Uint8Array(total);
|
|
16793
|
+
let offset = 0;
|
|
16794
|
+
for (const c of chunks) {
|
|
16795
|
+
out.set(c, offset);
|
|
16796
|
+
offset += c.byteLength;
|
|
16797
|
+
}
|
|
16798
|
+
return out;
|
|
16799
|
+
}
|
|
16800
|
+
async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
16801
|
+
if (!Array.isArray(attachments) || attachments.length === 0) return [];
|
|
16802
|
+
const items = [];
|
|
16803
|
+
attachments.forEach((raw, i) => {
|
|
16804
|
+
const a = typeof raw === "object" && raw;
|
|
16805
|
+
const downloadUrl = a && typeof a.download_url === "string" ? a.download_url : "";
|
|
16806
|
+
let url;
|
|
16807
|
+
try {
|
|
16808
|
+
url = /^https?:\/\//i.test(downloadUrl) ? new URL(downloadUrl) : void 0;
|
|
16809
|
+
} catch {
|
|
16810
|
+
url = void 0;
|
|
16811
|
+
}
|
|
16812
|
+
if (!a || !url) {
|
|
16813
|
+
throw new AttachmentError(
|
|
16814
|
+
`attachments[${i}] was not resolved to a download URL (got ${JSON.stringify(
|
|
16815
|
+
typeof raw === "string" ? raw : (a && a.download_url) ?? raw
|
|
16816
|
+
)}). \`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).`
|
|
16817
|
+
);
|
|
16818
|
+
}
|
|
16819
|
+
const problem = checkAttachmentUrl(url);
|
|
16820
|
+
if (problem) {
|
|
16821
|
+
throw new AttachmentError(
|
|
16822
|
+
`attachments[${i}].download_url ${problem}. Only platform-resolved attachments are accepted here; for any other URL fetch it in \`code\` via wix.request or put the bytes in the \`files\` bundle.`
|
|
16823
|
+
);
|
|
16824
|
+
}
|
|
16825
|
+
items.push({ a, url });
|
|
16826
|
+
});
|
|
16827
|
+
let remaining = MAX_ATTACHMENTS_BYTES;
|
|
16828
|
+
const out = [];
|
|
16829
|
+
for (const [i, { a, url }] of items.entries()) {
|
|
16830
|
+
const path = attachmentPath(a, url);
|
|
16831
|
+
const label = `attachments[${i}] (${path})`;
|
|
16832
|
+
let res;
|
|
16833
|
+
try {
|
|
16834
|
+
res = await fetchAllowed(url, fetchImpl, label);
|
|
16835
|
+
} catch (e) {
|
|
16836
|
+
if (e instanceof AttachmentError) throw e;
|
|
16837
|
+
throw new AttachmentError(
|
|
16838
|
+
`${label}: download failed (${e.message}).`
|
|
16839
|
+
);
|
|
16840
|
+
}
|
|
16841
|
+
if (!res.ok) {
|
|
16842
|
+
await res.body?.cancel().catch(() => void 0);
|
|
16843
|
+
throw new AttachmentError(
|
|
16844
|
+
`${label}: download returned HTTP ${res.status}. The download_url may have expired; attach the file again.`
|
|
16845
|
+
);
|
|
16846
|
+
}
|
|
16847
|
+
const bytes = await readWithin(
|
|
16848
|
+
res,
|
|
16849
|
+
remaining,
|
|
16850
|
+
() => new AttachmentError(
|
|
16851
|
+
`${label} pushes the attachments past ${MAX_ATTACHMENTS_BYTES} bytes in total for this call; split the upload across calls.`
|
|
16852
|
+
)
|
|
16853
|
+
);
|
|
16854
|
+
remaining -= bytes.byteLength;
|
|
16855
|
+
out.push({ path, content: toBase64(bytes), encoding: "base64" });
|
|
16856
|
+
}
|
|
16857
|
+
return out;
|
|
16858
|
+
}
|
|
16859
|
+
|
|
16708
16860
|
// src/code-mode/validation.ts
|
|
16709
16861
|
var EXECUTE_FUNCTION_EXPRESSION_HINT = 'ExecuteWixAPI code must be a function expression, not a script body. Pass code like: async () => { const response = await wix.request({ scope: "site", method: "GET", url: "https://www.wixapis.com/..." }); return response.data; }. Do not write top-level const/await/return outside the function and do not invoke the function yourself.';
|
|
16710
16862
|
function validateExecuteCode(code) {
|
|
@@ -16719,6 +16871,327 @@ function validateExecuteCode(code) {
|
|
|
16719
16871
|
return { isValid: true };
|
|
16720
16872
|
}
|
|
16721
16873
|
|
|
16874
|
+
// src/api-call/site-files.ts
|
|
16875
|
+
var MAX_FILES_BUNDLE_CHARS = 4e6;
|
|
16876
|
+
var MAX_UNZIP_BYTES = 64 * 1024 * 1024;
|
|
16877
|
+
var MAX_SITE_FILES = 2e3;
|
|
16878
|
+
var textEncoder = new TextEncoder();
|
|
16879
|
+
function base64ToBytes(b64) {
|
|
16880
|
+
const clean = b64.replace(/^data:[^,]*,/, "").replace(/\s+/g, "");
|
|
16881
|
+
return Uint8Array.from(atob(clean), (c) => c.charCodeAt(0));
|
|
16882
|
+
}
|
|
16883
|
+
function parseFileBundle(bundle) {
|
|
16884
|
+
if (!bundle || !bundle.trim()) return [];
|
|
16885
|
+
if (bundle.length > MAX_FILES_BUNDLE_CHARS) {
|
|
16886
|
+
throw new Error(
|
|
16887
|
+
`files bundle is too large (${bundle.length} chars; max ${MAX_FILES_BUNDLE_CHARS}). Keep it to text and small base64 assets; pass larger files through attachments or reference media by URL.`
|
|
16888
|
+
);
|
|
16889
|
+
}
|
|
16890
|
+
const delimiter = /^=== FILE: (.+?)(?: (base64))? ===[ \t]*$/gm;
|
|
16891
|
+
const matches = [...bundle.matchAll(delimiter)];
|
|
16892
|
+
if (matches.length === 0) {
|
|
16893
|
+
throw new Error(
|
|
16894
|
+
"files bundle has no `=== FILE: <path> ===` sections. Start each file with that line (add ` base64` before the closing === for binary content)."
|
|
16895
|
+
);
|
|
16896
|
+
}
|
|
16897
|
+
return matches.map((match, i) => {
|
|
16898
|
+
const path = match[1].trim();
|
|
16899
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
16900
|
+
const end = i + 1 < matches.length ? matches[i + 1].index ?? bundle.length : bundle.length;
|
|
16901
|
+
const content = bundle.slice(start, end).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
|
|
16902
|
+
return {
|
|
16903
|
+
path,
|
|
16904
|
+
bytes: match[2] === "base64" ? base64ToBytes(content) : textEncoder.encode(content)
|
|
16905
|
+
};
|
|
16906
|
+
});
|
|
16907
|
+
}
|
|
16908
|
+
async function unzip(z) {
|
|
16909
|
+
const u16 = (o) => z[o] | z[o + 1] << 8;
|
|
16910
|
+
const u32 = (o) => (u16(o) | u16(o + 2) << 16) >>> 0;
|
|
16911
|
+
let end = z.length - 22;
|
|
16912
|
+
while (end >= 0 && !(u32(end) === 101010256 && end + 22 + u16(end + 20) === z.length))
|
|
16913
|
+
end--;
|
|
16914
|
+
if (end < 0) throw new Error("not a zip file (no end-of-central-directory)");
|
|
16915
|
+
const out = [];
|
|
16916
|
+
let p = u32(end + 16);
|
|
16917
|
+
let total = 0;
|
|
16918
|
+
const decoder = new TextDecoder();
|
|
16919
|
+
for (let i = 0, n = u16(end + 10); i < n; i++) {
|
|
16920
|
+
const path = decoder.decode(z.slice(p + 46, p + 46 + u16(p + 28)));
|
|
16921
|
+
const method = u16(p + 10);
|
|
16922
|
+
const size = u32(p + 24);
|
|
16923
|
+
total += size;
|
|
16924
|
+
if (total > MAX_UNZIP_BYTES) {
|
|
16925
|
+
throw new Error(
|
|
16926
|
+
`zip expands past ${MAX_UNZIP_BYTES} bytes; a static site is at most 20 MB`
|
|
16927
|
+
);
|
|
16928
|
+
}
|
|
16929
|
+
const at = u32(p + 42);
|
|
16930
|
+
const start = at + 30 + u16(at + 26) + u16(at + 28);
|
|
16931
|
+
const comp = z.slice(start, start + u32(p + 20));
|
|
16932
|
+
p += 46 + u16(p + 28) + u16(p + 30) + u16(p + 32);
|
|
16933
|
+
if (path.endsWith("/")) continue;
|
|
16934
|
+
let bytes;
|
|
16935
|
+
if (method === 0) bytes = comp;
|
|
16936
|
+
else if (method === 8) {
|
|
16937
|
+
bytes = new Uint8Array(
|
|
16938
|
+
await new Response(
|
|
16939
|
+
new Blob([comp]).stream().pipeThrough(new DecompressionStream("deflate-raw"))
|
|
16940
|
+
).arrayBuffer()
|
|
16941
|
+
);
|
|
16942
|
+
} else
|
|
16943
|
+
throw new Error(
|
|
16944
|
+
`zip entry '${path}' uses unsupported compression method ${method}`
|
|
16945
|
+
);
|
|
16946
|
+
if (bytes.length !== size) {
|
|
16947
|
+
throw new Error(
|
|
16948
|
+
`zip entry '${path}' declares ${size} bytes but holds ${bytes.length}`
|
|
16949
|
+
);
|
|
16950
|
+
}
|
|
16951
|
+
out.push({ path, bytes });
|
|
16952
|
+
}
|
|
16953
|
+
return out;
|
|
16954
|
+
}
|
|
16955
|
+
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);
|
|
16956
|
+
var isRootIndex = (p) => /^index\.html?$/i.test(p);
|
|
16957
|
+
async function normalizeSiteFiles(input) {
|
|
16958
|
+
let list = [];
|
|
16959
|
+
for (const f of input) {
|
|
16960
|
+
if (isZip(f)) list.push(...await unzip(f.bytes));
|
|
16961
|
+
else list.push(f);
|
|
16962
|
+
}
|
|
16963
|
+
list = list.map((f) => ({
|
|
16964
|
+
...f,
|
|
16965
|
+
path: f.path.replace(/\\/g, "/").replace(/^\.?\//, "")
|
|
16966
|
+
})).filter(
|
|
16967
|
+
(f) => f.path && !f.path.startsWith("__MACOSX/") && !/(^|\/)\.DS_Store$/.test(f.path) && !f.path.split("/").includes("..")
|
|
16968
|
+
);
|
|
16969
|
+
if (list.length === 0) {
|
|
16970
|
+
throw new Error(
|
|
16971
|
+
"No files to upload: pass the site in `files` or `attachments`."
|
|
16972
|
+
);
|
|
16973
|
+
}
|
|
16974
|
+
if (list.length > MAX_SITE_FILES) {
|
|
16975
|
+
throw new Error(
|
|
16976
|
+
`Too many files (${list.length}); a site upload is at most ${MAX_SITE_FILES}.`
|
|
16977
|
+
);
|
|
16978
|
+
}
|
|
16979
|
+
if (!list.some((f) => isRootIndex(f.path))) {
|
|
16980
|
+
const first = list[0].path.split("/")[0];
|
|
16981
|
+
if (list.every((f) => f.path.includes("/") && f.path.split("/")[0] === first)) {
|
|
16982
|
+
list = list.map((f) => ({
|
|
16983
|
+
...f,
|
|
16984
|
+
path: f.path.split("/").slice(1).join("/")
|
|
16985
|
+
}));
|
|
16986
|
+
}
|
|
16987
|
+
}
|
|
16988
|
+
const rootHtml = list.filter(
|
|
16989
|
+
(f) => !f.path.includes("/") && /\.html?$/i.test(f.path)
|
|
16990
|
+
);
|
|
16991
|
+
if (!list.some((f) => isRootIndex(f.path)) && rootHtml.length === 1) {
|
|
16992
|
+
list = list.map(
|
|
16993
|
+
(f) => f === rootHtml[0] ? { ...f, path: "index.html" } : f
|
|
16994
|
+
);
|
|
16995
|
+
}
|
|
16996
|
+
if (!list.some((f) => isRootIndex(f.path))) {
|
|
16997
|
+
const shown = list.slice(0, 8).map((f) => f.path).join(", ");
|
|
16998
|
+
throw new Error(
|
|
16999
|
+
`The site needs an index.html at its root. Files received: ${shown}${list.length > 8 ? ", \u2026" : ""}`
|
|
17000
|
+
);
|
|
17001
|
+
}
|
|
17002
|
+
return list;
|
|
17003
|
+
}
|
|
17004
|
+
function guessContentType(path) {
|
|
17005
|
+
const ext = (/\.([a-zA-Z0-9]+)$/.exec(path)?.[1] ?? "").toLowerCase();
|
|
17006
|
+
const map = {
|
|
17007
|
+
html: "text/html",
|
|
17008
|
+
htm: "text/html",
|
|
17009
|
+
css: "text/css",
|
|
17010
|
+
js: "text/javascript",
|
|
17011
|
+
mjs: "text/javascript",
|
|
17012
|
+
json: "application/json",
|
|
17013
|
+
svg: "image/svg+xml",
|
|
17014
|
+
txt: "text/plain",
|
|
17015
|
+
md: "text/markdown",
|
|
17016
|
+
xml: "application/xml",
|
|
17017
|
+
webmanifest: "application/manifest+json",
|
|
17018
|
+
ico: "image/x-icon",
|
|
17019
|
+
png: "image/png",
|
|
17020
|
+
jpg: "image/jpeg",
|
|
17021
|
+
jpeg: "image/jpeg",
|
|
17022
|
+
gif: "image/gif",
|
|
17023
|
+
webp: "image/webp",
|
|
17024
|
+
avif: "image/avif",
|
|
17025
|
+
woff: "font/woff",
|
|
17026
|
+
woff2: "font/woff2",
|
|
17027
|
+
ttf: "font/ttf",
|
|
17028
|
+
otf: "font/otf",
|
|
17029
|
+
pdf: "application/pdf",
|
|
17030
|
+
mp4: "video/mp4",
|
|
17031
|
+
webm: "video/webm",
|
|
17032
|
+
mp3: "audio/mpeg"
|
|
17033
|
+
};
|
|
17034
|
+
return map[ext] ?? "application/octet-stream";
|
|
17035
|
+
}
|
|
17036
|
+
function buildFormData(files) {
|
|
17037
|
+
const fd = new FormData();
|
|
17038
|
+
for (const f of files) {
|
|
17039
|
+
fd.append(
|
|
17040
|
+
"files",
|
|
17041
|
+
new Blob([f.bytes], { type: guessContentType(f.path) }),
|
|
17042
|
+
f.path
|
|
17043
|
+
);
|
|
17044
|
+
}
|
|
17045
|
+
return fd;
|
|
17046
|
+
}
|
|
17047
|
+
|
|
17048
|
+
// src/api-call/upload-headless-website-files.ts
|
|
17049
|
+
var UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME = "UploadHeadlessWebsiteFiles";
|
|
17050
|
+
var HEADLESS_BUSINESS = "https://www.wixapis.com/headless-business-setup/v1/headless-business";
|
|
17051
|
+
function dropAttribution(clientName) {
|
|
17052
|
+
const slug = (clientName ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
17053
|
+
return `?campaign=mcp&agent=${encodeURIComponent(slug || "wix-mcp")}`;
|
|
17054
|
+
}
|
|
17055
|
+
var UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS = {
|
|
17056
|
+
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 and 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.",
|
|
17057
|
+
attachments: "ChatGPT only. Files attached in the chat or created in your sandbox \u2014 any type: a zip of the whole site, HTML, CSS, JS, images. Pass each file as you have it, its file id or its /mnt/data path, and ChatGPT swaps it for { download_url, file_id, file_name?, mime_type? } before the call reaches Wix. No other client does this swap, so outside ChatGPT a path or file:// URL here fails at once; leave this out and use `files`, or, with a shell that reaches the internet, drop the files with curl as the StartDeployWebsite recipe shows. A zip is unpacked; a top-level folder wrapping the site is stripped. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.",
|
|
17058
|
+
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.",
|
|
17059
|
+
calledStartDeployWebsite: "Whether you called StartDeployWebsite earlier in this conversation and are following the recipe it returned. Answer truthfully; it is recorded, not checked.",
|
|
17060
|
+
agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
|
|
17061
|
+
reason: "One sentence: the user request that led here and what is being published."
|
|
17062
|
+
};
|
|
17063
|
+
function formatDropResult(r) {
|
|
17064
|
+
const shown = r.files.slice(0, 60);
|
|
17065
|
+
return [
|
|
17066
|
+
"Uploaded; the site is live with this file set.",
|
|
17067
|
+
`Live URL: ${r.siteUrl ?? "(not returned)"}`,
|
|
17068
|
+
`metaSiteId: ${r.metaSiteId} \u2014 pass it as \`siteId\` to update this site later; every upload replaces the whole file set.`,
|
|
17069
|
+
`Dashboard: https://manage.wix.com/dashboard/${r.metaSiteId}`,
|
|
17070
|
+
`${r.files.length} file(s): ${shown.join(", ")}${r.files.length > shown.length ? ", \u2026" : ""}`,
|
|
17071
|
+
"",
|
|
17072
|
+
"Give the user the live URL and the dashboard link.",
|
|
17073
|
+
`The upload step is done. For what comes next \u2014 changing the site later, connecting a domain, or adding a Wix backend (forms, bookings, a store) to this same site \u2014 continue with the guide you are following, from where it left off: the StartDeployWebsite recipe (${START_DEPLOY_WEBSITE_RECIPE_URL}) or, for a site built through StartHeadlessBuild, the headless guide (${HEADLESS_GUIDE_URL}).`
|
|
17074
|
+
].join("\n");
|
|
17075
|
+
}
|
|
17076
|
+
async function readJson(res, what) {
|
|
17077
|
+
const text = await res.text();
|
|
17078
|
+
if (!res.ok)
|
|
17079
|
+
throw new WixFetchError(res.status, res.statusText, `${what}: ${text}`);
|
|
17080
|
+
try {
|
|
17081
|
+
return JSON.parse(text);
|
|
17082
|
+
} catch {
|
|
17083
|
+
throw new Error(`${what} returned a non-JSON body: ${text.slice(0, 200)}`);
|
|
17084
|
+
}
|
|
17085
|
+
}
|
|
17086
|
+
function errorResult2(message) {
|
|
17087
|
+
return {
|
|
17088
|
+
isError: true,
|
|
17089
|
+
content: [
|
|
17090
|
+
{
|
|
17091
|
+
type: "text",
|
|
17092
|
+
text: `UploadHeadlessWebsiteFiles error: ${message}`
|
|
17093
|
+
}
|
|
17094
|
+
]
|
|
17095
|
+
};
|
|
17096
|
+
}
|
|
17097
|
+
function addUploadHeadlessWebsiteFilesTool(server, options) {
|
|
17098
|
+
const { authStrategy, toolDescriptions } = options;
|
|
17099
|
+
const clientName = () => options.clientName ?? server.clientName?.() ?? server.server?.getClientVersion?.()?.name;
|
|
17100
|
+
const fetchImpl = options.fetchImpl ?? ((u, i) => fetch(u, i));
|
|
17101
|
+
const d = UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS;
|
|
17102
|
+
const registered = server.tool(
|
|
17103
|
+
UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME,
|
|
17104
|
+
toolDescriptions?.[UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME] ?? defaultToolDescriptions.UploadHeadlessWebsiteFiles,
|
|
17105
|
+
{
|
|
17106
|
+
files: external_exports.string().optional().describe(d.files),
|
|
17107
|
+
attachments: external_exports.array(
|
|
17108
|
+
external_exports.object({
|
|
17109
|
+
download_url: external_exports.string(),
|
|
17110
|
+
file_id: external_exports.string(),
|
|
17111
|
+
file_name: external_exports.string().optional(),
|
|
17112
|
+
mime_type: external_exports.string().optional()
|
|
17113
|
+
})
|
|
17114
|
+
).optional().describe(d.attachments),
|
|
17115
|
+
siteId: external_exports.string().uuid("siteId must be a Wix metaSiteId (UUID)").describe(d.siteId),
|
|
17116
|
+
calledStartDeployWebsite: external_exports.boolean().describe(d.calledStartDeployWebsite),
|
|
17117
|
+
agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
|
|
17118
|
+
reason: external_exports.string().describe(d.reason)
|
|
17119
|
+
},
|
|
17120
|
+
// openWorldHint true: it fetches agent-supplied attachment URLs and publishes a
|
|
17121
|
+
// public website.
|
|
17122
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
17123
|
+
async (rawArgs, extra) => {
|
|
17124
|
+
const {
|
|
17125
|
+
files,
|
|
17126
|
+
attachments,
|
|
17127
|
+
siteId,
|
|
17128
|
+
calledStartDeployWebsite,
|
|
17129
|
+
agentIdentity,
|
|
17130
|
+
reason
|
|
17131
|
+
} = rawArgs;
|
|
17132
|
+
logger.log(
|
|
17133
|
+
`[UploadHeadlessWebsiteFiles] siteId=${siteId} calledStartDeployWebsite=${calledStartDeployWebsite} agent="${agentIdentity}" files=${files?.length ?? 0} chars attachments=${attachments?.length ?? 0} reason="${reason}"`
|
|
17134
|
+
);
|
|
17135
|
+
if (!files?.trim() && !(attachments && attachments.length)) {
|
|
17136
|
+
return errorResult2(
|
|
17137
|
+
"Nothing to publish: pass the site in `files` (text bundle) or `attachments` (files from this chat or your sandbox)."
|
|
17138
|
+
);
|
|
17139
|
+
}
|
|
17140
|
+
let siteFiles;
|
|
17141
|
+
try {
|
|
17142
|
+
const fromAttachments = (await resolveAttachments(attachments, fetchImpl)).map((a) => ({ path: a.path, bytes: base64ToBytes(a.content) }));
|
|
17143
|
+
siteFiles = await normalizeSiteFiles([
|
|
17144
|
+
...parseFileBundle(files),
|
|
17145
|
+
...fromAttachments
|
|
17146
|
+
]);
|
|
17147
|
+
} catch (e) {
|
|
17148
|
+
if (e instanceof AttachmentError || e instanceof Error)
|
|
17149
|
+
return errorResult2(e.message);
|
|
17150
|
+
throw e;
|
|
17151
|
+
}
|
|
17152
|
+
const authHeaders = await authStrategy.getAccountAuthHeaders();
|
|
17153
|
+
try {
|
|
17154
|
+
const metaSiteId = siteId;
|
|
17155
|
+
extra?.setToolBiParams?.({ siteId: metaSiteId });
|
|
17156
|
+
const dropped = await readJson(
|
|
17157
|
+
await fetchImpl(
|
|
17158
|
+
`${HEADLESS_BUSINESS}/${metaSiteId}/drop${dropAttribution(clientName())}`,
|
|
17159
|
+
{
|
|
17160
|
+
method: "POST",
|
|
17161
|
+
headers: { ...authHeaders },
|
|
17162
|
+
// fetch sets the multipart boundary itself
|
|
17163
|
+
body: buildFormData(siteFiles)
|
|
17164
|
+
}
|
|
17165
|
+
),
|
|
17166
|
+
"drop"
|
|
17167
|
+
);
|
|
17168
|
+
return {
|
|
17169
|
+
content: [
|
|
17170
|
+
{
|
|
17171
|
+
type: "text",
|
|
17172
|
+
text: formatDropResult({
|
|
17173
|
+
metaSiteId,
|
|
17174
|
+
siteUrl: dropped.siteUrl,
|
|
17175
|
+
uploadId: dropped.uploadId,
|
|
17176
|
+
files: siteFiles.map((f) => f.path)
|
|
17177
|
+
})
|
|
17178
|
+
}
|
|
17179
|
+
]
|
|
17180
|
+
};
|
|
17181
|
+
} catch (err) {
|
|
17182
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17183
|
+
logger.log(`[UploadHeadlessWebsiteFiles] Error: ${message}`);
|
|
17184
|
+
rethrowIfUnauthorized(err);
|
|
17185
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
17186
|
+
return errorResult2(message);
|
|
17187
|
+
}
|
|
17188
|
+
}
|
|
17189
|
+
);
|
|
17190
|
+
registered?.update?.({
|
|
17191
|
+
_meta: { ...registered._meta, "openai/fileParams": ["attachments"] }
|
|
17192
|
+
});
|
|
17193
|
+
}
|
|
17194
|
+
|
|
16722
17195
|
// src/code-mode/index.ts
|
|
16723
17196
|
var defaultCodeModeFetch = (url, init) => fetch(url, init);
|
|
16724
17197
|
var SEARCH_TOOL_NAME = "SearchWixAPISpec";
|
|
@@ -16916,7 +17389,7 @@ function addExecuteTool(server, options) {
|
|
|
16916
17389
|
} = options;
|
|
16917
17390
|
const executeUrl = `${codeModeBaseUrl}/api/code-mode/execute`;
|
|
16918
17391
|
const d = desc("ExecuteWixAPI");
|
|
16919
|
-
server.tool(
|
|
17392
|
+
const registered = server.tool(
|
|
16920
17393
|
EXECUTE_TOOL_NAME,
|
|
16921
17394
|
toolDescriptions?.[EXECUTE_TOOL_NAME] ?? defaultToolDescriptions.ExecuteWixAPI,
|
|
16922
17395
|
{
|
|
@@ -16933,7 +17406,7 @@ function addExecuteTool(server, options) {
|
|
|
16933
17406
|
"One sentence explaining the original user request and why you are executing code to complete it."
|
|
16934
17407
|
),
|
|
16935
17408
|
hasMutations: external_exports.boolean().describe(
|
|
16936
|
-
visitor ? "
|
|
17409
|
+
visitor ? "True if the code changes site data on the visitor\u2019s behalf (create, update, delete, import, upload); false if it only reads (get, query, list, search)." : "True if the code changes anything in the Wix account or a site (create, update, delete, publish, install, import, upload); false if it only reads (get, query, list, search)."
|
|
16937
17410
|
),
|
|
16938
17411
|
sourceDocUrls: external_exports.array(external_exports.string()).describe(d.sourceDocUrls),
|
|
16939
17412
|
files: external_exports.string().optional().describe(
|
|
@@ -16945,6 +17418,20 @@ function addExecuteTool(server, options) {
|
|
|
16945
17418
|
'Use base64 entries for small assets (icons, logos, fonts) and for files you downloaded and are re-uploading; large images/video go in by URL (`<img src="https://...">`), not in the bundle. A file containing a line exactly like the delimiter is not supported.'
|
|
16946
17419
|
].join(" ")
|
|
16947
17420
|
),
|
|
17421
|
+
// ChatGPT file attachments (`openai/fileParams`). Item shape must match
|
|
17422
|
+
// OpenAI's file schema: download_url + file_id required, the rest optional.
|
|
17423
|
+
// Declaring it is what makes the platform hand us a download URL instead
|
|
17424
|
+
// of a sandbox path. Resolved host-side into base64 `files` entries.
|
|
17425
|
+
attachments: external_exports.array(
|
|
17426
|
+
external_exports.object({
|
|
17427
|
+
download_url: external_exports.string(),
|
|
17428
|
+
file_id: external_exports.string(),
|
|
17429
|
+
file_name: external_exports.string().optional(),
|
|
17430
|
+
mime_type: external_exports.string().optional()
|
|
17431
|
+
})
|
|
17432
|
+
).optional().describe(
|
|
17433
|
+
'ChatGPT only. Files attached in the chat or created in your sandbox (any type: a zip of a site, HTML, images, PDFs): pass each file as you have it, its file id or its /mnt/data path, and ChatGPT swaps it for { download_url, file_id, file_name?, mime_type? } before the call reaches Wix. No other client does this swap, so outside ChatGPT a path or file:// URL here fails at once; leave this out and use `files`, or upload from your shell if it reaches the internet. 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.'
|
|
17434
|
+
),
|
|
16948
17435
|
// Visitor mode takes an agent-supplied token and pins the site; account/site
|
|
16949
17436
|
// mode lets the agent pick the site.
|
|
16950
17437
|
...visitor ? {
|
|
@@ -16957,9 +17444,20 @@ function addExecuteTool(server, options) {
|
|
|
16957
17444
|
)
|
|
16958
17445
|
}
|
|
16959
17446
|
},
|
|
16960
|
-
|
|
17447
|
+
// openWorldHint true: agent code picks the Wix endpoints at run time (and may
|
|
17448
|
+
// receive platform-resolved attachment URLs); the sandbox's host allowlist
|
|
17449
|
+
// bounds it, but the operation set is open-ended, which is what the hint means.
|
|
17450
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
16961
17451
|
async (rawArgs, extra) => {
|
|
16962
|
-
const {
|
|
17452
|
+
const {
|
|
17453
|
+
code,
|
|
17454
|
+
reason,
|
|
17455
|
+
hasMutations,
|
|
17456
|
+
sourceDocUrls,
|
|
17457
|
+
visitorToken,
|
|
17458
|
+
files,
|
|
17459
|
+
attachments
|
|
17460
|
+
} = rawArgs;
|
|
16963
17461
|
const siteId = visitor ? visitor.siteId : rawArgs.siteId;
|
|
16964
17462
|
logger.log(
|
|
16965
17463
|
`[ExecuteWixAPI]${visitor ? " (visitor)" : ""} Executing code (${code.length} chars), siteId=${siteId ?? "none"}, hasMutations=${hasMutations}, sourceDocUrls=${JSON.stringify(sourceDocUrls)}, reason="${reason}"`
|
|
@@ -16971,6 +17469,13 @@ function addExecuteTool(server, options) {
|
|
|
16971
17469
|
);
|
|
16972
17470
|
return executeTextResult(validation.error);
|
|
16973
17471
|
}
|
|
17472
|
+
let attachmentFiles = [];
|
|
17473
|
+
try {
|
|
17474
|
+
attachmentFiles = await resolveAttachments(attachments);
|
|
17475
|
+
} catch (e) {
|
|
17476
|
+
if (e instanceof AttachmentError) return executeErrorResult(e.message);
|
|
17477
|
+
throw e;
|
|
17478
|
+
}
|
|
16974
17479
|
const accountHeaders = await authStrategy?.getAccountAuthHeaders();
|
|
16975
17480
|
const siteAuthContext = !visitor && authStrategy?.getSiteAuthContext ? await authStrategy.getSiteAuthContext() : void 0;
|
|
16976
17481
|
let siteHeaders;
|
|
@@ -17005,7 +17510,8 @@ function addExecuteTool(server, options) {
|
|
|
17005
17510
|
accountToken: visitor ? visitorToken : accountHeaders?.Authorization,
|
|
17006
17511
|
accountId: accountHeaders?.["wix-account-id"],
|
|
17007
17512
|
siteAuthContext,
|
|
17008
|
-
...files ? { files } : {}
|
|
17513
|
+
...files ? { files } : {},
|
|
17514
|
+
...attachmentFiles.length ? { attachmentFiles } : {}
|
|
17009
17515
|
},
|
|
17010
17516
|
visitor ? visitorToken : accountHeaders?.Authorization ?? siteHeaders?.Authorization,
|
|
17011
17517
|
codeModeFetch
|
|
@@ -17036,6 +17542,9 @@ function addExecuteTool(server, options) {
|
|
|
17036
17542
|
}
|
|
17037
17543
|
}
|
|
17038
17544
|
);
|
|
17545
|
+
registered?.update?.({
|
|
17546
|
+
_meta: { ...registered._meta, "openai/fileParams": ["attachments"] }
|
|
17547
|
+
});
|
|
17039
17548
|
}
|
|
17040
17549
|
|
|
17041
17550
|
// ../../node_modules/@wix/panorama-common-shared/dist/esm/types/enum-types.js
|
|
@@ -19083,6 +19592,13 @@ function createWixToolkit(options) {
|
|
|
19083
19592
|
maxResponseSize
|
|
19084
19593
|
});
|
|
19085
19594
|
}
|
|
19595
|
+
if (requestedTools.has("UploadHeadlessWebsiteFiles") && !disableTools?.includes("UploadHeadlessWebsiteFiles")) {
|
|
19596
|
+
addUploadHeadlessWebsiteFilesTool(server, {
|
|
19597
|
+
authStrategy,
|
|
19598
|
+
toolDescriptions: effectiveToolDescriptions,
|
|
19599
|
+
clientName
|
|
19600
|
+
});
|
|
19601
|
+
}
|
|
19086
19602
|
const requestedGraphqlTools = GRAPHQL_TOOL_NAMES.filter(
|
|
19087
19603
|
(name) => requestedTools.has(name)
|
|
19088
19604
|
);
|