@wix/mcp 1.0.97 → 1.0.98
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 +2 -8
- package/build/cjs/index.cjs +444 -11
- package/build/dts/api-call/availability-tool/index.js +1 -4
- package/build/dts/api-call/suggested-domain-tool/index.js +1 -4
- package/build/dts/auth/index.d.ts +14 -3
- package/build/dts/toolkit-entry.d.cts +14 -3
- package/build/dts/toolkit-entry.d.ts +14 -3
- package/build/dts/wixel/api.d.ts +23 -0
- package/build/dts/wixel/api.js +47 -0
- package/build/dts/wixel/app-icon-files.d.ts +19 -0
- package/build/dts/wixel/app-icon-files.js +77 -0
- package/build/dts/wixel/index.d.ts +1 -0
- package/build/dts/wixel/index.js +4 -0
- package/build/dts/wixel/wixel-asset-files-tool.d.ts +29 -0
- package/build/dts/wixel/wixel-asset-files-tool.js +327 -0
- package/build/dts/wixel/wixel-session-entries.d.ts +28 -0
- package/build/dts/wixel/wixel-session-entries.js +34 -1
- package/build/dts/wixel/wixel-session-tools.js +13 -2
- package/build/esm/index.js +443 -11
- package/package.json +2 -2
package/build/bin-standalone.js
CHANGED
|
@@ -100102,10 +100102,7 @@ function addSuggestedDomainTool(server2, authStrategy2) {
|
|
|
100102
100102
|
openWorldHint: false
|
|
100103
100103
|
},
|
|
100104
100104
|
_meta: {
|
|
100105
|
-
securitySchemes: [
|
|
100106
|
-
{ type: "noauth" },
|
|
100107
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
100108
|
-
],
|
|
100105
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
100109
100106
|
// Model-visible so hosts (e.g. Microsoft 365 Copilot) can call it directly and show it
|
|
100110
100107
|
// in their debug info; 'app' keeps it callable from the site-builder widget.
|
|
100111
100108
|
ui: { visibility: ["app", "model"] },
|
|
@@ -100331,10 +100328,7 @@ function addDomainAvailabilityTool(server2) {
|
|
|
100331
100328
|
openWorldHint: false
|
|
100332
100329
|
},
|
|
100333
100330
|
_meta: {
|
|
100334
|
-
securitySchemes: [
|
|
100335
|
-
{ type: "noauth" },
|
|
100336
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
100337
|
-
],
|
|
100331
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
100338
100332
|
ui: { visibility: ["app", "model"] },
|
|
100339
100333
|
"openai/toolInvocation/invoking": "Checking domain availability",
|
|
100340
100334
|
"openai/toolInvocation/invoked": "Domain availability checked"
|
package/build/cjs/index.cjs
CHANGED
|
@@ -20923,6 +20923,7 @@ __export(index_exports, {
|
|
|
20923
20923
|
addEditSiteWidgetTool: () => addEditSiteWidgetTool,
|
|
20924
20924
|
addExecuteCodeModeTool: () => addExecuteCodeModeTool,
|
|
20925
20925
|
addGetProfileTool: () => addGetProfileTool,
|
|
20926
|
+
addGetWixelAssetFilesTool: () => addGetWixelAssetFilesTool,
|
|
20926
20927
|
addGetWixelAssetTool: () => addGetWixelAssetTool,
|
|
20927
20928
|
addGetWixelJobStatusTool: () => addGetWixelJobStatusTool,
|
|
20928
20929
|
addGetWixelProjectAssetsTool: () => addGetWixelProjectAssetsTool,
|
|
@@ -49123,10 +49124,7 @@ function addSuggestedDomainTool(server, authStrategy) {
|
|
|
49123
49124
|
openWorldHint: false
|
|
49124
49125
|
},
|
|
49125
49126
|
_meta: {
|
|
49126
|
-
securitySchemes: [
|
|
49127
|
-
{ type: "noauth" },
|
|
49128
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
49129
|
-
],
|
|
49127
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
49130
49128
|
// Model-visible so hosts (e.g. Microsoft 365 Copilot) can call it directly and show it
|
|
49131
49129
|
// in their debug info; 'app' keeps it callable from the site-builder widget.
|
|
49132
49130
|
ui: { visibility: ["app", "model"] },
|
|
@@ -49352,10 +49350,7 @@ function addDomainAvailabilityTool(server) {
|
|
|
49352
49350
|
openWorldHint: false
|
|
49353
49351
|
},
|
|
49354
49352
|
_meta: {
|
|
49355
|
-
securitySchemes: [
|
|
49356
|
-
{ type: "noauth" },
|
|
49357
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
49358
|
-
],
|
|
49353
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
49359
49354
|
ui: { visibility: ["app", "model"] },
|
|
49360
49355
|
"openai/toolInvocation/invoking": "Checking domain availability",
|
|
49361
49356
|
"openai/toolInvocation/invoked": "Domain availability checked"
|
|
@@ -51915,6 +51910,59 @@ async function getStylePresetsByIds2({
|
|
|
51915
51910
|
};
|
|
51916
51911
|
});
|
|
51917
51912
|
}
|
|
51913
|
+
function wixelHostClient({
|
|
51914
|
+
siteAuthHeaders,
|
|
51915
|
+
apiBaseURL = WWW_ASSET_V1_ASSET_API_BASE_URL,
|
|
51916
|
+
conductorOverride
|
|
51917
|
+
}) {
|
|
51918
|
+
return (0, import_http_client14.createHttpClient)({
|
|
51919
|
+
headers: {
|
|
51920
|
+
...COMMON_HEADERS,
|
|
51921
|
+
...siteAuthHeaders,
|
|
51922
|
+
...conductorOverride ? { ...CONDUCTOR_OVERRIDE_HEADERS } : {}
|
|
51923
|
+
},
|
|
51924
|
+
baseURL: apiBaseURL
|
|
51925
|
+
});
|
|
51926
|
+
}
|
|
51927
|
+
var WIXEL_HOST_EXPORT_ASSET_PATH = "/_api/wixel/v1/export/asset";
|
|
51928
|
+
var WIXEL_HOST_EXPORT_STATE_PATH = "/_api/wixel/v1/export/state";
|
|
51929
|
+
async function startAssetExport({
|
|
51930
|
+
assetId,
|
|
51931
|
+
params,
|
|
51932
|
+
...options
|
|
51933
|
+
}) {
|
|
51934
|
+
const { data } = await wixelHostClient(options).post(
|
|
51935
|
+
WIXEL_HOST_EXPORT_ASSET_PATH,
|
|
51936
|
+
{
|
|
51937
|
+
projectId: options.projectId,
|
|
51938
|
+
assetId,
|
|
51939
|
+
assetType: options.assetType,
|
|
51940
|
+
...params ? { params } : {}
|
|
51941
|
+
}
|
|
51942
|
+
);
|
|
51943
|
+
if (!data?.jobId) {
|
|
51944
|
+
throw new Error("Wixel did not start an export job for this asset");
|
|
51945
|
+
}
|
|
51946
|
+
return data.jobId;
|
|
51947
|
+
}
|
|
51948
|
+
async function getAssetExportState({
|
|
51949
|
+
jobId,
|
|
51950
|
+
...options
|
|
51951
|
+
}) {
|
|
51952
|
+
const { data } = await wixelHostClient(options).post(WIXEL_HOST_EXPORT_STATE_PATH, {
|
|
51953
|
+
jobId,
|
|
51954
|
+
projectId: options.projectId,
|
|
51955
|
+
assetType: options.assetType
|
|
51956
|
+
});
|
|
51957
|
+
const state = data?.state ?? {};
|
|
51958
|
+
return {
|
|
51959
|
+
complete: state.complete === true,
|
|
51960
|
+
progress: state.progress ?? null,
|
|
51961
|
+
url: state.url ?? null,
|
|
51962
|
+
errorDescription: state.errorDescription ?? null,
|
|
51963
|
+
title: state.title ?? null
|
|
51964
|
+
};
|
|
51965
|
+
}
|
|
51918
51966
|
|
|
51919
51967
|
// src/wixel/get-wixel-job-status-tool.ts
|
|
51920
51968
|
var TOOL_NAME = "getWixelJobStatus";
|
|
@@ -52571,6 +52619,29 @@ async function getWixelSessionProjectId({
|
|
|
52571
52619
|
const session = data?.sessions?.find((entry) => entry.id === sessionId);
|
|
52572
52620
|
return session?.projectId ?? null;
|
|
52573
52621
|
}
|
|
52622
|
+
var PREVIEW_TOOLS = /* @__PURE__ */ new Set([
|
|
52623
|
+
"generate_image",
|
|
52624
|
+
"edit_image",
|
|
52625
|
+
"remove_image_background"
|
|
52626
|
+
]);
|
|
52627
|
+
var MAX_PREVIEW_URLS = 4;
|
|
52628
|
+
function collectPreviewUrls(messages, askIndex, turnId) {
|
|
52629
|
+
const urls = [];
|
|
52630
|
+
for (let i = 0; i <= askIndex; i++) {
|
|
52631
|
+
const message = messages[i];
|
|
52632
|
+
if (turnId && message.turnId !== turnId) continue;
|
|
52633
|
+
for (const part of message.parts ?? []) {
|
|
52634
|
+
const response = part.functionResponse;
|
|
52635
|
+
if (!response?.name || !PREVIEW_TOOLS.has(response.name)) continue;
|
|
52636
|
+
for (const url of extractMediaUrls(
|
|
52637
|
+
JSON.stringify(response.response ?? "")
|
|
52638
|
+
)) {
|
|
52639
|
+
if (!urls.includes(url)) urls.push(url);
|
|
52640
|
+
}
|
|
52641
|
+
}
|
|
52642
|
+
}
|
|
52643
|
+
return urls.slice(-MAX_PREVIEW_URLS);
|
|
52644
|
+
}
|
|
52574
52645
|
async function getWixelSessionActivity({
|
|
52575
52646
|
sessionId,
|
|
52576
52647
|
...base
|
|
@@ -52662,10 +52733,12 @@ async function getWixelPendingQuestion({
|
|
|
52662
52733
|
const questions = call.args?.questions ?? [];
|
|
52663
52734
|
if (!questions.length) continue;
|
|
52664
52735
|
if (ownTurn && message.turnId !== ownTurn) return null;
|
|
52736
|
+
const previewUrls = collectPreviewUrls(messages, i, message.turnId);
|
|
52665
52737
|
return {
|
|
52666
52738
|
toolCallId: call.id,
|
|
52667
52739
|
turnId: message.turnId,
|
|
52668
|
-
questions
|
|
52740
|
+
questions,
|
|
52741
|
+
...previewUrls.length ? { previewUrls } : {}
|
|
52669
52742
|
};
|
|
52670
52743
|
}
|
|
52671
52744
|
if (part.text || part.functionCall || part.functionResponse) {
|
|
@@ -53026,14 +53099,20 @@ Call this when get_wixel_task flags a question, or its reply asks the user somet
|
|
|
53026
53099
|
const summary = pending.questions.map(
|
|
53027
53100
|
(question, index) => `${index + 1}. [${question.kind}] ${question.title ?? ""}${question.customCanvas?.componentName ? ` (${question.customCanvas.componentName})` : ""}`
|
|
53028
53101
|
).join("\n");
|
|
53102
|
+
const previewUrls = pending.previewUrls ?? [];
|
|
53103
|
+
const previewText = previewUrls.length ? `
|
|
53104
|
+
|
|
53105
|
+
Sample made so far (what the user is approving):
|
|
53106
|
+
${previewUrls.map((url) => `- ${url}`).join("\n")}` : "";
|
|
53107
|
+
const previewInstruction = previewUrls.length ? "Show the user the sample first: give the link(s) above and, when you can write files, save them under assets/_preview/ (for example assets/_preview/sample-1.png) and name the path, so the user sees the look before answering. " : "";
|
|
53029
53108
|
const responseText = widgetShown ? `An interactive question widget is displayed to the user (questionId: ${pending.toolCallId}):
|
|
53030
53109
|
${summary}
|
|
53031
53110
|
<agent_instruction>Do NOT repeat the question content as text \u2014 the user answers inside the widget, which sends the answer to Wixel itself. This is NOT a turn-taking situation: the result of the user's widget answer reaches YOU only through get_wixel_task \u2014 nothing wakes you if you stop, and ending your turn BREAKS the flow (the result would never be delivered).
|
|
53032
53111
|
Therefore: briefly tell the user to answer in the widget, then keep calling get_wixel_task (taskId: "${args.taskId}", metaSiteId: "${args.metaSiteId}", waitSeconds: 25) in a continuous loop, and do NOT end your turn until it reports the turn done, however long it takes (the user may think for a while, then generation takes minutes). If it names a newer taskId (the widget's answer opened a new turn), follow that one.
|
|
53033
53112
|
- Polls that flag questionId ${pending.toolCallId} mean the user has not answered THIS question yet \u2014 do NOT call get_wixel_pending_question again for it, just keep polling.
|
|
53034
53113
|
- When the turn is done: if the response flags a NEW interactive question, call get_wixel_pending_question again (it renders the next widget). Otherwise present the reply to the user as a normal chat message, always including the "Edit in Wixel" link when provided.</agent_instruction>` : `The Wixel agent asked:
|
|
53035
|
-
${summary}
|
|
53036
|
-
<agent_instruction
|
|
53114
|
+
${summary}${previewText}
|
|
53115
|
+
<agent_instruction>${previewInstruction}Present the question(s) with their options/content to the user, then send their answer as plain text via send_wixel_message (contextId: "${args.contextId}", metaSiteId: "${args.metaSiteId}") and poll get_wixel_task for the taskId it returns.</agent_instruction>`;
|
|
53037
53116
|
return {
|
|
53038
53117
|
isError: false,
|
|
53039
53118
|
content: [{ type: "text", text: responseText }],
|
|
@@ -53996,6 +54075,358 @@ function addWixelSessionTools(server, authStrategy, options = {}) {
|
|
|
53996
54075
|
addGetWixelTurnResultTool(server, authStrategy, shared);
|
|
53997
54076
|
}
|
|
53998
54077
|
|
|
54078
|
+
// src/wixel/app-icon-files.ts
|
|
54079
|
+
var WIX_MEDIA_FILE = /^(https:\/\/static\.wixstatic\.com\/media\/[^/?#]+)/;
|
|
54080
|
+
var ANDROID_MIPMAPS = [
|
|
54081
|
+
["mdpi", 48],
|
|
54082
|
+
["hdpi", 72],
|
|
54083
|
+
["xhdpi", 96],
|
|
54084
|
+
["xxhdpi", 144],
|
|
54085
|
+
["xxxhdpi", 192]
|
|
54086
|
+
];
|
|
54087
|
+
function wixMediaFileUrl(url) {
|
|
54088
|
+
return WIX_MEDIA_FILE.exec(url)?.[1] ?? null;
|
|
54089
|
+
}
|
|
54090
|
+
function wixMediaSquarePng(fileUrl, size) {
|
|
54091
|
+
return `${fileUrl}/v1/fill/w_${size},h_${size},al_c,q_95/icon-${size}.png`;
|
|
54092
|
+
}
|
|
54093
|
+
function buildAppIconFiles(exportedPngUrl, appName) {
|
|
54094
|
+
const file = wixMediaFileUrl(exportedPngUrl);
|
|
54095
|
+
if (!file) {
|
|
54096
|
+
return null;
|
|
54097
|
+
}
|
|
54098
|
+
const png = (path, size) => ({
|
|
54099
|
+
path,
|
|
54100
|
+
url: wixMediaSquarePng(file, size)
|
|
54101
|
+
});
|
|
54102
|
+
return [
|
|
54103
|
+
png("ios/AppIcon.appiconset/icon-1024.png", 1024),
|
|
54104
|
+
{
|
|
54105
|
+
path: "ios/AppIcon.appiconset/Contents.json",
|
|
54106
|
+
content: JSON.stringify(
|
|
54107
|
+
{
|
|
54108
|
+
images: [
|
|
54109
|
+
{
|
|
54110
|
+
filename: "icon-1024.png",
|
|
54111
|
+
idiom: "universal",
|
|
54112
|
+
platform: "ios",
|
|
54113
|
+
size: "1024x1024"
|
|
54114
|
+
}
|
|
54115
|
+
],
|
|
54116
|
+
info: { author: "wixel", version: 1 }
|
|
54117
|
+
},
|
|
54118
|
+
null,
|
|
54119
|
+
2
|
|
54120
|
+
)
|
|
54121
|
+
},
|
|
54122
|
+
...ANDROID_MIPMAPS.map(
|
|
54123
|
+
([density, size]) => png(`android/res/mipmap-${density}/ic_launcher.png`, size)
|
|
54124
|
+
),
|
|
54125
|
+
png("android/play-store-512.png", 512),
|
|
54126
|
+
png("web/favicon-16.png", 16),
|
|
54127
|
+
png("web/favicon-32.png", 32),
|
|
54128
|
+
png("web/favicon-48.png", 48),
|
|
54129
|
+
png("web/apple-touch-icon.png", 180),
|
|
54130
|
+
png("web/icon-192.png", 192),
|
|
54131
|
+
png("web/icon-512.png", 512),
|
|
54132
|
+
{
|
|
54133
|
+
path: "web/site.webmanifest",
|
|
54134
|
+
content: JSON.stringify(
|
|
54135
|
+
{
|
|
54136
|
+
name: appName,
|
|
54137
|
+
short_name: appName.slice(0, 12),
|
|
54138
|
+
icons: [
|
|
54139
|
+
{ src: "icon-192.png", sizes: "192x192", type: "image/png" },
|
|
54140
|
+
{ src: "icon-512.png", sizes: "512x512", type: "image/png" }
|
|
54141
|
+
],
|
|
54142
|
+
display: "standalone"
|
|
54143
|
+
},
|
|
54144
|
+
null,
|
|
54145
|
+
2
|
|
54146
|
+
)
|
|
54147
|
+
}
|
|
54148
|
+
];
|
|
54149
|
+
}
|
|
54150
|
+
|
|
54151
|
+
// src/wixel/wixel-asset-files-tool.ts
|
|
54152
|
+
var ASSET_FILE_LAYOUTS = ["files", "spriteSheet", "appIcons"];
|
|
54153
|
+
var ICONS_FILE_TYPES = [
|
|
54154
|
+
"png",
|
|
54155
|
+
"pngTransparentBackground",
|
|
54156
|
+
"svg",
|
|
54157
|
+
"svgTransparentBackground"
|
|
54158
|
+
];
|
|
54159
|
+
var ICONS_SIZES = [
|
|
54160
|
+
"16px",
|
|
54161
|
+
"24px",
|
|
54162
|
+
"32px",
|
|
54163
|
+
"64px",
|
|
54164
|
+
"128px",
|
|
54165
|
+
"256px",
|
|
54166
|
+
"512px"
|
|
54167
|
+
];
|
|
54168
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
54169
|
+
var MAX_WAIT_SECONDS = 25;
|
|
54170
|
+
function buildExportParams(assetType, args) {
|
|
54171
|
+
const layout = args.layout ?? "files";
|
|
54172
|
+
if (assetType === "IMAGE" && layout === "appIcons") {
|
|
54173
|
+
return { format: "png", quality: 100, scale: 1 };
|
|
54174
|
+
}
|
|
54175
|
+
if (assetType !== "ICONS") {
|
|
54176
|
+
if (layout !== "files") {
|
|
54177
|
+
throw new Error(
|
|
54178
|
+
layout === "appIcons" ? `layout "appIcons" needs an ICONS or IMAGE asset (the app icon); this asset is ${assetType}` : `layout "${layout}" needs an ICONS asset (an icon / sprite set); this asset is ${assetType}`
|
|
54179
|
+
);
|
|
54180
|
+
}
|
|
54181
|
+
return void 0;
|
|
54182
|
+
}
|
|
54183
|
+
if (layout === "files") {
|
|
54184
|
+
return {
|
|
54185
|
+
// Engines and app projects want plain transparent PNGs, not the
|
|
54186
|
+
// editor's default SVG-wrapped PNG.
|
|
54187
|
+
fileType: args.fileType ?? "pngTransparentBackground",
|
|
54188
|
+
...args.size ? { size: args.size } : { size: "512px" }
|
|
54189
|
+
};
|
|
54190
|
+
}
|
|
54191
|
+
return {
|
|
54192
|
+
layout,
|
|
54193
|
+
...layout === "spriteSheet" && args.size ? { size: args.size } : {}
|
|
54194
|
+
};
|
|
54195
|
+
}
|
|
54196
|
+
async function waitForExport(read, waitSeconds) {
|
|
54197
|
+
const deadline = Date.now() + waitSeconds * 1e3;
|
|
54198
|
+
let state = await read();
|
|
54199
|
+
while (!state.complete && !state.errorDescription && Date.now() < deadline) {
|
|
54200
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
54201
|
+
state = await read();
|
|
54202
|
+
}
|
|
54203
|
+
return state;
|
|
54204
|
+
}
|
|
54205
|
+
function describeContents(layout, assetType) {
|
|
54206
|
+
if (assetType !== "ICONS") {
|
|
54207
|
+
return "The file is the asset as the editor downloads it.";
|
|
54208
|
+
}
|
|
54209
|
+
switch (layout) {
|
|
54210
|
+
case "spriteSheet":
|
|
54211
|
+
return 'The zip holds <set>-sheet.png (frames on one grid, feet on one baseline), <set>-sheet.json (Aseprite array frame map: frames "0","1"\u2026, one frameTags entry per pose \u2014 load with Phaser this.load.aseprite + this.anims.createFromAseprite) and frames/<n>-<pose>.png.';
|
|
54212
|
+
case "appIcons":
|
|
54213
|
+
return "The zip holds ios/AppIcon.appiconset (1024 px + Contents.json, drop into Assets.xcassets), android/res/mipmap-*/ic_launcher.png + android/play-store-512.png, and web/ favicons, apple-touch-icon.png, icon-192/512.png and site.webmanifest.";
|
|
54214
|
+
default:
|
|
54215
|
+
return "The zip holds one transparent PNG per tile, named after the tile.";
|
|
54216
|
+
}
|
|
54217
|
+
}
|
|
54218
|
+
function addGetWixelAssetFilesTool(server, authStrategy, options = {}) {
|
|
54219
|
+
const { getSiteAuthHeaders } = authStrategy;
|
|
54220
|
+
server.registerTool(
|
|
54221
|
+
"get_wixel_asset_files",
|
|
54222
|
+
{
|
|
54223
|
+
title: "Get Wixel asset files",
|
|
54224
|
+
description: `Download the files of an asset made in a Wixel conversation, to use them in code (a game, an app, a site). Pass the metaSiteId and sessionId that send_wixel_message returned.
|
|
54225
|
+
- Without assetId: lists the project's assets (id, name, type) \u2014 pick one.
|
|
54226
|
+
- With assetId: exports it and returns a zip URL. For an ICONS asset choose layout:
|
|
54227
|
+
"files" (default) one transparent PNG per tile;
|
|
54228
|
+
"spriteSheet" for a character's poses / animation frames: one aligned sheet + Aseprite JSON frame map (Phaser createFromAseprite) + aligned frames;
|
|
54229
|
+
"appIcons" for an app or game icon: iOS app icon set, Android mipmaps + Play Store 512, web favicons + manifest.
|
|
54230
|
+
An IMAGE asset (an app icon tile) also takes layout "appIcons": you get one URL per file instead of a zip.
|
|
54231
|
+
- If the export is still running, call again with the returned jobId, assetType, layout and assetId.
|
|
54232
|
+
Download the URL and unzip it into the project; never re-draw or re-generate assets the user already approved.`,
|
|
54233
|
+
inputSchema: {
|
|
54234
|
+
metaSiteId: external_exports.string().describe("metaSiteId returned by send_wixel_message"),
|
|
54235
|
+
sessionId: external_exports.string().describe("sessionId returned by send_wixel_message"),
|
|
54236
|
+
assetId: external_exports.string().optional().describe("Asset to export; omit to list the project assets"),
|
|
54237
|
+
layout: external_exports.enum(ASSET_FILE_LAYOUTS).optional().describe(
|
|
54238
|
+
"ICONS assets only: files (default), spriteSheet, appIcons"
|
|
54239
|
+
),
|
|
54240
|
+
fileType: external_exports.enum(ICONS_FILE_TYPES).optional().describe(
|
|
54241
|
+
'ICONS layout "files" only; default pngTransparentBackground'
|
|
54242
|
+
),
|
|
54243
|
+
size: external_exports.enum(ICONS_SIZES).optional().describe(
|
|
54244
|
+
'ICONS: tile size for "files" (default 512px), frame cell size for "spriteSheet" (default 256px)'
|
|
54245
|
+
),
|
|
54246
|
+
jobId: external_exports.string().optional().describe("Continue waiting on an export this tool started"),
|
|
54247
|
+
assetType: external_exports.string().optional().describe("With jobId: the assetType this tool returned"),
|
|
54248
|
+
waitSeconds: external_exports.number().min(0).max(MAX_WAIT_SECONDS).optional().describe("How long to wait for the export (default 25)")
|
|
54249
|
+
},
|
|
54250
|
+
annotations: {
|
|
54251
|
+
readOnlyHint: true,
|
|
54252
|
+
destructiveHint: false,
|
|
54253
|
+
openWorldHint: false
|
|
54254
|
+
}
|
|
54255
|
+
},
|
|
54256
|
+
async (args, extra) => {
|
|
54257
|
+
setWixelToolBi(extra, { siteId: args.metaSiteId });
|
|
54258
|
+
const siteAuthHeaders = await getSiteAuthHeaders(args.metaSiteId);
|
|
54259
|
+
try {
|
|
54260
|
+
const projectId = await getWixelSessionProjectId({
|
|
54261
|
+
authHeaders: siteAuthHeaders,
|
|
54262
|
+
conductorOverride: options.conductorOverride,
|
|
54263
|
+
sessionId: args.sessionId
|
|
54264
|
+
});
|
|
54265
|
+
if (!projectId) {
|
|
54266
|
+
throw new Error(
|
|
54267
|
+
"This Wixel conversation has no project yet \u2014 make something first"
|
|
54268
|
+
);
|
|
54269
|
+
}
|
|
54270
|
+
const base = {
|
|
54271
|
+
siteAuthHeaders,
|
|
54272
|
+
projectId,
|
|
54273
|
+
conductorOverride: options.conductorOverride
|
|
54274
|
+
};
|
|
54275
|
+
const waitSeconds = args.waitSeconds ?? MAX_WAIT_SECONDS;
|
|
54276
|
+
if (args.jobId) {
|
|
54277
|
+
if (!args.assetType) {
|
|
54278
|
+
throw new Error("Pass the assetType returned with the jobId");
|
|
54279
|
+
}
|
|
54280
|
+
const assetType2 = args.assetType;
|
|
54281
|
+
const state2 = await waitForExport(
|
|
54282
|
+
() => getAssetExportState({ ...base, assetType: assetType2, jobId: args.jobId }),
|
|
54283
|
+
waitSeconds
|
|
54284
|
+
);
|
|
54285
|
+
const assetName = state2.complete && args.assetId && assetType2 === "IMAGE" && args.layout === "appIcons" ? await getAsset({ ...base, assetId: args.assetId }).then((asset2) => asset2?.name).catch(() => void 0) : void 0;
|
|
54286
|
+
return exportResult(state2, args, assetType2, args.jobId, assetName);
|
|
54287
|
+
}
|
|
54288
|
+
if (!args.assetId) {
|
|
54289
|
+
const { assets } = await queryAssets(base);
|
|
54290
|
+
const list = (assets ?? []).filter((asset2) => !asset2.archived).map((asset2) => ({
|
|
54291
|
+
id: asset2.id,
|
|
54292
|
+
name: asset2.name ?? null,
|
|
54293
|
+
type: asset2.type ?? null
|
|
54294
|
+
}));
|
|
54295
|
+
return {
|
|
54296
|
+
isError: false,
|
|
54297
|
+
content: [
|
|
54298
|
+
{
|
|
54299
|
+
type: "text",
|
|
54300
|
+
text: `${list.length} assets in this Wixel project:
|
|
54301
|
+
${list.map(
|
|
54302
|
+
(a2) => `- ${a2.name ?? "(unnamed)"} [${a2.type}] id=${a2.id}`
|
|
54303
|
+
).join(
|
|
54304
|
+
"\n"
|
|
54305
|
+
)}
|
|
54306
|
+
Call get_wixel_asset_files again with the assetId to download one.`
|
|
54307
|
+
}
|
|
54308
|
+
],
|
|
54309
|
+
structuredContent: { success: true, projectId, assets: list }
|
|
54310
|
+
};
|
|
54311
|
+
}
|
|
54312
|
+
const asset = await getAsset({ ...base, assetId: args.assetId });
|
|
54313
|
+
const assetType = asset?.type;
|
|
54314
|
+
if (!assetType) {
|
|
54315
|
+
throw new Error(
|
|
54316
|
+
`Asset ${args.assetId} was not found in this project`
|
|
54317
|
+
);
|
|
54318
|
+
}
|
|
54319
|
+
const params = buildExportParams(assetType, args);
|
|
54320
|
+
const jobId = await startAssetExport({
|
|
54321
|
+
...base,
|
|
54322
|
+
assetType,
|
|
54323
|
+
assetId: args.assetId,
|
|
54324
|
+
params
|
|
54325
|
+
});
|
|
54326
|
+
const state = await waitForExport(
|
|
54327
|
+
() => getAssetExportState({ ...base, assetType, jobId }),
|
|
54328
|
+
waitSeconds
|
|
54329
|
+
);
|
|
54330
|
+
return exportResult(state, args, assetType, jobId, asset?.name);
|
|
54331
|
+
} catch (error2) {
|
|
54332
|
+
rethrowIfUnauthorized(error2);
|
|
54333
|
+
console.error("\u274C Error getting Wixel asset files:", {
|
|
54334
|
+
message: error2.message,
|
|
54335
|
+
requestId: error2.requestId
|
|
54336
|
+
});
|
|
54337
|
+
const errorMessage = error2.message;
|
|
54338
|
+
return {
|
|
54339
|
+
isError: true,
|
|
54340
|
+
content: [{ type: "text", text: errorMessage }],
|
|
54341
|
+
structuredContent: { success: false, error: errorMessage }
|
|
54342
|
+
};
|
|
54343
|
+
}
|
|
54344
|
+
}
|
|
54345
|
+
);
|
|
54346
|
+
}
|
|
54347
|
+
function exportResult(state, args, assetType, jobId, assetName) {
|
|
54348
|
+
if (state.errorDescription) {
|
|
54349
|
+
return {
|
|
54350
|
+
isError: true,
|
|
54351
|
+
content: [
|
|
54352
|
+
{
|
|
54353
|
+
type: "text",
|
|
54354
|
+
text: `Export failed: ${state.errorDescription}`
|
|
54355
|
+
}
|
|
54356
|
+
],
|
|
54357
|
+
structuredContent: {
|
|
54358
|
+
success: false,
|
|
54359
|
+
error: state.errorDescription,
|
|
54360
|
+
jobId,
|
|
54361
|
+
assetType
|
|
54362
|
+
}
|
|
54363
|
+
};
|
|
54364
|
+
}
|
|
54365
|
+
const layout = args.layout ?? "files";
|
|
54366
|
+
if (!state.complete || !state.url) {
|
|
54367
|
+
return {
|
|
54368
|
+
isError: false,
|
|
54369
|
+
content: [
|
|
54370
|
+
{
|
|
54371
|
+
type: "text",
|
|
54372
|
+
text: `Export still running${state.progress != null ? ` (${state.progress}%)` : ""}. Call get_wixel_asset_files again with jobId "${jobId}", assetType "${assetType}", layout "${layout}"${args.assetId ? ` and assetId "${args.assetId}"` : ""}.`
|
|
54373
|
+
}
|
|
54374
|
+
],
|
|
54375
|
+
structuredContent: {
|
|
54376
|
+
success: true,
|
|
54377
|
+
status: "running",
|
|
54378
|
+
progress: state.progress,
|
|
54379
|
+
jobId,
|
|
54380
|
+
assetType,
|
|
54381
|
+
layout,
|
|
54382
|
+
...args.assetId ? { assetId: args.assetId } : {}
|
|
54383
|
+
}
|
|
54384
|
+
};
|
|
54385
|
+
}
|
|
54386
|
+
if (assetType === "IMAGE" && layout === "appIcons") {
|
|
54387
|
+
const files = buildAppIconFiles(state.url, assetName || "App");
|
|
54388
|
+
if (files) {
|
|
54389
|
+
return {
|
|
54390
|
+
isError: false,
|
|
54391
|
+
content: [
|
|
54392
|
+
{
|
|
54393
|
+
type: "text",
|
|
54394
|
+
text: `App icon files ready \u2014 download each URL to its path, write the JSON files as given:
|
|
54395
|
+
${files.map((f2) => `- ${f2.path}: ${f2.url ?? f2.content}`).join("\n")}`
|
|
54396
|
+
}
|
|
54397
|
+
],
|
|
54398
|
+
structuredContent: {
|
|
54399
|
+
success: true,
|
|
54400
|
+
status: "ready",
|
|
54401
|
+
url: state.url,
|
|
54402
|
+
files,
|
|
54403
|
+
jobId,
|
|
54404
|
+
assetType,
|
|
54405
|
+
layout
|
|
54406
|
+
}
|
|
54407
|
+
};
|
|
54408
|
+
}
|
|
54409
|
+
}
|
|
54410
|
+
return {
|
|
54411
|
+
isError: false,
|
|
54412
|
+
content: [
|
|
54413
|
+
{
|
|
54414
|
+
type: "text",
|
|
54415
|
+
text: `Files ready: ${state.url}
|
|
54416
|
+
${describeContents(args.layout, assetType)}`
|
|
54417
|
+
}
|
|
54418
|
+
],
|
|
54419
|
+
structuredContent: {
|
|
54420
|
+
success: true,
|
|
54421
|
+
status: "ready",
|
|
54422
|
+
url: state.url,
|
|
54423
|
+
jobId,
|
|
54424
|
+
assetType,
|
|
54425
|
+
layout: args.layout ?? "files"
|
|
54426
|
+
}
|
|
54427
|
+
};
|
|
54428
|
+
}
|
|
54429
|
+
|
|
53999
54430
|
// src/wixel/index.ts
|
|
54000
54431
|
function addWixelTools(server, authStrategy, options = {}) {
|
|
54001
54432
|
addGetWixelJobStatusTool(server, authStrategy, options);
|
|
@@ -54003,6 +54434,7 @@ function addWixelTools(server, authStrategy, options = {}) {
|
|
|
54003
54434
|
addGetWixelAssetTool(server, authStrategy, options);
|
|
54004
54435
|
addGetWixelProjectAssetsTool(server, authStrategy, options);
|
|
54005
54436
|
addWixelSessionTools(server, authStrategy, options);
|
|
54437
|
+
addGetWixelAssetFilesTool(server, authStrategy, options);
|
|
54006
54438
|
}
|
|
54007
54439
|
|
|
54008
54440
|
// src/import-claude-design-tool/constants.ts
|
|
@@ -56989,6 +57421,7 @@ function createWixToolkit(options) {
|
|
|
56989
57421
|
addEditSiteWidgetTool,
|
|
56990
57422
|
addExecuteCodeModeTool,
|
|
56991
57423
|
addGetProfileTool,
|
|
57424
|
+
addGetWixelAssetFilesTool,
|
|
56992
57425
|
addGetWixelAssetTool,
|
|
56993
57426
|
addGetWixelJobStatusTool,
|
|
56994
57427
|
addGetWixelProjectAssetsTool,
|
|
@@ -45,10 +45,7 @@ export function addDomainAvailabilityTool(server) {
|
|
|
45
45
|
openWorldHint: false
|
|
46
46
|
},
|
|
47
47
|
_meta: {
|
|
48
|
-
securitySchemes: [
|
|
49
|
-
{ type: 'noauth' },
|
|
50
|
-
{ type: 'oauth2', scopes: ['offline_access'] }
|
|
51
|
-
],
|
|
48
|
+
securitySchemes: [{ type: 'oauth2', scopes: ['offline_access'] }],
|
|
52
49
|
ui: { visibility: ['app', 'model'] },
|
|
53
50
|
'openai/toolInvocation/invoking': 'Checking domain availability',
|
|
54
51
|
'openai/toolInvocation/invoked': 'Domain availability checked'
|
|
@@ -68,10 +68,7 @@ export function addSuggestedDomainTool(server, authStrategy) {
|
|
|
68
68
|
openWorldHint: false
|
|
69
69
|
},
|
|
70
70
|
_meta: {
|
|
71
|
-
securitySchemes: [
|
|
72
|
-
{ type: 'noauth' },
|
|
73
|
-
{ type: 'oauth2', scopes: ['offline_access'] }
|
|
74
|
-
],
|
|
71
|
+
securitySchemes: [{ type: 'oauth2', scopes: ['offline_access'] }],
|
|
75
72
|
// Model-visible so hosts (e.g. Microsoft 365 Copilot) can call it directly and show it
|
|
76
73
|
// in their debug info; 'app' keeps it callable from the site-builder widget.
|
|
77
74
|
ui: { visibility: ['app', 'model'] },
|
|
@@ -2,14 +2,25 @@ export declare const UNAUTHORIZED_ERROR_NAME = "UnauthorizedError";
|
|
|
2
2
|
export type McpHeaders = Record<string, string>;
|
|
3
3
|
/**
|
|
4
4
|
* Material the execute runtime needs to obtain a site-scoped token for any site
|
|
5
|
-
* on demand (multi-site ExecuteWixAPI).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* on demand (multi-site ExecuteWixAPI).
|
|
6
|
+
*
|
|
7
|
+
* - `oauth`: exchanged per site via the oauth2/token refresh grant.
|
|
8
|
+
* - `accountToken`: an account-level user access token, exchanged per site via
|
|
9
|
+
* the oauth2/token RFC 8693 token-exchange grant (account token + site_id →
|
|
10
|
+
* site-scoped token, no client secret, no refresh token issued). For callers
|
|
11
|
+
* that mint short-lived account tokens and hold no refresh token, such as
|
|
12
|
+
* the AI Assistant platform.
|
|
13
|
+
* - `apiKey`: works for any site as-is; the runtime only adds `wix-site-id`.
|
|
14
|
+
*
|
|
15
|
+
* Every variant is a credential: never log it, never expose it to sandbox JS.
|
|
8
16
|
*/
|
|
9
17
|
export type SiteAuthContext = {
|
|
10
18
|
type: 'oauth';
|
|
11
19
|
clientId: string;
|
|
12
20
|
refreshToken: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'accountToken';
|
|
23
|
+
accessToken: string;
|
|
13
24
|
} | {
|
|
14
25
|
type: 'apiKey';
|
|
15
26
|
accessToken: string;
|
|
@@ -39,14 +39,25 @@ export declare const UNAUTHORIZED_ERROR_NAME = "UnauthorizedError";
|
|
|
39
39
|
export type McpHeaders = Record<string, string>;
|
|
40
40
|
/**
|
|
41
41
|
* Material the execute runtime needs to obtain a site-scoped token for any site
|
|
42
|
-
* on demand (multi-site ExecuteWixAPI).
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* on demand (multi-site ExecuteWixAPI).
|
|
43
|
+
*
|
|
44
|
+
* - `oauth`: exchanged per site via the oauth2/token refresh grant.
|
|
45
|
+
* - `accountToken`: an account-level user access token, exchanged per site via
|
|
46
|
+
* the oauth2/token RFC 8693 token-exchange grant (account token + site_id →
|
|
47
|
+
* site-scoped token, no client secret, no refresh token issued). For callers
|
|
48
|
+
* that mint short-lived account tokens and hold no refresh token, such as
|
|
49
|
+
* the AI Assistant platform.
|
|
50
|
+
* - `apiKey`: works for any site as-is; the runtime only adds `wix-site-id`.
|
|
51
|
+
*
|
|
52
|
+
* Every variant is a credential: never log it, never expose it to sandbox JS.
|
|
45
53
|
*/
|
|
46
54
|
export type SiteAuthContext = {
|
|
47
55
|
type: "oauth";
|
|
48
56
|
clientId: string;
|
|
49
57
|
refreshToken: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "accountToken";
|
|
60
|
+
accessToken: string;
|
|
50
61
|
} | {
|
|
51
62
|
type: "apiKey";
|
|
52
63
|
accessToken: string;
|
|
@@ -39,14 +39,25 @@ export declare const UNAUTHORIZED_ERROR_NAME = "UnauthorizedError";
|
|
|
39
39
|
export type McpHeaders = Record<string, string>;
|
|
40
40
|
/**
|
|
41
41
|
* Material the execute runtime needs to obtain a site-scoped token for any site
|
|
42
|
-
* on demand (multi-site ExecuteWixAPI).
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* on demand (multi-site ExecuteWixAPI).
|
|
43
|
+
*
|
|
44
|
+
* - `oauth`: exchanged per site via the oauth2/token refresh grant.
|
|
45
|
+
* - `accountToken`: an account-level user access token, exchanged per site via
|
|
46
|
+
* the oauth2/token RFC 8693 token-exchange grant (account token + site_id →
|
|
47
|
+
* site-scoped token, no client secret, no refresh token issued). For callers
|
|
48
|
+
* that mint short-lived account tokens and hold no refresh token, such as
|
|
49
|
+
* the AI Assistant platform.
|
|
50
|
+
* - `apiKey`: works for any site as-is; the runtime only adds `wix-site-id`.
|
|
51
|
+
*
|
|
52
|
+
* Every variant is a credential: never log it, never expose it to sandbox JS.
|
|
45
53
|
*/
|
|
46
54
|
export type SiteAuthContext = {
|
|
47
55
|
type: "oauth";
|
|
48
56
|
clientId: string;
|
|
49
57
|
refreshToken: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "accountToken";
|
|
60
|
+
accessToken: string;
|
|
50
61
|
} | {
|
|
51
62
|
type: "apiKey";
|
|
52
63
|
accessToken: string;
|