@wix/mcp 1.0.94 → 1.0.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/bin-standalone.js +1116 -321
- package/build/cjs/index.cjs +2813 -1147
- package/build/cjs/toolkit.cjs +1788 -362
- package/build/dts/api-call/headless-release.d.ts +56 -0
- package/build/dts/api-call/headless-release.js +135 -0
- package/build/dts/api-call/index.d.ts +9 -1
- package/build/dts/api-call/index.js +13 -7
- package/build/dts/api-call/site-files.d.ts +14 -0
- package/build/dts/api-call/site-files.js +106 -10
- package/build/dts/api-call/upload-headless-website-files.d.ts +88 -5
- package/build/dts/api-call/upload-headless-website-files.js +473 -20
- package/build/dts/api-call/upload-image-tool.d.ts +2 -0
- package/build/dts/api-call/upload-image-tool.js +8 -4
- package/build/dts/bin.d.ts +1 -1
- package/build/dts/bin.js +3 -1
- package/build/dts/code-mode/index.d.ts +6 -1
- package/build/dts/code-mode/index.js +30 -10
- package/build/dts/code-mode/truncate-response.js +5 -1
- package/build/dts/config/default-config.d.ts +35 -5
- package/build/dts/config/default-config.js +334 -178
- package/build/dts/config/file-params.d.ts +22 -0
- package/build/dts/config/file-params.js +24 -0
- package/build/dts/config/param-descriptions.d.ts +14 -2
- package/build/dts/config/param-descriptions.js +33 -14
- package/build/dts/constants.d.ts +1 -0
- package/build/dts/constants.js +1 -0
- package/build/dts/docs/docs.d.ts +12 -1
- package/build/dts/docs/docs.js +210 -42
- package/build/dts/docs/fetch-article.d.ts +21 -0
- package/build/dts/docs/fetch-article.js +66 -4
- package/build/dts/docs/get-to-know.d.ts +7 -2
- package/build/dts/docs/get-to-know.js +36 -14
- package/build/dts/docs/mintlify-docs.d.ts +121 -0
- package/build/dts/docs/mintlify-docs.js +391 -0
- package/build/dts/docs/semanticSearch.d.ts +2 -1
- package/build/dts/docs/semanticSearch.js +14 -7
- package/build/dts/docs/start-deploy-website.d.ts +7 -6
- package/build/dts/docs/start-deploy-website.js +13 -8
- package/build/dts/docs/start-headless-build.d.ts +8 -10
- package/build/dts/docs/start-headless-build.js +14 -28
- package/build/dts/index.d.ts +2 -2
- package/build/dts/index.js +2 -2
- package/build/dts/infra/redact-personal-data.d.ts +4 -0
- package/build/dts/infra/redact-personal-data.js +79 -0
- package/build/dts/site-widget-tools/constants.d.ts +49 -1
- package/build/dts/site-widget-tools/constants.js +64 -7
- package/build/dts/site-widget-tools/index.d.ts +1 -0
- package/build/dts/site-widget-tools/index.js +1 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.d.ts +14 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.js +138 -0
- package/build/dts/site-widget-tools/search-site-templates-tool/index.js +16 -2
- package/build/dts/site-widget-tools/site-builder-tool/index.js +22 -2
- package/build/dts/site-widget-tools/site-creation-tool/index.d.ts +7 -1
- package/build/dts/site-widget-tools/site-creation-tool/index.js +22 -51
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +6 -3
- package/build/dts/support/index.d.ts +4 -0
- package/build/dts/support/index.js +13 -7
- package/build/dts/toolkit-entry.d.cts +15 -10
- package/build/dts/toolkit-entry.d.ts +15 -10
- package/build/dts/toolkit.d.ts +2 -2
- package/build/dts/toolkit.js +14 -7
- package/build/dts/wix-mcp-server.d.ts +10 -1
- package/build/dts/wix-mcp-server.js +25 -7
- package/build/dts/wixel/constants.d.ts +8 -1
- package/build/dts/wixel/constants.js +8 -1
- package/build/dts/wixel/index.d.ts +1 -1
- package/build/dts/wixel/index.js +2 -2
- package/build/dts/wixel/wixel-a2a-client.d.ts +83 -0
- package/build/dts/wixel/wixel-a2a-client.js +166 -0
- package/build/dts/wixel/wixel-bi.d.ts +2 -1
- package/build/dts/wixel/wixel-bi.js +2 -1
- package/build/dts/wixel/{external-agent-api.d.ts → wixel-session-entries.d.ts} +11 -33
- package/build/dts/wixel/{external-agent-api.js → wixel-session-entries.js} +33 -54
- package/build/dts/wixel/wixel-session-tools.d.ts +21 -26
- package/build/dts/wixel/wixel-session-tools.js +494 -599
- package/build/esm/index.js +2824 -1170
- package/build/esm/toolkit.js +1786 -360
- package/package.json +4 -4
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Wix Astro build among the files UploadHeadlessWebsiteFiles received (the output
|
|
3
|
+
* of `astro build` with the Wix hosting adapter, zipped from the project root):
|
|
4
|
+
* recognised here, released by headless-business-setup's bundle routes (the tool
|
|
5
|
+
* PUTs the zip and calls `release-bundle`), not dropped as static files.
|
|
6
|
+
*
|
|
7
|
+
* The build is recognised by `.wix/build-metadata.json`, which the adapter writes
|
|
8
|
+
* beside `dist/`: it names the client folder (served as static files), the server
|
|
9
|
+
* folder (run on the hosting runtime) and the app manifest. The folder that holds
|
|
10
|
+
* `.wix/` is the project root; the project's other files there are its source.
|
|
11
|
+
*
|
|
12
|
+
* Runs in the MCP server (a Cloudflare Worker): no `node:` imports, only web APIs.
|
|
13
|
+
*/
|
|
14
|
+
import type { SiteFile } from './site-files.js';
|
|
15
|
+
export declare const BUILD_METADATA_SUFFIX = ".wix/build-metadata.json";
|
|
16
|
+
export interface BuildMetadata {
|
|
17
|
+
appManifestPath: string;
|
|
18
|
+
clientDir?: string;
|
|
19
|
+
serverDir?: string;
|
|
20
|
+
staticsUrlPlaceholder?: string;
|
|
21
|
+
serverUrlPlaceholder?: string;
|
|
22
|
+
generateAppDeploymentGroupId?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface HeadlessBuild {
|
|
25
|
+
metadata: BuildMetadata;
|
|
26
|
+
/** The project root in the upload: `''`, or the one folder that wraps the project (`proj/`). */
|
|
27
|
+
root: string;
|
|
28
|
+
/** The manifest as the build wrote it (`{ appId, components }`). */
|
|
29
|
+
manifestText: string;
|
|
30
|
+
clientFiles: SiteFile[];
|
|
31
|
+
serverFiles: SiteFile[];
|
|
32
|
+
/** The client folder in the upload (`dist/client/`), when the metadata names one. */
|
|
33
|
+
clientDir?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The upload without the build's empty static files: the hosting refuses a static file
|
|
37
|
+
* of size 0 (`size got 0, expected 1 or more`), and an empty file serves nothing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function withoutEmptyStatics(files: SiteFile[], build: HeadlessBuild): {
|
|
40
|
+
files: SiteFile[];
|
|
41
|
+
dropped: string[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Recognise a Wix Astro build among the uploaded files: the file set holds
|
|
45
|
+
* `.wix/build-metadata.json` (at the root, or under one folder that wraps the project).
|
|
46
|
+
* Returns null for a static site, so the caller drops it as before. Throws an agent-facing
|
|
47
|
+
* error for a build whose metadata or files are not usable.
|
|
48
|
+
*/
|
|
49
|
+
export declare function findHeadlessBuild(files: SiteFile[]): HeadlessBuild | null;
|
|
50
|
+
/**
|
|
51
|
+
* The project's source among the uploaded files: every file under the build's
|
|
52
|
+
* project root outside `dist/`, `.wix/`, `.astro/`, `.agents/`, `node_modules/` and
|
|
53
|
+
* `.env*`, with paths relative to the root. Empty when only the build was uploaded.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildSourceFiles(files: SiteFile[], build: HeadlessBuild): SiteFile[];
|
|
56
|
+
//# sourceMappingURL=headless-release.d.ts.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export const BUILD_METADATA_SUFFIX = '.wix/build-metadata.json';
|
|
2
|
+
/**
|
|
3
|
+
* The upload without the build's empty static files: the hosting refuses a static file
|
|
4
|
+
* of size 0 (`size got 0, expected 1 or more`), and an empty file serves nothing.
|
|
5
|
+
*/
|
|
6
|
+
export function withoutEmptyStatics(files, build) {
|
|
7
|
+
const dir = build.clientDir;
|
|
8
|
+
if (dir === undefined)
|
|
9
|
+
return { files, dropped: [] };
|
|
10
|
+
const dropped = [];
|
|
11
|
+
const kept = files.filter((f) => {
|
|
12
|
+
const path = normalizePath(f.path);
|
|
13
|
+
if (f.bytes.byteLength === 0 && path.startsWith(dir)) {
|
|
14
|
+
dropped.push(path.slice(dir.length));
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
return { files: kept, dropped };
|
|
20
|
+
}
|
|
21
|
+
const normalizePath = (p) => p.replace(/\\/g, '/').replace(/^\.?\//, '');
|
|
22
|
+
/**
|
|
23
|
+
* Recognise a Wix Astro build among the uploaded files: the file set holds
|
|
24
|
+
* `.wix/build-metadata.json` (at the root, or under one folder that wraps the project).
|
|
25
|
+
* Returns null for a static site, so the caller drops it as before. Throws an agent-facing
|
|
26
|
+
* error for a build whose metadata or files are not usable.
|
|
27
|
+
*/
|
|
28
|
+
export function findHeadlessBuild(files) {
|
|
29
|
+
const entries = new Map(files.map((f) => [normalizePath(f.path), f.bytes]));
|
|
30
|
+
const metadataEntry = [...entries.keys()].find((name) => name === BUILD_METADATA_SUFFIX ||
|
|
31
|
+
name.endsWith(`/${BUILD_METADATA_SUFFIX}`));
|
|
32
|
+
if (metadataEntry === undefined)
|
|
33
|
+
return null;
|
|
34
|
+
const zipRoot = metadataEntry.slice(0, metadataEntry.length - BUILD_METADATA_SUFFIX.length);
|
|
35
|
+
let metadata;
|
|
36
|
+
try {
|
|
37
|
+
metadata = JSON.parse(new TextDecoder().decode(entries.get(metadataEntry)));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error(`${BUILD_METADATA_SUFFIX} is not valid JSON.`);
|
|
41
|
+
}
|
|
42
|
+
if (typeof metadata.appManifestPath !== 'string') {
|
|
43
|
+
throw new Error(`${BUILD_METADATA_SUFFIX} has no appManifestPath; send the build as the Wix hosting adapter wrote it (.wix/ and dist/ zipped from the project root).`);
|
|
44
|
+
}
|
|
45
|
+
// The metadata holds the builder's absolute paths; the files in the zip are relative to the
|
|
46
|
+
// project root. The manifest is found by the longest tail of its path that exists, and the
|
|
47
|
+
// part of the path before that tail is the builder's project root.
|
|
48
|
+
const manifestPath = resolveZipPath(metadata.appManifestPath, zipRoot, entries);
|
|
49
|
+
if (manifestPath === undefined) {
|
|
50
|
+
throw new Error(`The build's app manifest (${metadata.appManifestPath}) is not in the upload; zip .wix/ and dist/ together from the project root after \`astro build\`.`);
|
|
51
|
+
}
|
|
52
|
+
const builderRoot = metadata.appManifestPath.slice(0, metadata.appManifestPath.length - (manifestPath.length - zipRoot.length));
|
|
53
|
+
const clientDir = metadata.clientDir === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: toZipDir(metadata.clientDir, builderRoot, zipRoot);
|
|
56
|
+
const serverDir = metadata.serverDir === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: toZipDir(metadata.serverDir, builderRoot, zipRoot);
|
|
59
|
+
const clientFiles = [];
|
|
60
|
+
const serverFiles = [];
|
|
61
|
+
for (const [name, bytes] of entries) {
|
|
62
|
+
if (name === manifestPath)
|
|
63
|
+
continue;
|
|
64
|
+
if (serverDir !== undefined && name.startsWith(serverDir)) {
|
|
65
|
+
serverFiles.push({ path: name.slice(serverDir.length), bytes });
|
|
66
|
+
}
|
|
67
|
+
else if (clientDir !== undefined && name.startsWith(clientDir)) {
|
|
68
|
+
clientFiles.push({ path: name.slice(clientDir.length), bytes });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (clientFiles.length + serverFiles.length === 0) {
|
|
72
|
+
throw new Error('The build has no client or server output files; run `astro build` first and zip .wix/ and dist/ from the project root.');
|
|
73
|
+
}
|
|
74
|
+
if (serverFiles.length > 0 &&
|
|
75
|
+
!serverFiles.some((f) => f.path === 'entry.mjs')) {
|
|
76
|
+
throw new Error("The server build has no entry.mjs, which the Wix hosting adapter (@wix/astro-wix-hosting-adapter) writes into the server folder on every build. Build with the project's own `astro build` (the adapter in astro.config) and zip .wix/ and dist/ from the project root as the build left them.");
|
|
77
|
+
}
|
|
78
|
+
const manifestText = new TextDecoder().decode(entries.get(manifestPath));
|
|
79
|
+
return {
|
|
80
|
+
metadata,
|
|
81
|
+
root: zipRoot,
|
|
82
|
+
manifestText,
|
|
83
|
+
clientFiles,
|
|
84
|
+
serverFiles,
|
|
85
|
+
clientDir
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// What the source zip leaves out, relative to the project root: the build output, the
|
|
89
|
+
// adapter's and Astro's caches, the agent's own folder at the top; dependencies and
|
|
90
|
+
// environment files at any depth.
|
|
91
|
+
const NOT_SOURCE_TOP = new Set(['dist', '.wix', '.astro', '.agents']);
|
|
92
|
+
const isSourcePath = (relative) => {
|
|
93
|
+
const segments = relative.split('/');
|
|
94
|
+
return (!NOT_SOURCE_TOP.has(segments[0]) &&
|
|
95
|
+
!segments.some((s) => s === 'node_modules' || s.startsWith('.env')));
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The project's source among the uploaded files: every file under the build's
|
|
99
|
+
* project root outside `dist/`, `.wix/`, `.astro/`, `.agents/`, `node_modules/` and
|
|
100
|
+
* `.env*`, with paths relative to the root. Empty when only the build was uploaded.
|
|
101
|
+
*/
|
|
102
|
+
export function buildSourceFiles(files, build) {
|
|
103
|
+
return files.flatMap((f) => {
|
|
104
|
+
const path = normalizePath(f.path);
|
|
105
|
+
if (!path.startsWith(build.root))
|
|
106
|
+
return [];
|
|
107
|
+
const relative = path.slice(build.root.length);
|
|
108
|
+
return relative && isSourcePath(relative)
|
|
109
|
+
? [{ path: relative, bytes: f.bytes }]
|
|
110
|
+
: [];
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function resolveZipPath(builderPath, zipRoot, entries) {
|
|
114
|
+
const segments = normalizePath(builderPath)
|
|
115
|
+
.split('/')
|
|
116
|
+
.filter((s) => s.length > 0);
|
|
117
|
+
for (let k = 0; k < segments.length; k++) {
|
|
118
|
+
const candidate = zipRoot + segments.slice(k).join('/');
|
|
119
|
+
if (entries.has(candidate))
|
|
120
|
+
return candidate;
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
function toZipDir(builderDir, builderRoot, zipRoot) {
|
|
125
|
+
const normalized = normalizePath(builderDir);
|
|
126
|
+
const normalizedRoot = normalizePath(builderRoot);
|
|
127
|
+
const relative = builderDir.startsWith(builderRoot)
|
|
128
|
+
? builderDir.slice(builderRoot.length)
|
|
129
|
+
: normalized.startsWith(normalizedRoot)
|
|
130
|
+
? normalized.slice(normalizedRoot.length)
|
|
131
|
+
: normalized;
|
|
132
|
+
const dir = zipRoot + normalizePath(relative);
|
|
133
|
+
return dir.endsWith('/') ? dir : `${dir}/`;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=headless-release.js.map
|
|
@@ -6,5 +6,13 @@ export interface ToolHintsConfig {
|
|
|
6
6
|
openWorldHint?: boolean;
|
|
7
7
|
}
|
|
8
8
|
export { addUploadImageTool } from './upload-image-tool.js';
|
|
9
|
-
export declare function addApiCallTool(server: WixMcpServer, strategy: McpAuthenticationStrategy, allowedTools?: string[], disableTools?: string[], enableTools?: string[], toolDescriptions?: Record<string, string>, toolHints?: Record<string, ToolHintsConfig>, maxResponseSize?: number
|
|
9
|
+
export declare function addApiCallTool(server: WixMcpServer, strategy: McpAuthenticationStrategy, allowedTools?: string[], disableTools?: string[], enableTools?: string[], toolDescriptions?: Record<string, string>, toolHints?: Record<string, ToolHintsConfig>, maxResponseSize?: number, options?: {
|
|
10
|
+
imagePickerToolName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* This server serves ExecuteWixAPI. Only then does CallWixSiteAPI tell the user to reconnect when
|
|
13
|
+
* the client lists no ExecuteWixAPI (an old or cached tool list); where it is off on purpose, the
|
|
14
|
+
* agent just uses CallWixSiteAPI.
|
|
15
|
+
*/
|
|
16
|
+
executeWixAPIServed?: boolean;
|
|
17
|
+
}): void;
|
|
10
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -5,8 +5,8 @@ import { handleWixAPIResponse } from '../tool-utils.js';
|
|
|
5
5
|
import { rethrowIfUnauthorized } from '../auth/index.js';
|
|
6
6
|
import { isApiUrlAllowed } from './validation.js';
|
|
7
7
|
import { addUploadImageTool } from './upload-image-tool.js';
|
|
8
|
-
import { defaultToolDescriptions } from '../config/default-config.js';
|
|
9
|
-
import { desc } from '../config/param-descriptions.js';
|
|
8
|
+
import { CALL_WIX_SITE_API_NO_EXECUTE_LINE, CALL_WIX_SITE_API_RECONNECT_LINE, defaultToolDescriptions } from '../config/default-config.js';
|
|
9
|
+
import { desc, WHY_NOT_EXECUTE_NO_EXECUTE, WHY_NOT_EXECUTE_RECONNECT } from '../config/param-descriptions.js';
|
|
10
10
|
import { MAX_ALLOWED_RESPONSE_CHARS } from '../infra/response-size-limits.js';
|
|
11
11
|
export { addUploadImageTool } from './upload-image-tool.js';
|
|
12
12
|
const DEFAULT_MAX_RESPONSE_SIZE = 50000; // ~50KB cap
|
|
@@ -56,7 +56,8 @@ function formatApiResponse(responseData, maxResponseSize = DEFAULT_MAX_RESPONSE_
|
|
|
56
56
|
}
|
|
57
57
|
return { text: responseText, isError: false, originalLength, returnedRows };
|
|
58
58
|
}
|
|
59
|
-
export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI', 'ListWixSites', 'ManageWixSite'], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize) {
|
|
59
|
+
export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI', 'ListWixSites', 'ManageWixSite'], disableTools = [], enableTools = [], toolDescriptions, toolHints, maxResponseSize, options) {
|
|
60
|
+
const executeAdvice = (text, reconnect, plain) => options?.executeWixAPIServed ? text : text.replace(reconnect, plain);
|
|
60
61
|
// Compute effective allowed tools: (default + enableTools) - disableTools
|
|
61
62
|
const effectiveAllowedTools = [
|
|
62
63
|
...new Set([...allowedTools, ...enableTools])
|
|
@@ -71,7 +72,7 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
71
72
|
const getDescription = (toolName, defaultDesc) => toolDescriptions?.[toolName] ?? defaultDesc;
|
|
72
73
|
if (effectiveAllowedTools.includes('CallWixSiteAPI') &&
|
|
73
74
|
!disableTools.includes('CallWixSiteAPI')) {
|
|
74
|
-
server.tool('CallWixSiteAPI', getDescription('CallWixSiteAPI', defaultToolDescriptions.CallWixSiteAPI), (() => {
|
|
75
|
+
server.tool('CallWixSiteAPI', executeAdvice(getDescription('CallWixSiteAPI', defaultToolDescriptions.CallWixSiteAPI), CALL_WIX_SITE_API_RECONNECT_LINE, CALL_WIX_SITE_API_NO_EXECUTE_LINE), (() => {
|
|
75
76
|
const d = desc('CallWixSiteAPI');
|
|
76
77
|
return {
|
|
77
78
|
siteId: z.string().describe(d.siteId),
|
|
@@ -82,6 +83,10 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
82
83
|
.optional()
|
|
83
84
|
.describe(d.body),
|
|
84
85
|
reason: z.string().describe(d.reason),
|
|
86
|
+
whyNotExecuteWixAPI: z
|
|
87
|
+
.string()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe(executeAdvice(d.whyNotExecuteWixAPI, WHY_NOT_EXECUTE_RECONNECT, WHY_NOT_EXECUTE_NO_EXECUTE)),
|
|
85
90
|
sourceDocUrl: z.string().describe(d.sourceDocUrl)
|
|
86
91
|
};
|
|
87
92
|
})(),
|
|
@@ -91,8 +96,8 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
91
96
|
readOnlyHint: false,
|
|
92
97
|
destructiveHint: true,
|
|
93
98
|
openWorldHint: true
|
|
94
|
-
}), async ({ url, body, method, siteId, reason, sourceDocUrl }, { httpClient, setToolBiParams }) => {
|
|
95
|
-
logger.log(`[CallWixSiteAPI] siteId=${siteId}, method=${method}, url=${url}, sourceDocUrl=${sourceDocUrl}, reason="${reason}", body=${JSON.stringify(body)}`);
|
|
99
|
+
}), async ({ url, body, method, siteId, reason, whyNotExecuteWixAPI, sourceDocUrl }, { httpClient, setToolBiParams }) => {
|
|
100
|
+
logger.log(`[CallWixSiteAPI] siteId=${siteId}, method=${method}, url=${url}, sourceDocUrl=${sourceDocUrl}, reason="${reason}", whyNotExecuteWixAPI="${whyNotExecuteWixAPI ?? ''}", body=${JSON.stringify(body)}`);
|
|
96
101
|
const requestHeaders = {
|
|
97
102
|
// Ask the Wix API edge for JSON error bodies (it serves a branded HTML
|
|
98
103
|
// error page on 4xx otherwise); */* keeps non-JSON responses working.
|
|
@@ -328,7 +333,8 @@ export function addApiCallTool(server, strategy, allowedTools = ['CallWixSiteAPI
|
|
|
328
333
|
!disableTools.includes('UploadImageToWixSite')) {
|
|
329
334
|
addUploadImageTool(server, strategy, {
|
|
330
335
|
description: getDescription('UploadImageToWixSite', ''),
|
|
331
|
-
hints: toolHints?.['UploadImageToWixSite']
|
|
336
|
+
hints: toolHints?.['UploadImageToWixSite'],
|
|
337
|
+
imagePickerToolName: options?.imagePickerToolName
|
|
332
338
|
});
|
|
333
339
|
}
|
|
334
340
|
}
|
|
@@ -16,12 +16,26 @@ export declare function base64ToBytes(b64: string): Uint8Array;
|
|
|
16
16
|
export declare function parseFileBundle(bundle: string | undefined): SiteFile[];
|
|
17
17
|
/** Unzip (stored + deflate entries) into raw bytes. Validates the EOCD and the declared sizes. */
|
|
18
18
|
export declare function unzip(z: Uint8Array): Promise<SiteFile[]>;
|
|
19
|
+
export declare function crc32(bytes: Uint8Array): number;
|
|
20
|
+
/** A zip of the files, every entry stored (no compression), names in UTF-8. No zip64: under 4 GB and 65,535 entries. */
|
|
21
|
+
export declare function zipStored(files: SiteFile[]): Uint8Array;
|
|
19
22
|
export declare const isZip: (f: SiteFile) => boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The file set as uploaded: zips unpacked, paths cleaned, junk dropped, within the file
|
|
25
|
+
* count and the byte budget for the whole upload. No site-shape rule yet: a Wix Astro
|
|
26
|
+
* build is told apart from a static site on this list.
|
|
27
|
+
*/
|
|
28
|
+
export declare function expandSiteFiles(input: SiteFile[]): Promise<SiteFile[]>;
|
|
20
29
|
/**
|
|
21
30
|
* Turn the raw inputs into the file set to drop: unpack zips, drop junk, strip a
|
|
22
31
|
* single wrapping folder, require a root index.html. Throws an agent-facing error.
|
|
23
32
|
*/
|
|
24
33
|
export declare function normalizeSiteFiles(input: SiteFile[]): Promise<SiteFile[]>;
|
|
34
|
+
/**
|
|
35
|
+
* The static-site rules on a list `expandSiteFiles` already made (its zips are not
|
|
36
|
+
* unpacked again): strip a single wrapping folder, require a root index.html.
|
|
37
|
+
*/
|
|
38
|
+
export declare function staticSiteFiles(expanded: SiteFile[]): SiteFile[];
|
|
25
39
|
export declare function guessContentType(path: string): string;
|
|
26
40
|
/** The drop endpoint's body: one `files` part per file, the path as the filename. */
|
|
27
41
|
export declare function buildFormData(files: SiteFile[]): FormData;
|
|
@@ -58,7 +58,11 @@ export async function unzip(z) {
|
|
|
58
58
|
let p = u32(end + 16);
|
|
59
59
|
let total = 0;
|
|
60
60
|
const decoder = new TextDecoder();
|
|
61
|
-
|
|
61
|
+
const n = u16(end + 10);
|
|
62
|
+
if (n > MAX_SITE_FILES) {
|
|
63
|
+
throw new Error(`zip holds ${n} entries; a site upload is at most ${MAX_SITE_FILES} files`);
|
|
64
|
+
}
|
|
65
|
+
for (let i = 0; i < n; i++) {
|
|
62
66
|
const path = decoder.decode(z.slice(p + 46, p + 46 + u16(p + 28)));
|
|
63
67
|
const method = u16(p + 10);
|
|
64
68
|
const size = u32(p + 24);
|
|
@@ -89,6 +93,77 @@ export async function unzip(z) {
|
|
|
89
93
|
}
|
|
90
94
|
return out;
|
|
91
95
|
}
|
|
96
|
+
const CRC_TABLE = (() => {
|
|
97
|
+
const t = new Uint32Array(256);
|
|
98
|
+
for (let n = 0; n < 256; n++) {
|
|
99
|
+
let c = n;
|
|
100
|
+
for (let k = 0; k < 8; k++)
|
|
101
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
102
|
+
t[n] = c >>> 0;
|
|
103
|
+
}
|
|
104
|
+
return t;
|
|
105
|
+
})();
|
|
106
|
+
export function crc32(bytes) {
|
|
107
|
+
let c = 0xffffffff;
|
|
108
|
+
for (let i = 0; i < bytes.length; i++)
|
|
109
|
+
c = CRC_TABLE[(c ^ bytes[i]) & 0xff] ^ (c >>> 8);
|
|
110
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
111
|
+
}
|
|
112
|
+
/** A zip of the files, every entry stored (no compression), names in UTF-8. No zip64: under 4 GB and 65,535 entries. */
|
|
113
|
+
export function zipStored(files) {
|
|
114
|
+
const encoder = new TextEncoder();
|
|
115
|
+
const entries = files.map((f) => ({
|
|
116
|
+
name: encoder.encode(f.path),
|
|
117
|
+
bytes: f.bytes,
|
|
118
|
+
crc: crc32(f.bytes)
|
|
119
|
+
}));
|
|
120
|
+
const localSize = entries.reduce((sum, e) => sum + 30 + e.name.length + e.bytes.length, 0);
|
|
121
|
+
const centralSize = entries.reduce((sum, e) => sum + 46 + e.name.length, 0);
|
|
122
|
+
const out = new Uint8Array(localSize + centralSize + 22);
|
|
123
|
+
const view = new DataView(out.buffer);
|
|
124
|
+
const UTF8_FLAG = 0x0800;
|
|
125
|
+
let p = 0;
|
|
126
|
+
const offsets = [];
|
|
127
|
+
for (const e of entries) {
|
|
128
|
+
offsets.push(p);
|
|
129
|
+
view.setUint32(p, 0x04034b50, true);
|
|
130
|
+
view.setUint16(p + 4, 20, true); // version needed
|
|
131
|
+
view.setUint16(p + 6, UTF8_FLAG, true);
|
|
132
|
+
view.setUint16(p + 8, 0, true); // stored
|
|
133
|
+
view.setUint32(p + 10, 0, true); // time, date
|
|
134
|
+
view.setUint32(p + 14, e.crc, true);
|
|
135
|
+
view.setUint32(p + 18, e.bytes.length, true);
|
|
136
|
+
view.setUint32(p + 22, e.bytes.length, true);
|
|
137
|
+
view.setUint16(p + 26, e.name.length, true);
|
|
138
|
+
view.setUint16(p + 28, 0, true);
|
|
139
|
+
out.set(e.name, p + 30);
|
|
140
|
+
out.set(e.bytes, p + 30 + e.name.length);
|
|
141
|
+
p += 30 + e.name.length + e.bytes.length;
|
|
142
|
+
}
|
|
143
|
+
const centralStart = p;
|
|
144
|
+
entries.forEach((e, i) => {
|
|
145
|
+
view.setUint32(p, 0x02014b50, true);
|
|
146
|
+
view.setUint16(p + 4, 20, true); // version made by
|
|
147
|
+
view.setUint16(p + 6, 20, true); // version needed
|
|
148
|
+
view.setUint16(p + 8, UTF8_FLAG, true);
|
|
149
|
+
view.setUint16(p + 10, 0, true);
|
|
150
|
+
view.setUint32(p + 12, 0, true);
|
|
151
|
+
view.setUint32(p + 16, e.crc, true);
|
|
152
|
+
view.setUint32(p + 20, e.bytes.length, true);
|
|
153
|
+
view.setUint32(p + 24, e.bytes.length, true);
|
|
154
|
+
view.setUint16(p + 28, e.name.length, true);
|
|
155
|
+
// extra, comment, disk, internal attrs, external attrs: zero
|
|
156
|
+
view.setUint32(p + 42, offsets[i], true);
|
|
157
|
+
out.set(e.name, p + 46);
|
|
158
|
+
p += 46 + e.name.length;
|
|
159
|
+
});
|
|
160
|
+
view.setUint32(p, 0x06054b50, true);
|
|
161
|
+
view.setUint16(p + 8, entries.length, true);
|
|
162
|
+
view.setUint16(p + 10, entries.length, true);
|
|
163
|
+
view.setUint32(p + 12, p - centralStart, true);
|
|
164
|
+
view.setUint32(p + 16, centralStart, true);
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
92
167
|
// A zip is recognised by its bytes (local-file-header or empty-archive signature), not its name:
|
|
93
168
|
// an attachment can arrive without a file name, and a zip's path then carries no extension.
|
|
94
169
|
export const isZip = (f) => (f.bytes.length >= 4 &&
|
|
@@ -99,18 +174,25 @@ export const isZip = (f) => (f.bytes.length >= 4 &&
|
|
|
99
174
|
/\.zip$/i.test(f.path);
|
|
100
175
|
const isRootIndex = (p) => /^index\.html?$/i.test(p);
|
|
101
176
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
177
|
+
* The file set as uploaded: zips unpacked, paths cleaned, junk dropped, within the file
|
|
178
|
+
* count and the byte budget for the whole upload. No site-shape rule yet: a Wix Astro
|
|
179
|
+
* build is told apart from a static site on this list.
|
|
104
180
|
*/
|
|
105
|
-
export async function
|
|
106
|
-
|
|
181
|
+
export async function expandSiteFiles(input) {
|
|
182
|
+
const list = [];
|
|
183
|
+
let bytes = 0;
|
|
107
184
|
for (const f of input) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
185
|
+
const added = isZip(f) ? await unzip(f.bytes) : [f];
|
|
186
|
+
list.push(...added);
|
|
187
|
+
bytes += added.reduce((sum, a) => sum + a.bytes.byteLength, 0);
|
|
188
|
+
if (list.length > MAX_SITE_FILES) {
|
|
189
|
+
throw new Error(`Too many files (more than ${MAX_SITE_FILES}); a site upload is at most ${MAX_SITE_FILES}.`);
|
|
190
|
+
}
|
|
191
|
+
if (bytes > MAX_UNZIP_BYTES) {
|
|
192
|
+
throw new Error(`The upload expands past ${MAX_UNZIP_BYTES} bytes; send only the site's files.`);
|
|
193
|
+
}
|
|
112
194
|
}
|
|
113
|
-
|
|
195
|
+
return list
|
|
114
196
|
.map((f) => ({
|
|
115
197
|
...f,
|
|
116
198
|
path: f.path.replace(/\\/g, '/').replace(/^\.?\//, '')
|
|
@@ -119,6 +201,20 @@ export async function normalizeSiteFiles(input) {
|
|
|
119
201
|
!f.path.startsWith('__MACOSX/') &&
|
|
120
202
|
!/(^|\/)\.DS_Store$/.test(f.path) &&
|
|
121
203
|
!f.path.split('/').includes('..'));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Turn the raw inputs into the file set to drop: unpack zips, drop junk, strip a
|
|
207
|
+
* single wrapping folder, require a root index.html. Throws an agent-facing error.
|
|
208
|
+
*/
|
|
209
|
+
export async function normalizeSiteFiles(input) {
|
|
210
|
+
return staticSiteFiles(await expandSiteFiles(input));
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The static-site rules on a list `expandSiteFiles` already made (its zips are not
|
|
214
|
+
* unpacked again): strip a single wrapping folder, require a root index.html.
|
|
215
|
+
*/
|
|
216
|
+
export function staticSiteFiles(expanded) {
|
|
217
|
+
let list = expanded;
|
|
122
218
|
if (list.length === 0) {
|
|
123
219
|
throw new Error('No files to upload: pass the site in `files` or `attachments`.');
|
|
124
220
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { type SiteFile } from './site-files.js';
|
|
1
2
|
import { WixMcpServer } from '../wix-mcp-server.js';
|
|
2
3
|
import { type McpAuthenticationStrategy } from '../auth/index.js';
|
|
3
4
|
export declare const UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME = "UploadHeadlessWebsiteFiles";
|
|
5
|
+
/** The largest site zip `bundle-upload-url` presigns (headless-business-setup's MAX_BUNDLE_BYTES). */
|
|
6
|
+
export declare const MAX_BUNDLE_BYTES: number;
|
|
4
7
|
/**
|
|
5
8
|
* Attribution the drop endpoint stamps on its upload BI event (`source` from
|
|
6
9
|
* `campaign`, `agent` from `agent`, both ≤ 64 chars) — the same parser as the
|
|
@@ -17,12 +20,18 @@ export interface UploadHeadlessWebsiteFilesOptions {
|
|
|
17
20
|
clientName?: string;
|
|
18
21
|
}
|
|
19
22
|
export declare const UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS: {
|
|
20
|
-
readonly 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
|
|
21
|
-
readonly attachments:
|
|
22
|
-
readonly
|
|
23
|
+
readonly files: "The site files as text, so file bytes never pass through a code string. Sections delimited by a line `=== FILE: <path> ===` (text, raw) or `=== FILE: <path> base64 ===` (binary, base64); `<path>` is relative to the site root; a static site must include `index.html`. Use base64 for small binaries (icons, logos, fonts); large images/video belong behind a URL in the HTML, not in the bundle. Optional when `attachments` carries the files. A Wix Astro build (`.wix/build-metadata.json` and `dist/` as `astro build` with the Wix hosting adapter wrote them, zipped from the project root) needs no `index.html`: it is released to the site's Wix hosting instead of dropped, and the project's other files in the upload are stored as its source when the site's repository accepts it.";
|
|
24
|
+
readonly attachments: string;
|
|
25
|
+
readonly prepare: "Phase one of the two-call upload of a Wix Astro build, for a shell that reaches the internet and no file handle (a sandbox whose network is limited to a few hosts cannot upload to the URLs): zip the build first, then `true` with its `size` returns presigned upload URLs for the build zip and, when the site's repository takes it, the project source zip, as `curl` commands to run verbatim, with the exact second call. The two calls: {siteId, prepare: true, size}, then {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. No `files`, `attachments`, `uploadKey` or `sourceUploadId` on the first; `expectedHeadRevisionId` and `releaseNotes` given on the first are echoed into the second. Not for a static site: send its files in `files` or `attachments`, or drop them as the StartDeployWebsite recipe shows.";
|
|
26
|
+
readonly size: "Phase one, with `prepare: true` only, and required there: the build zip's length in bytes (`wc -c < build.zip`), 1 to 104857600 (100 MB). The upload URL takes a body of exactly this length; a zip changed after the prepare needs a new prepare.";
|
|
27
|
+
readonly uploadKey: "Phase two of the two-call upload: the `uploadKey` the `prepare` call returned, after the build zip was PUT to its URL. Releases that build to the site: {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. Excludes `files` and `attachments`.";
|
|
28
|
+
readonly sourceUploadId: "Phase two: the `sourceUploadId` the `prepare` call returned, after the source zip was PUT to its URL. Stores the project source with the release. Needs `uploadKey`; leave it out when the prepare returned no source upload.";
|
|
29
|
+
readonly expectedHeadRevisionId: "The repository revision your project started from, so the source replaces only that and a commit made since (another chat, a Dev Machine) is never dropped unseen: the revision you downloaded the project at; for a project started here, the `headRevisionId` the `prepare` call returned. Leave it out only for a repository with nothing in it yet. Goes with `uploadKey`, or with `files`/`attachments` holding a build.";
|
|
30
|
+
readonly releaseNotes: "Optional: one line on what this release changes, at most 200 characters, kept with the deployment of a Wix Astro build, whether it comes in `files`/`attachments` or as the second call with `uploadKey` (given on the `prepare` call it is echoed into the second). A static drop keeps no notes.";
|
|
31
|
+
readonly siteId: "The metaSiteId of the Wix Headless site to upload into: the one returned by the provision call of the StartDeployWebsite recipe for a new site, or of a site deployed earlier to replace its files. Every upload replaces the whole file set, so send all files; the URL stays the same. A build released here replaces static pages dropped earlier, at the same URL.";
|
|
23
32
|
readonly calledStartDeployWebsite: "Whether you called StartDeployWebsite earlier in this conversation and are following the recipe it returned. Answer truthfully; it is recorded, not checked.";
|
|
24
|
-
readonly agentIdentity: "Which agent or product you are and the harness you run in.";
|
|
25
|
-
readonly reason: "One sentence: the user request that led here and what is being published.";
|
|
33
|
+
readonly agentIdentity: "Which agent or product you are, which model if you know it, and the harness you run in. No personal data: no names, emails, phones, passwords, keys or local paths.";
|
|
34
|
+
readonly reason: "One sentence: the user request that led here and what is being published. No personal data: no names, emails, phones, passwords, keys or local paths.";
|
|
26
35
|
};
|
|
27
36
|
export interface DropResult {
|
|
28
37
|
metaSiteId: string;
|
|
@@ -31,5 +40,79 @@ export interface DropResult {
|
|
|
31
40
|
files: string[];
|
|
32
41
|
}
|
|
33
42
|
export declare function formatDropResult(r: DropResult): string;
|
|
43
|
+
/** One presigned PUT: where the zip goes and the headers the storage requires on it. */
|
|
44
|
+
interface PresignedPut {
|
|
45
|
+
putUrl: string;
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
expiresAt?: string;
|
|
48
|
+
}
|
|
49
|
+
/** `POST /{siteId}/bundle-upload-url`: the site half, plus the Code Store half when the site has a source repository. */
|
|
50
|
+
export interface BundleUploadUrls extends PresignedPut {
|
|
51
|
+
uploadKey: string;
|
|
52
|
+
source?: PresignedPut & {
|
|
53
|
+
uploadId: string;
|
|
54
|
+
headRevisionId?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** `POST /{siteId}/release-bundle`. */
|
|
58
|
+
/**
|
|
59
|
+
* What the tool BI event records about an upload, beside the result text: the path it
|
|
60
|
+
* took (`drop` static pages, `build` an attached Astro build over the bundle routes,
|
|
61
|
+
* `prepare` and `release-bundle` the two calls) and whether the source was stored.
|
|
62
|
+
*/
|
|
63
|
+
export interface UploadBiFields {
|
|
64
|
+
path: 'drop' | 'build' | 'prepare' | 'release-bundle';
|
|
65
|
+
sourceOffered?: boolean;
|
|
66
|
+
sourceStored?: boolean;
|
|
67
|
+
sourceFiles?: number;
|
|
68
|
+
emptyStaticsDropped?: number;
|
|
69
|
+
files?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface BundleReleaseResult {
|
|
72
|
+
deploymentId?: string;
|
|
73
|
+
siteUrl?: string;
|
|
74
|
+
revision?: {
|
|
75
|
+
id?: string;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** The prepare reply: what the agent runs, verbatim, and the exact second call. */
|
|
79
|
+
export declare function formatPrepareResult(siteId: string, urls: BundleUploadUrls, size?: number, given?: {
|
|
80
|
+
expectedHeadRevisionId?: string;
|
|
81
|
+
releaseNotes?: string;
|
|
82
|
+
}): string;
|
|
83
|
+
/** The release-bundle reply, for both the two-call form and a build uploaded as files. */
|
|
84
|
+
export declare function formatBundleReleaseResult(siteId: string, r: BundleReleaseResult, notes?: string[]): string;
|
|
85
|
+
/**
|
|
86
|
+
* How the agent starts over: in the two-call form a new `prepare`, after a build
|
|
87
|
+
* uploaded as files the same call again (the tool asks for new URLs itself).
|
|
88
|
+
*/
|
|
89
|
+
export type BundleRetry = 'prepare' | 'resend';
|
|
90
|
+
/** The bundle-upload-url route's errors, as the agent should act on them. */
|
|
91
|
+
export declare function describeBundleUploadUrlError(err: unknown): string;
|
|
92
|
+
/** The deployment label the service keeps is 200 characters; longer notes are cut, not refused. */
|
|
93
|
+
export declare const RELEASE_NOTES_MAX_CHARS = 200;
|
|
94
|
+
export declare function releaseNotesForService(notes: string): string;
|
|
95
|
+
/** The release-bundle route's errors, as the agent should act on them. */
|
|
96
|
+
export declare function describeBundleReleaseError(err: unknown, uploadKey: string, retry?: BundleRetry): string;
|
|
97
|
+
/**
|
|
98
|
+
* A build that came as files, released over the bundle routes: an upload URL for the
|
|
99
|
+
* site zip's size, the zip PUT there, the source zip PUT to the repository's URL when
|
|
100
|
+
* the service offered one and the upload holds source files, then the release with
|
|
101
|
+
* the ids. `onStaged` hears the uploadKey once the site zip is staged.
|
|
102
|
+
*/
|
|
103
|
+
export declare function releaseBuildByUrl(o: {
|
|
104
|
+
siteId: string;
|
|
105
|
+
siteZip: Uint8Array;
|
|
106
|
+
sourceFiles: SiteFile[];
|
|
107
|
+
siteHeaders: Record<string, string>;
|
|
108
|
+
fetchImpl: typeof fetch;
|
|
109
|
+
/** The revision the agent's project started from; never the head read here, which would replace work committed since. */
|
|
110
|
+
expectedHeadRevisionId?: string;
|
|
111
|
+
/** One line kept with the deployment; cut to the service's limit. */
|
|
112
|
+
releaseNotes?: string;
|
|
113
|
+
onStaged?: (uploadKey: string) => void;
|
|
114
|
+
log?: (line: string) => void;
|
|
115
|
+
}): Promise<BundleReleaseResult>;
|
|
34
116
|
export declare function addUploadHeadlessWebsiteFilesTool(server: WixMcpServer, options: UploadHeadlessWebsiteFilesOptions): void;
|
|
117
|
+
export {};
|
|
35
118
|
//# sourceMappingURL=upload-headless-website-files.d.ts.map
|