@wix/mcp 1.0.91 → 1.0.93

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.
@@ -36655,9 +36655,12 @@ var runSemanticSearch = async (toolName, toolParams, maxResults = 20, rerank = f
36655
36655
  );
36656
36656
  return data?.results;
36657
36657
  };
36658
- var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult) => {
36658
+ var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20, linesInEachResult, extraDocumentTypes) => {
36659
36659
  const startTime = Date.now();
36660
- logger2.log(`[SemanticSearch/v2] Tool: ${toolName}, Query: "${searchTerm}"`);
36660
+ const documentTypes = extraDocumentTypes?.length ? [toolName, ...extraDocumentTypes] : void 0;
36661
+ logger2.log(
36662
+ `[SemanticSearch/v2] Tool: ${documentTypes ? documentTypes.join("+") : toolName}, Query: "${searchTerm}"`
36663
+ );
36661
36664
  const response = await fetch(
36662
36665
  "https://www.wixapis.com/mcp-docs-search/v1/docs/search/markdown",
36663
36666
  {
@@ -36668,7 +36671,7 @@ var runPlatformizedSemanticSearch = async (toolName, searchTerm, maxResults = 20
36668
36671
  },
36669
36672
  body: JSON.stringify({
36670
36673
  search_term: searchTerm,
36671
- document_type: toolName,
36674
+ ...documentTypes ? { document_types: documentTypes } : { document_type: toolName },
36672
36675
  maximum_results: maxResults,
36673
36676
  lines_in_each_result: linesInEachResult
36674
36677
  })
@@ -36697,14 +36700,16 @@ var runSemanticSearchAndFormat = async ({
36697
36700
  maxResults = DEFAULT_MAX_RESULTS,
36698
36701
  rerank,
36699
36702
  linesInEachResult,
36700
- usePlatformizedSearch = true
36703
+ usePlatformizedSearch = true,
36704
+ extraDocumentTypes
36701
36705
  }) => {
36702
36706
  if (usePlatformizedSearch) {
36703
36707
  return runPlatformizedSemanticSearch(
36704
36708
  toolName,
36705
36709
  toolParams.searchTerm,
36706
36710
  maxResults,
36707
- linesInEachResult
36711
+ linesInEachResult,
36712
+ extraDocumentTypes
36708
36713
  );
36709
36714
  } else {
36710
36715
  const results = await runSemanticSearch(
@@ -37295,9 +37300,11 @@ var SITE_CREATION_COPY = {
37295
37300
  }
37296
37301
  };
37297
37302
  var SITE_GUIDANCE = dedent_default`
37298
- A new-site request has 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.
37303
+ First decide which of two different things is being asked for:
37304
+ - The site is code the user or you control — hand-written HTML/CSS/JS, a static build, a zip, another AI builder's export, or a page or design you made earlier in this conversation — and it should go live on Wix. This is also the route whenever the user wants to keep working on the site here, with you: a Wix Headless site is a regular coded site, so you keep full control of the design and can change and redeploy it from this conversation at any time, and Wix business capabilities can be added to it as a backend when they are needed. Call StartDeployWebsite: it returns the recipe to host those exact files as a Wix Headless site (updates reuse the same site and URL; the drop page is only its fallback). Do not offer AI or templates for this and do not call any site-creation tool.
37305
+ - Wix should create the site — generated with AI from a description, or started from a template — and the user will design and edit it in Wix rather than through you. That is what the site-creation tools do; they never take files you already have.
37299
37306
 
37300
- One case skips all of this: a finished page or site ALREADY EXISTS, in any form — hand-written HTML/CSS/JS, a static build, a zip, another AI builder's export, or a page you made earlier in this conversation. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool: call StartDeployWebsite, which returns the recipe to publish the files yourself (the drop page is only its fallback).
37307
+ 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.
37301
37308
 
37302
37309
  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.
37303
37310
  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.
@@ -37394,13 +37401,14 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
37394
37401
  unzip(input: string | Uint8Array): Promise<Array<{ path: string; bytes: Uint8Array; text(): string }>>;
37395
37402
  };
37396
37403
  declare const siteId: string | undefined;
37397
- // Files from the ExecuteWixAPI \`files\` param. Text entries have \`content\`;
37398
- // an entry tagged \`=== FILE: <path> base64 ===\` has encoding:'base64' and
37399
- // base64 \`content\` (a binary file — image, font).
37404
+ // Files from the ExecuteWixAPI \`files\` param, followed by the resolved
37405
+ // \`attachments\`. Text entries have \`content\`; an entry tagged
37406
+ // \`=== FILE: <path> base64 ===\` — and every attachment, path = its file name —
37407
+ // has encoding:'base64' and base64 \`content\`.
37400
37408
  declare const files: Array<{ path: string; content: string; encoding?: "base64" }>;
37401
37409
  \`\`\`
37402
37410
 
37403
- FILE UPLOADS: put every file in the \`files\` param (never inline in \`code\`). 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):
37411
+ FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments\` takes files attached in the chat or created in your file sandbox, of any type and size up to 10 MB per call (a zip of a whole site, HTML, images, PDFs): pass the files themselves, all in one call, and the platform resolves each into \`{ download_url, file_id, file_name?, mime_type? }\` — the bytes are fetched for you and 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):
37404
37412
  \`\`\`javascript
37405
37413
  // files param:
37406
37414
  // === FILE: index.html ===
@@ -37415,6 +37423,12 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
37415
37423
  return res.data;
37416
37424
  }
37417
37425
  \`\`\`
37426
+ A site zip passed through \`attachments\` is one base64 entry; unzip it and upload the entries:
37427
+ \`\`\`javascript
37428
+ // attachments: [site.zip] → files = [{ path: "site.zip", content: "<base64>", encoding: "base64" }]
37429
+ const entries = await wix.unzip(files[0].content); // [{ path, bytes, text() }]
37430
+ const mp = wix.multipart(entries);
37431
+ \`\`\`
37418
37432
  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()\`:
37419
37433
  \`\`\`javascript
37420
37434
  const r = await wix.request({ scope: "account", method: "GET", responseType: "base64", url: zipUrl });
@@ -37619,12 +37633,12 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
37619
37633
  NEVER call this tool with a missing, empty, or guessed siteId — ask first instead.
37620
37634
 
37621
37635
  Parameters — provide image input via image and/or imageUrls, OR imageBase64:
37622
- • 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.
37636
+ • 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.
37623
37637
  • 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.
37624
37638
  • imageBase64 (string): base64-encoded image + mimeType. One image at a time. Do NOT combine with image/imageUrls.
37625
37639
 
37626
37640
  Delivering the bytes — this is where uploads most often fail:
37627
- • NEVER pass a local or sandbox file path (e.g. /mnt/..., /home/user/..., /Users/...) in any field — the server cannot reach your filesystem. Use a public http(s) URL (imageUrls) or the complete base64 (imageBase64).
37641
+ • 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).
37628
37642
  • 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.
37629
37643
  `,
37630
37644
  SupportAndFeedback: dedent_default`
@@ -38121,14 +38135,14 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
38121
38135
  WixSiteBuilder: dedent_default`
38122
38136
  ${SITE_GUIDANCE}
38123
38137
 
38124
- Tool for generating a Wix site with AI (creating / building a site in Wix Harmony).
38138
+ Tool for generating a Wix site with AI (creating / building a site in Wix Harmony) from a description. It does not take pages or files you already have — those go to StartDeployWebsite.
38125
38139
 
38126
38140
  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.
38127
38141
  Never default to this tool: if the user hasn't explicitly named AI, don't call this tool — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.
38128
38142
 
38129
38143
  For templates or Wix Studio, call \`SearchSiteTemplates\`; for a specific template by \`metaSiteId\` or \`templateId\`, call \`CreateSiteFromTemplate\`; for a headless or classic Wix Editor site, or manual/account-API creation, call \`CreateWixBusinessGuide\`.
38130
38144
 
38131
- Do NOT suggest HTML code, prompt templates, or alternative approaches — actually build the site through this tool.
38145
+ 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.)
38132
38146
  `,
38133
38147
  SearchSiteTemplates: dedent_default`
38134
38148
  ${SITE_GUIDANCE}
@@ -38172,8 +38186,8 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
38172
38186
  Set \`hasMutations: true\` for any document containing a mutation root field.
38173
38187
  `,
38174
38188
  StartDeployWebsite: dedent_default`
38175
- 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: 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, publish, put online, make it live, get a URL, or upload my site / index.html.
38176
- Also for updating a site deployed this way (replace its files; same site, same URL) and for implementing a backend on one (stores, bookings, CMS, members) 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.
38189
+ 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, publish, put online, make it live, get a URL, update my site, or upload my site / index.html.
38190
+ 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.
38177
38191
  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).
38178
38192
  Returns the full recipe; follow it and run the flow yourself. The drop page is only the fallback when you can't.
38179
38193
  NOT for: a screenshot, mockup or reference image of a site the user WANTS (nothing exists to deploy yet — that is site creation: WixSiteBuilder / templates); a Claude Design share 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).
@@ -38876,7 +38890,7 @@ var paramDescriptions = {
38876
38890
  },
38877
38891
  SearchWixRESTDocumentation: {
38878
38892
  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.`,
38879
- maxResults: "The maximum number of results to return, default is 10, max is 15",
38893
+ maxResults: "The maximum number of results to return (1\u201317, default 17). Results mix REST API documentation with matching recipes and skills.",
38880
38894
  reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
38881
38895
  },
38882
38896
  WixBusinessFlowsDocumentation: {
@@ -38897,7 +38911,7 @@ var paramDescriptions = {
38897
38911
  },
38898
38912
  SearchWixHeadlessDocumentation: {
38899
38913
  searchTerm: "The search term to search for in the Headless Documentation",
38900
- maxResults: "The maximum number of results to return, default is 10, max is 15"
38914
+ maxResults: "The maximum number of results to return (1\u201317, default 17). Results mix Headless documentation with matching recipes and skills."
38901
38915
  },
38902
38916
  SearchWixVeloDocumentation: {
38903
38917
  searchTerm: "The search term to search for in the Velo Documentation",
@@ -38925,7 +38939,7 @@ var paramDescriptions = {
38925
38939
  },
38926
38940
  UploadImageToWixSite: {
38927
38941
  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.",
38928
- 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 attached files are automatically resolved to these fields by the platform, so just include them here. 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.",
38942
+ 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.",
38929
38943
  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.",
38930
38944
  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.',
38931
38945
  mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
@@ -39329,7 +39343,17 @@ ${articleContent}${nextSteps}`;
39329
39343
  }
39330
39344
 
39331
39345
  // src/docs/start-deploy-website.ts
39332
- var START_DEPLOY_WEBSITE_BRIEF_DESCRIPTION = "A few sentences covering: the user's request (quoted if short); what the site is \u2014 its purpose and who it is for \u2014 and its current form (HTML/CSS/JS files, a static build, a zip, another builder's export, a design created in this conversation), roughly how many files, and whether it includes images; what the site must be able to do for its visitors beyond showing pages, if anything \u2014 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 \u2014 described in your own words, or a note that it is a plain static site with no such needs; which agent or product you are and the harness you run in.";
39346
+ var START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS = {
39347
+ userRequest: "The user's request that led here, quoted if short, otherwise summarized in a sentence.",
39348
+ siteDescription: "What the site is: its purpose and who it is for. One or two sentences.",
39349
+ siteForm: "The form the site exists in right now (HTML/CSS/JS files, a static build, a zip, another builder's export, a design created in this conversation, or something else), roughly how many files, and whether it includes images.",
39350
+ 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.",
39351
+ agentIdentity: "Which agent or product you are and the harness you run in.",
39352
+ 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; and which Wix identity you act with \u2014 the user's login through this MCP, a CLI login, or none."
39353
+ };
39354
+ var START_DEPLOY_WEBSITE_PARAMS = Object.keys(
39355
+ START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS
39356
+ );
39333
39357
  var START_DEPLOY_WEBSITE_TOOL_NAME = "StartDeployWebsite";
39334
39358
  var START_DEPLOY_WEBSITE_RECIPE_URL = "https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md";
39335
39359
  var DROP_PAGE_URL = "https://www.wix.com/headless/drop?utm_campaign=mcp";
@@ -39355,7 +39379,12 @@ function addStartDeployWebsiteTool(server, disableTools = [], toolDescriptions,
39355
39379
  START_DEPLOY_WEBSITE_TOOL_NAME,
39356
39380
  toolDescriptions?.[START_DEPLOY_WEBSITE_TOOL_NAME] ?? defaultToolDescriptions[START_DEPLOY_WEBSITE_TOOL_NAME],
39357
39381
  {
39358
- brief: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_BRIEF_DESCRIPTION)
39382
+ userRequest: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.userRequest),
39383
+ siteDescription: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteDescription),
39384
+ siteForm: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteForm),
39385
+ siteFunctionality: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteFunctionality),
39386
+ agentIdentity: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity),
39387
+ wixAccess: external_exports.string().min(1).describe(START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.wixAccess)
39359
39388
  },
39360
39389
  { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
39361
39390
  async (_args, { httpClient: httpClient6 }) => {
@@ -39580,7 +39609,7 @@ var addDocsTools = (server, allowedTools = [
39580
39609
  const d2 = desc("SearchWixRESTDocumentation");
39581
39610
  return {
39582
39611
  searchTerm: external_exports.string().describe(d2.searchTerm),
39583
- maxResults: external_exports.number().describe(d2.maxResults).min(1).max(15).optional().default(15),
39612
+ maxResults: external_exports.number().describe(d2.maxResults).min(1).max(17).optional().default(17),
39584
39613
  reason: external_exports.string().describe(d2.reason)
39585
39614
  };
39586
39615
  })(),
@@ -39595,9 +39624,12 @@ var addDocsTools = (server, allowedTools = [
39595
39624
  toolParams: {
39596
39625
  searchTerm
39597
39626
  },
39598
- maxResults: Math.max(1, Math.min(maxResults ?? 15, 15)),
39627
+ maxResults: Math.max(1, Math.min(maxResults ?? 17, 17)),
39599
39628
  linesInEachResult: 30,
39600
- usePlatformizedSearch
39629
+ usePlatformizedSearch,
39630
+ // Merge recipes/skills into the REST ranking — they live in a
39631
+ // separate corpus the REST document_type doesn't cover on its own.
39632
+ extraDocumentTypes: ["SKILLS"]
39601
39633
  });
39602
39634
  const grounding = result.includes(QUERY_CAPABILITY_MARKER) ? [
39603
39635
  REST_SEARCH_API_GROUNDING_REMINDER,
@@ -39860,7 +39892,7 @@ var addDocsTools = (server, allowedTools = [
39860
39892
  const d2 = desc("SearchWixHeadlessDocumentation");
39861
39893
  return {
39862
39894
  searchTerm: external_exports.string().describe(d2.searchTerm),
39863
- maxResults: external_exports.number().describe(d2.maxResults).min(1).max(15).optional().default(15)
39895
+ maxResults: external_exports.number().describe(d2.maxResults).min(1).max(17).optional().default(17)
39864
39896
  };
39865
39897
  })(),
39866
39898
  { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
@@ -39872,8 +39904,11 @@ var addDocsTools = (server, allowedTools = [
39872
39904
  toolParams: {
39873
39905
  searchTerm
39874
39906
  },
39875
- maxResults: Math.max(1, Math.min(maxResults ?? 15, 15)),
39876
- usePlatformizedSearch
39907
+ maxResults: Math.max(1, Math.min(maxResults ?? 17, 17)),
39908
+ usePlatformizedSearch,
39909
+ // Merge recipes/skills into the headless ranking — same separate
39910
+ // corpus the WIX_HEADLESS document_type doesn't cover on its own.
39911
+ extraDocumentTypes: ["SKILLS"]
39877
39912
  });
39878
39913
  return {
39879
39914
  content: [
@@ -53105,6 +53140,189 @@ ${text.slice(0, 2e3)}`
53105
53140
  );
53106
53141
  }
53107
53142
 
53143
+ // src/code-mode/attachments.ts
53144
+ var MAX_ATTACHMENTS_BYTES = 10 * 1024 * 1024;
53145
+ var MAX_REDIRECTS = 4;
53146
+ var OAI_USERCONTENT_HOST = /^[a-z0-9-]+\.oaiusercontent\.com$/;
53147
+ var OAI_BLOB_ACCOUNTS = new Set(
53148
+ [
53149
+ "australiaeast",
53150
+ "brazilsouth",
53151
+ "cacentral",
53152
+ "centralindia",
53153
+ "centralus",
53154
+ "centralusdr",
53155
+ "denmarkeast",
53156
+ "eastus",
53157
+ "eastus2",
53158
+ "indiasocentral",
53159
+ "italynorth",
53160
+ "japaneast",
53161
+ "koreacentral",
53162
+ "northcentralus",
53163
+ "northeu",
53164
+ "nznorth",
53165
+ "polandcentral",
53166
+ "seasia",
53167
+ "southcentralus",
53168
+ "southeastus3",
53169
+ "ukwest",
53170
+ "westcentralus",
53171
+ "westus",
53172
+ "westus2",
53173
+ "westus3"
53174
+ ].map((region) => `oaisdmntpr${region}.blob.core.windows.net`)
53175
+ );
53176
+ function isAllowedAttachmentHost(hostname2) {
53177
+ const h2 = hostname2.toLowerCase();
53178
+ return OAI_USERCONTENT_HOST.test(h2) || OAI_BLOB_ACCOUNTS.has(h2);
53179
+ }
53180
+ function checkAttachmentUrl(url) {
53181
+ if (url.protocol !== "https:") return "is not an https URL";
53182
+ if (url.port) return "uses a non-default port";
53183
+ if (!isAllowedAttachmentHost(url.hostname)) {
53184
+ return `host "${url.hostname}" is not a ChatGPT file host`;
53185
+ }
53186
+ return void 0;
53187
+ }
53188
+ var AttachmentError = class extends Error {
53189
+ };
53190
+ function attachmentPath(a2, url) {
53191
+ let fromUrl = "";
53192
+ try {
53193
+ fromUrl = decodeURIComponent(
53194
+ url.pathname.split("/").filter(Boolean).pop() ?? ""
53195
+ );
53196
+ } catch {
53197
+ fromUrl = "";
53198
+ }
53199
+ const raw = (a2.file_name?.trim() || fromUrl || a2.file_id).replace(/\\/g, "/");
53200
+ const segments = raw.split("/").filter((s2) => s2 && s2 !== "." && s2 !== "..");
53201
+ return segments.join("/") || a2.file_id;
53202
+ }
53203
+ function toBase64(bytes2) {
53204
+ let binary = "";
53205
+ const chunk = 32768;
53206
+ for (let i = 0; i < bytes2.length; i += chunk) {
53207
+ binary += String.fromCharCode(...bytes2.subarray(i, i + chunk));
53208
+ }
53209
+ return btoa(binary);
53210
+ }
53211
+ async function fetchAllowed(start, fetchImpl, label) {
53212
+ let url = start;
53213
+ for (let hop = 0; ; hop++) {
53214
+ const res = await fetchImpl(url.toString(), { redirect: "manual" });
53215
+ const location = res.headers.get("location");
53216
+ const isRedirect = res.status >= 300 && res.status < 400 && location;
53217
+ if (!isRedirect) return res;
53218
+ await res.body?.cancel().catch(() => void 0);
53219
+ if (hop >= MAX_REDIRECTS) {
53220
+ throw new AttachmentError(`${label}: too many redirects.`);
53221
+ }
53222
+ let next;
53223
+ try {
53224
+ next = new URL(location, url);
53225
+ } catch {
53226
+ throw new AttachmentError(`${label}: redirect to an invalid URL.`);
53227
+ }
53228
+ const problem = checkAttachmentUrl(next);
53229
+ if (problem) {
53230
+ throw new AttachmentError(
53231
+ `${label}: the download redirected to a URL that ${problem}; refused.`
53232
+ );
53233
+ }
53234
+ url = next;
53235
+ }
53236
+ }
53237
+ async function readWithin(res, budget, overBudget) {
53238
+ const declared = Number(res.headers.get("content-length") ?? 0);
53239
+ if (declared > budget) throw overBudget();
53240
+ if (!res.body) {
53241
+ const bytes2 = new Uint8Array(await res.arrayBuffer());
53242
+ if (bytes2.byteLength > budget) throw overBudget();
53243
+ return bytes2;
53244
+ }
53245
+ const reader = res.body.getReader();
53246
+ const chunks = [];
53247
+ let total = 0;
53248
+ for (; ; ) {
53249
+ const { done, value } = await reader.read();
53250
+ if (done) break;
53251
+ total += value.byteLength;
53252
+ if (total > budget) {
53253
+ await reader.cancel().catch(() => void 0);
53254
+ throw overBudget();
53255
+ }
53256
+ chunks.push(value);
53257
+ }
53258
+ const out = new Uint8Array(total);
53259
+ let offset = 0;
53260
+ for (const c of chunks) {
53261
+ out.set(c, offset);
53262
+ offset += c.byteLength;
53263
+ }
53264
+ return out;
53265
+ }
53266
+ async function resolveAttachments(attachments, fetchImpl = fetch) {
53267
+ if (!Array.isArray(attachments) || attachments.length === 0) return [];
53268
+ const items = [];
53269
+ attachments.forEach((raw, i) => {
53270
+ const a2 = typeof raw === "object" && raw;
53271
+ const downloadUrl = a2 && typeof a2.download_url === "string" ? a2.download_url : "";
53272
+ let url;
53273
+ try {
53274
+ url = /^https?:\/\//i.test(downloadUrl) ? new URL(downloadUrl) : void 0;
53275
+ } catch {
53276
+ url = void 0;
53277
+ }
53278
+ if (!a2 || !url) {
53279
+ throw new AttachmentError(
53280
+ `attachments[${i}] was not resolved to a download URL (got ${JSON.stringify(
53281
+ typeof raw === "string" ? raw : (a2 && a2.download_url) ?? raw
53282
+ )}). The platform did not hydrate this attachment; retry once, and if it still arrives like this put the file in the \`files\` bundle instead (\`=== FILE: <path> base64 ===\` for binary).`
53283
+ );
53284
+ }
53285
+ const problem = checkAttachmentUrl(url);
53286
+ if (problem) {
53287
+ throw new AttachmentError(
53288
+ `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.`
53289
+ );
53290
+ }
53291
+ items.push({ a: a2, url });
53292
+ });
53293
+ let remaining = MAX_ATTACHMENTS_BYTES;
53294
+ const out = [];
53295
+ for (const [i, { a: a2, url }] of items.entries()) {
53296
+ const path = attachmentPath(a2, url);
53297
+ const label = `attachments[${i}] (${path})`;
53298
+ let res;
53299
+ try {
53300
+ res = await fetchAllowed(url, fetchImpl, label);
53301
+ } catch (e) {
53302
+ if (e instanceof AttachmentError) throw e;
53303
+ throw new AttachmentError(
53304
+ `${label}: download failed (${e.message}).`
53305
+ );
53306
+ }
53307
+ if (!res.ok) {
53308
+ await res.body?.cancel().catch(() => void 0);
53309
+ throw new AttachmentError(
53310
+ `${label}: download returned HTTP ${res.status}. The download_url may have expired; attach the file again.`
53311
+ );
53312
+ }
53313
+ const bytes2 = await readWithin(
53314
+ res,
53315
+ remaining,
53316
+ () => new AttachmentError(
53317
+ `${label} pushes the attachments past ${MAX_ATTACHMENTS_BYTES} bytes in total for this call; split the upload across calls.`
53318
+ )
53319
+ );
53320
+ remaining -= bytes2.byteLength;
53321
+ out.push({ path, content: toBase64(bytes2), encoding: "base64" });
53322
+ }
53323
+ return out;
53324
+ }
53325
+
53108
53326
  // src/code-mode/validation.ts
53109
53327
  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.';
53110
53328
  function validateExecuteCode(code) {
@@ -53337,7 +53555,7 @@ function addExecuteTool(server, options) {
53337
53555
  } = options;
53338
53556
  const executeUrl = `${codeModeBaseUrl}/api/code-mode/execute`;
53339
53557
  const d2 = desc("ExecuteWixAPI");
53340
- server.tool(
53558
+ const registered = server.tool(
53341
53559
  EXECUTE_TOOL_NAME,
53342
53560
  toolDescriptions?.[EXECUTE_TOOL_NAME] ?? defaultToolDescriptions.ExecuteWixAPI,
53343
53561
  {
@@ -53366,6 +53584,20 @@ function addExecuteTool(server, options) {
53366
53584
  '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.'
53367
53585
  ].join(" ")
53368
53586
  ),
53587
+ // ChatGPT file attachments (`openai/fileParams`). Item shape must match
53588
+ // OpenAI's file schema: download_url + file_id required, the rest optional.
53589
+ // Declaring it is what makes the platform hand us a download URL instead
53590
+ // of a sandbox path. Resolved host-side into base64 `files` entries.
53591
+ attachments: external_exports.array(
53592
+ external_exports.object({
53593
+ download_url: external_exports.string(),
53594
+ file_id: external_exports.string(),
53595
+ file_name: external_exports.string().optional(),
53596
+ mime_type: external_exports.string().optional()
53597
+ })
53598
+ ).optional().describe(
53599
+ 'Files attached in this chat, or files you created in your own file sandbox (any type: a zip of a site, HTML, images, PDFs). Pass the files themselves and the platform resolves each into { download_url, file_id, file_name?, mime_type? }; 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.'
53600
+ ),
53369
53601
  // Visitor mode takes an agent-supplied token and pins the site; account/site
53370
53602
  // mode lets the agent pick the site.
53371
53603
  ...visitor ? {
@@ -53380,7 +53612,15 @@ function addExecuteTool(server, options) {
53380
53612
  },
53381
53613
  { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
53382
53614
  async (rawArgs, extra) => {
53383
- const { code, reason, hasMutations, sourceDocUrls, visitorToken, files } = rawArgs;
53615
+ const {
53616
+ code,
53617
+ reason,
53618
+ hasMutations,
53619
+ sourceDocUrls,
53620
+ visitorToken,
53621
+ files,
53622
+ attachments
53623
+ } = rawArgs;
53384
53624
  const siteId = visitor ? visitor.siteId : rawArgs.siteId;
53385
53625
  logger2.log(
53386
53626
  `[ExecuteWixAPI]${visitor ? " (visitor)" : ""} Executing code (${code.length} chars), siteId=${siteId ?? "none"}, hasMutations=${hasMutations}, sourceDocUrls=${JSON.stringify(sourceDocUrls)}, reason="${reason}"`
@@ -53392,6 +53632,13 @@ function addExecuteTool(server, options) {
53392
53632
  );
53393
53633
  return executeTextResult(validation.error);
53394
53634
  }
53635
+ let attachmentFiles = [];
53636
+ try {
53637
+ attachmentFiles = await resolveAttachments(attachments);
53638
+ } catch (e) {
53639
+ if (e instanceof AttachmentError) return executeErrorResult(e.message);
53640
+ throw e;
53641
+ }
53395
53642
  const accountHeaders = await authStrategy?.getAccountAuthHeaders();
53396
53643
  const siteAuthContext = !visitor && authStrategy?.getSiteAuthContext ? await authStrategy.getSiteAuthContext() : void 0;
53397
53644
  let siteHeaders;
@@ -53426,7 +53673,8 @@ function addExecuteTool(server, options) {
53426
53673
  accountToken: visitor ? visitorToken : accountHeaders?.Authorization,
53427
53674
  accountId: accountHeaders?.["wix-account-id"],
53428
53675
  siteAuthContext,
53429
- ...files ? { files } : {}
53676
+ ...files ? { files } : {},
53677
+ ...attachmentFiles.length ? { attachmentFiles } : {}
53430
53678
  },
53431
53679
  visitor ? visitorToken : accountHeaders?.Authorization ?? siteHeaders?.Authorization,
53432
53680
  codeModeFetch
@@ -53457,6 +53705,9 @@ function addExecuteTool(server, options) {
53457
53705
  }
53458
53706
  }
53459
53707
  );
53708
+ registered?.update?.({
53709
+ _meta: { ...registered._meta, "openai/fileParams": ["attachments"] }
53710
+ });
53460
53711
  }
53461
53712
 
53462
53713
  // src/toolkit.ts