@wp-playground/mcp 3.1.13 → 3.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/mcp",
3
- "version": "3.1.13",
3
+ "version": "3.1.15",
4
4
  "description": "MCP server for WordPress Playground - enables AI agents to interact with the WordPress Playground website.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,10 +61,10 @@
61
61
  "ws": "^8.18.0",
62
62
  "yargs": "17.7.2",
63
63
  "zod": "^4.3",
64
- "@wp-playground/remote": "3.1.13",
65
- "@php-wasm/universal": "3.1.13"
64
+ "@wp-playground/remote": "3.1.15",
65
+ "@php-wasm/universal": "3.1.15"
66
66
  },
67
- "gitHead": "4523244be9c3e12aef7889bbaca81ca40bcfaef3",
67
+ "gitHead": "65a430c046c86341e7dc3cf6f30c9a5aeb8aa67d",
68
68
  "packageManager": "npm@10.9.2",
69
69
  "overrides": {
70
70
  "rollup": "^4.34.6",
@@ -0,0 +1,123 @@
1
+ async function h(e) {
2
+ const [t, o] = await Promise.all([
3
+ e.getCurrentURL(),
4
+ e.run({
5
+ code: `<?php
6
+ require_once "/wordpress/wp-load.php";
7
+ echo json_encode([
8
+ "documentRoot" => ABSPATH,
9
+ "wpVersion" => get_bloginfo("version"),
10
+ "siteUrl" => get_site_url(),
11
+ "phpVersion" => phpversion(),
12
+ ]);`
13
+ }).then((n) => n.text)
14
+ ]);
15
+ let r;
16
+ try {
17
+ r = JSON.parse(o);
18
+ } catch {
19
+ r = {};
20
+ }
21
+ return {
22
+ url: String(t),
23
+ documentRoot: r.documentRoot ?? "/wordpress",
24
+ siteUrl: r.siteUrl ?? String(t),
25
+ wpVersion: r.wpVersion ?? "unknown",
26
+ phpVersion: r.phpVersion ?? "unknown"
27
+ };
28
+ }
29
+ const _ = {
30
+ playground_execute_php: (e, t) => e.run({ code: t.code }),
31
+ playground_request: async (e, t) => {
32
+ const o = t.url, r = t.method ?? "GET", n = {
33
+ ...t.headers ?? {}
34
+ }, a = t.body;
35
+ try {
36
+ const c = new URL(o, "http://localhost"), u = o.includes("/wp-json/") || c.searchParams.has("rest_route");
37
+ u && a && !Object.keys(n).some(
38
+ (s) => s.toLowerCase() === "content-type"
39
+ ) && (n["Content-Type"] = "application/json");
40
+ const p = Object.keys(n).some(
41
+ (s) => s.toLowerCase() === "x-wp-nonce"
42
+ );
43
+ if (u && !p) {
44
+ const s = Math.random().toString(36).slice(2, 10), d = `/wordpress/wp-content/mcp-nonce-${s}.php`, l = `/wp-content/mcp-nonce-${s}.php`;
45
+ await e.writeFile(d, "<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');");
46
+ let i = "";
47
+ try {
48
+ i = (await e.request({
49
+ url: l,
50
+ method: "GET"
51
+ })).text.trim();
52
+ } finally {
53
+ await e.unlink(d);
54
+ }
55
+ i && i !== "0" && (n["X-WP-Nonce"] = i);
56
+ }
57
+ } catch {
58
+ }
59
+ return await e.request({ url: o, method: r, headers: n, body: a });
60
+ },
61
+ playground_navigate: async (e, t) => (await e.goTo(t.path), { url: await e.getCurrentURL() }),
62
+ playground_get_current_url: async (e) => ({
63
+ url: await e.getCurrentURL()
64
+ }),
65
+ playground_get_site_info: (e) => h(e),
66
+ playground_read_file: async (e, t) => ({
67
+ contents: await e.readFileAsText(t.path)
68
+ }),
69
+ playground_write_file: async (e, t) => (await e.writeFile(
70
+ t.path,
71
+ t.contents
72
+ ), { success: !0 }),
73
+ playground_list_files: async (e, t) => ({
74
+ files: await e.listFiles(t.path)
75
+ }),
76
+ playground_mkdir: async (e, t) => (await e.mkdirTree(t.path), { success: !0 }),
77
+ playground_delete_file: async (e, t) => (await e.unlink(t.path), { success: !0 }),
78
+ playground_delete_directory: async (e, t) => (await e.rmdir(t.path, {
79
+ recursive: t.recursive ?? !1
80
+ }), { success: !0 }),
81
+ playground_file_exists: async (e, t) => ({
82
+ exists: await e.fileExists(t.path)
83
+ })
84
+ };
85
+ function g(e) {
86
+ const t = new TextDecoder(), o = {
87
+ async run(r) {
88
+ const n = await e.run(r);
89
+ return {
90
+ text: t.decode(n.bytes),
91
+ errors: n.errors,
92
+ exitCode: n.exitCode
93
+ };
94
+ },
95
+ async request(r) {
96
+ const n = await e.request({
97
+ url: r.url,
98
+ method: r.method,
99
+ headers: r.headers,
100
+ body: r.body
101
+ });
102
+ return {
103
+ text: t.decode(n.bytes),
104
+ httpStatusCode: n.httpStatusCode,
105
+ headers: n.headers
106
+ };
107
+ }
108
+ };
109
+ return new Proxy(e, {
110
+ get: (r, n) => {
111
+ const a = o[n];
112
+ if (a !== void 0)
113
+ return a;
114
+ const c = r[n];
115
+ return typeof c == "function" ? c.bind(r) : c;
116
+ }
117
+ });
118
+ }
119
+ export {
120
+ g as c,
121
+ _ as t
122
+ };
123
+ //# sourceMappingURL=tool-executors-B9DCzwoD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executors-B9DCzwoD.js","sources":["../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst body = input['body'] as string | undefined;\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["executeSiteInfo","client","url","infoText","resp","info","toolExecutors","input","method","headers","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","prop","override","val"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ "use strict";async function h(e){const[t,n]=await Promise.all([e.getCurrentURL(),e.run({code:`<?php
2
+ require_once "/wordpress/wp-load.php";
3
+ echo json_encode([
4
+ "documentRoot" => ABSPATH,
5
+ "wpVersion" => get_bloginfo("version"),
6
+ "siteUrl" => get_site_url(),
7
+ "phpVersion" => phpversion(),
8
+ ]);`}).then(o=>o.text)]);let r;try{r=JSON.parse(n)}catch{r={}}return{url:String(t),documentRoot:r.documentRoot??"/wordpress",siteUrl:r.siteUrl??String(t),wpVersion:r.wpVersion??"unknown",phpVersion:r.phpVersion??"unknown"}}const w={playground_execute_php:(e,t)=>e.run({code:t.code}),playground_request:async(e,t)=>{const n=t.url,r=t.method??"GET",o={...t.headers??{}},a=t.body;try{const c=new URL(n,"http://localhost"),u=n.includes("/wp-json/")||c.searchParams.has("rest_route");u&&a&&!Object.keys(o).some(s=>s.toLowerCase()==="content-type")&&(o["Content-Type"]="application/json");const p=Object.keys(o).some(s=>s.toLowerCase()==="x-wp-nonce");if(u&&!p){const s=Math.random().toString(36).slice(2,10),d=`/wordpress/wp-content/mcp-nonce-${s}.php`,l=`/wp-content/mcp-nonce-${s}.php`;await e.writeFile(d,"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');");let i="";try{i=(await e.request({url:l,method:"GET"})).text.trim()}finally{await e.unlink(d)}i&&i!=="0"&&(o["X-WP-Nonce"]=i)}}catch{}return await e.request({url:n,method:r,headers:o,body:a})},playground_navigate:async(e,t)=>(await e.goTo(t.path),{url:await e.getCurrentURL()}),playground_get_current_url:async e=>({url:await e.getCurrentURL()}),playground_get_site_info:e=>h(e),playground_read_file:async(e,t)=>({contents:await e.readFileAsText(t.path)}),playground_write_file:async(e,t)=>(await e.writeFile(t.path,t.contents),{success:!0}),playground_list_files:async(e,t)=>({files:await e.listFiles(t.path)}),playground_mkdir:async(e,t)=>(await e.mkdirTree(t.path),{success:!0}),playground_delete_file:async(e,t)=>(await e.unlink(t.path),{success:!0}),playground_delete_directory:async(e,t)=>(await e.rmdir(t.path,{recursive:t.recursive??!1}),{success:!0}),playground_file_exists:async(e,t)=>({exists:await e.fileExists(t.path)})};function y(e){const t=new TextDecoder,n={async run(r){const o=await e.run(r);return{text:t.decode(o.bytes),errors:o.errors,exitCode:o.exitCode}},async request(r){const o=await e.request({url:r.url,method:r.method,headers:r.headers,body:r.body});return{text:t.decode(o.bytes),httpStatusCode:o.httpStatusCode,headers:o.headers}}};return new Proxy(e,{get:(r,o)=>{const a=n[o];if(a!==void 0)return a;const c=r[o];return typeof c=="function"?c.bind(r):c}})}exports.createToolClient=y;exports.toolExecutors=w;
9
+ //# sourceMappingURL=tool-executors-Bg92LDCE.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-executors-Bg92LDCE.cjs","sources":["../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst url = input['url'] as string;\n\t\tconst method = (input['method'] as string) ?? 'GET';\n\t\tconst headers = {\n\t\t\t...((input['headers'] as Record<string, string>) ?? {}),\n\t\t};\n\t\tconst body = input['body'] as string | undefined;\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url, 'http://localhost');\n\t\t\tconst isRestApi =\n\t\t\t\turl.includes('/wp-json/') ||\n\t\t\t\tparsedUrl.searchParams.has('rest_route');\n\n\t\t\t// Auto-set Content-Type for REST API JSON bodies.\n\t\t\tif (\n\t\t\t\tisRestApi &&\n\t\t\t\tbody &&\n\t\t\t\t!Object.keys(headers).some(\n\t\t\t\t\t(k) => k.toLowerCase() === 'content-type'\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\t}\n\n\t\t\tconst hasNonce = Object.keys(headers).some(\n\t\t\t\t(k) => k.toLowerCase() === 'x-wp-nonce'\n\t\t\t);\n\n\t\t\tif (isRestApi && !hasNonce) {\n\t\t\t\t// Generate the nonce via a temporary PHP file requested\n\t\t\t\t// through request() — not run() — so that the cookie\n\t\t\t\t// store is included and WordPress ties the nonce to\n\t\t\t\t// the logged-in user.\n\t\t\t\tconst nonceId = Math.random().toString(36).slice(2, 10);\n\t\t\t\tconst noncePath = `/wordpress/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceUrl = `/wp-content/mcp-nonce-${nonceId}.php`;\n\t\t\t\tconst nonceCode =\n\t\t\t\t\t\"<?php require_once '/wordpress/wp-load.php'; echo wp_create_nonce('wp_rest');\";\n\t\t\t\tawait client.writeFile(noncePath, nonceCode);\n\t\t\t\tlet nonce = '';\n\t\t\t\ttry {\n\t\t\t\t\tconst nonceResp = await client.request({\n\t\t\t\t\t\turl: nonceUrl,\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t});\n\t\t\t\t\tnonce = nonceResp.text.trim();\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.unlink(noncePath);\n\t\t\t\t}\n\t\t\t\tif (nonce && nonce !== '0') {\n\t\t\t\t\theaders['X-WP-Nonce'] = nonce;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Nonce generation failed — proceed without it.\n\t\t}\n\n\t\treturn await client.request({ url, method, headers, body });\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["executeSiteInfo","client","url","infoText","resp","info","toolExecutors","input","method","headers","body","parsedUrl","isRestApi","k","hasNonce","nonceId","noncePath","nonceUrl","nonce","createToolClient","decoder","overrides","options","target","prop","override","val"],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- "use strict";async function u(r){const[e,s]=await Promise.all([r.getCurrentURL(),r.run({code:`<?php
2
- require_once "/wordpress/wp-load.php";
3
- echo json_encode([
4
- "documentRoot" => ABSPATH,
5
- "wpVersion" => get_bloginfo("version"),
6
- "siteUrl" => get_site_url(),
7
- "phpVersion" => phpversion(),
8
- ]);`}).then(o=>o.text)]);let t;try{t=JSON.parse(s)}catch{t={}}return{url:String(e),documentRoot:t.documentRoot??"/wordpress",siteUrl:t.siteUrl??String(e),wpVersion:t.wpVersion??"unknown",phpVersion:t.phpVersion??"unknown"}}const i={playground_execute_php:(r,e)=>r.run({code:e.code}),playground_request:async(r,e)=>{const s={url:e.url,method:e.method??"GET"};return e.headers&&(s.headers=e.headers),e.body&&(s.body=e.body),await r.request(s)},playground_navigate:async(r,e)=>(await r.goTo(e.path),{url:await r.getCurrentURL()}),playground_get_current_url:async r=>({url:await r.getCurrentURL()}),playground_get_site_info:r=>u(r),playground_read_file:async(r,e)=>({contents:await r.readFileAsText(e.path)}),playground_write_file:async(r,e)=>(await r.writeFile(e.path,e.contents),{success:!0}),playground_list_files:async(r,e)=>({files:await r.listFiles(e.path)}),playground_mkdir:async(r,e)=>(await r.mkdirTree(e.path),{success:!0}),playground_delete_file:async(r,e)=>(await r.unlink(e.path),{success:!0}),playground_delete_directory:async(r,e)=>(await r.rmdir(e.path,{recursive:e.recursive??!1}),{success:!0}),playground_file_exists:async(r,e)=>({exists:await r.fileExists(e.path)})};function d(r){const e=new TextDecoder,s={async run(t){const o=await r.run(t);return{text:e.decode(o.bytes),errors:o.errors,exitCode:o.exitCode}},async request(t){const o=await r.request({url:t.url,method:t.method,headers:t.headers,body:t.body});return{text:e.decode(o.bytes),httpStatusCode:o.httpStatusCode,headers:o.headers}}};return new Proxy(r,{get:(t,o)=>{const a=s[o];if(a!==void 0)return a;const n=t[o];return typeof n=="function"?n.bind(t):n}})}exports.createToolClient=d;exports.toolExecutors=i;
9
- //# sourceMappingURL=tool-executors-ecXfLzk7.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-executors-ecXfLzk7.cjs","sources":["../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst options: {\n\t\t\turl: string;\n\t\t\tmethod: string;\n\t\t\theaders?: Record<string, string>;\n\t\t\tbody?: string;\n\t\t} = {\n\t\t\turl: input['url'] as string,\n\t\t\tmethod: (input['method'] as string) ?? 'GET',\n\t\t};\n\t\tif (input['headers']) {\n\t\t\toptions.headers = input['headers'] as Record<string, string>;\n\t\t}\n\t\tif (input['body']) {\n\t\t\toptions.body = input['body'] as string;\n\t\t}\n\t\treturn await client.request(options);\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["executeSiteInfo","client","url","infoText","resp","info","toolExecutors","input","options","createToolClient","decoder","overrides","target","prop","override","val"],"mappings":"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"}
@@ -1,100 +0,0 @@
1
- async function u(r) {
2
- const [e, o] = await Promise.all([
3
- r.getCurrentURL(),
4
- r.run({
5
- code: `<?php
6
- require_once "/wordpress/wp-load.php";
7
- echo json_encode([
8
- "documentRoot" => ABSPATH,
9
- "wpVersion" => get_bloginfo("version"),
10
- "siteUrl" => get_site_url(),
11
- "phpVersion" => phpversion(),
12
- ]);`
13
- }).then((s) => s.text)
14
- ]);
15
- let t;
16
- try {
17
- t = JSON.parse(o);
18
- } catch {
19
- t = {};
20
- }
21
- return {
22
- url: String(e),
23
- documentRoot: t.documentRoot ?? "/wordpress",
24
- siteUrl: t.siteUrl ?? String(e),
25
- wpVersion: t.wpVersion ?? "unknown",
26
- phpVersion: t.phpVersion ?? "unknown"
27
- };
28
- }
29
- const d = {
30
- playground_execute_php: (r, e) => r.run({ code: e.code }),
31
- playground_request: async (r, e) => {
32
- const o = {
33
- url: e.url,
34
- method: e.method ?? "GET"
35
- };
36
- return e.headers && (o.headers = e.headers), e.body && (o.body = e.body), await r.request(o);
37
- },
38
- playground_navigate: async (r, e) => (await r.goTo(e.path), { url: await r.getCurrentURL() }),
39
- playground_get_current_url: async (r) => ({
40
- url: await r.getCurrentURL()
41
- }),
42
- playground_get_site_info: (r) => u(r),
43
- playground_read_file: async (r, e) => ({
44
- contents: await r.readFileAsText(e.path)
45
- }),
46
- playground_write_file: async (r, e) => (await r.writeFile(
47
- e.path,
48
- e.contents
49
- ), { success: !0 }),
50
- playground_list_files: async (r, e) => ({
51
- files: await r.listFiles(e.path)
52
- }),
53
- playground_mkdir: async (r, e) => (await r.mkdirTree(e.path), { success: !0 }),
54
- playground_delete_file: async (r, e) => (await r.unlink(e.path), { success: !0 }),
55
- playground_delete_directory: async (r, e) => (await r.rmdir(e.path, {
56
- recursive: e.recursive ?? !1
57
- }), { success: !0 }),
58
- playground_file_exists: async (r, e) => ({
59
- exists: await r.fileExists(e.path)
60
- })
61
- };
62
- function i(r) {
63
- const e = new TextDecoder(), o = {
64
- async run(t) {
65
- const s = await r.run(t);
66
- return {
67
- text: e.decode(s.bytes),
68
- errors: s.errors,
69
- exitCode: s.exitCode
70
- };
71
- },
72
- async request(t) {
73
- const s = await r.request({
74
- url: t.url,
75
- method: t.method,
76
- headers: t.headers,
77
- body: t.body
78
- });
79
- return {
80
- text: e.decode(s.bytes),
81
- httpStatusCode: s.httpStatusCode,
82
- headers: s.headers
83
- };
84
- }
85
- };
86
- return new Proxy(r, {
87
- get: (t, s) => {
88
- const a = o[s];
89
- if (a !== void 0)
90
- return a;
91
- const n = t[s];
92
- return typeof n == "function" ? n.bind(t) : n;
93
- }
94
- });
95
- }
96
- export {
97
- i as c,
98
- d as t
99
- };
100
- //# sourceMappingURL=tool-executors-pMxJ1GXT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-executors-pMxJ1GXT.js","sources":["../../../../packages/playground/mcp/src/tools/tool-executors.ts"],"sourcesContent":["/**\n * Shared tool executor functions.\n *\n * Both the MCP server and WebMCP call these executors so that tool\n * output shapes are defined in exactly one place. Each transport\n * provides its own ToolClient implementation that normalises I/O\n * differences (e.g. byte decoding).\n */\n\nimport type { PlaygroundClient } from '@wp-playground/remote';\nimport type { PHPRequest } from '@php-wasm/universal';\n\n/**\n * Minimal client interface consumed by tool executors.\n *\n * - WebMCP implements this by wrapping PlaygroundClient (decoding\n * response bytes via TextDecoder).\n * - The MCP server implements this by wrapping bridge.sendCommand\n * (bytes are already decoded at the bridge-client boundary).\n */\nexport interface ToolClient {\n\trun(options: {\n\t\tcode: string;\n\t}): Promise<{ text: string; errors: string; exitCode: number }>;\n\trequest(options: {\n\t\turl: string;\n\t\tmethod: string;\n\t\theaders?: Record<string, string>;\n\t\tbody?: string;\n\t}): Promise<{\n\t\ttext: string;\n\t\thttpStatusCode: number;\n\t\theaders: Record<string, string[]>;\n\t}>;\n\tgoTo(path: string): Promise<void>;\n\tgetCurrentURL(): Promise<string>;\n\treadFileAsText(path: string): Promise<string>;\n\twriteFile(path: string, contents: string): Promise<void>;\n\tlistFiles(path: string): Promise<string[]>;\n\tmkdirTree(path: string): Promise<void>;\n\tunlink(path: string): Promise<void>;\n\trmdir(path: string, options: { recursive: boolean }): Promise<void>;\n\tfileExists(path: string): Promise<boolean>;\n}\n\nexport interface SiteInfo {\n\turl: string;\n\tdocumentRoot: string;\n\tsiteUrl: string;\n\twpVersion: string;\n\tphpVersion: string;\n}\n\nasync function executeSiteInfo(client: ToolClient): Promise<SiteInfo> {\n\tconst [url, infoText] = await Promise.all([\n\t\tclient.getCurrentURL(),\n\t\tclient\n\t\t\t.run({\n\t\t\t\tcode: `<?php\n\t\t\trequire_once \"/wordpress/wp-load.php\";\n\t\t\techo json_encode([\n\t\t\t\t\"documentRoot\" => ABSPATH,\n\t\t\t\t\"wpVersion\" => get_bloginfo(\"version\"),\n\t\t\t\t\"siteUrl\" => get_site_url(),\n\t\t\t\t\"phpVersion\" => phpversion(),\n\t\t\t]);`,\n\t\t\t})\n\t\t\t.then((resp) => resp.text),\n\t]);\n\n\tlet info: Partial<Omit<SiteInfo, 'url'>>;\n\ttry {\n\t\tinfo = JSON.parse(infoText);\n\t} catch {\n\t\tinfo = {};\n\t}\n\n\treturn {\n\t\turl: String(url),\n\t\tdocumentRoot: info.documentRoot ?? '/wordpress',\n\t\tsiteUrl: info.siteUrl ?? String(url),\n\t\twpVersion: info.wpVersion ?? 'unknown',\n\t\tphpVersion: info.phpVersion ?? 'unknown',\n\t};\n}\n\nexport const toolExecutors: Record<\n\tstring,\n\t(client: ToolClient, input: Record<string, unknown>) => Promise<unknown>\n> = {\n\tplayground_execute_php: (client, input) =>\n\t\tclient.run({ code: input['code'] as string }),\n\n\tplayground_request: async (client, input) => {\n\t\tconst options: {\n\t\t\turl: string;\n\t\t\tmethod: string;\n\t\t\theaders?: Record<string, string>;\n\t\t\tbody?: string;\n\t\t} = {\n\t\t\turl: input['url'] as string,\n\t\t\tmethod: (input['method'] as string) ?? 'GET',\n\t\t};\n\t\tif (input['headers']) {\n\t\t\toptions.headers = input['headers'] as Record<string, string>;\n\t\t}\n\t\tif (input['body']) {\n\t\t\toptions.body = input['body'] as string;\n\t\t}\n\t\treturn await client.request(options);\n\t},\n\n\tplayground_navigate: async (client, input) => {\n\t\tawait client.goTo(input['path'] as string);\n\t\treturn { url: await client.getCurrentURL() };\n\t},\n\n\tplayground_get_current_url: async (client) => ({\n\t\turl: await client.getCurrentURL(),\n\t}),\n\n\tplayground_get_site_info: (client): Promise<SiteInfo> =>\n\t\texecuteSiteInfo(client),\n\n\tplayground_read_file: async (client, input) => ({\n\t\tcontents: await client.readFileAsText(input['path'] as string),\n\t}),\n\n\tplayground_write_file: async (client, input) => {\n\t\tawait client.writeFile(\n\t\t\tinput['path'] as string,\n\t\t\tinput['contents'] as string\n\t\t);\n\t\treturn { success: true };\n\t},\n\n\tplayground_list_files: async (client, input) => ({\n\t\tfiles: await client.listFiles(input['path'] as string),\n\t}),\n\n\tplayground_mkdir: async (client, input) => {\n\t\tawait client.mkdirTree(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_file: async (client, input) => {\n\t\tawait client.unlink(input['path'] as string);\n\t\treturn { success: true };\n\t},\n\n\tplayground_delete_directory: async (client, input) => {\n\t\tawait client.rmdir(input['path'] as string, {\n\t\t\trecursive: (input['recursive'] as boolean) ?? false,\n\t\t});\n\t\treturn { success: true };\n\t},\n\n\tplayground_file_exists: async (client, input) => ({\n\t\texists: await client.fileExists(input['path'] as string),\n\t}),\n};\n\n/**\n * Wrap a PlaygroundClient as a ToolClient.\n *\n * Most methods pass through directly. Only `run` and `request`\n * are intercepted to decode PHP/HTTP response bytes into plain\n * strings via TextDecoder.\n */\nexport function createToolClient(client: PlaygroundClient): ToolClient {\n\tconst decoder = new TextDecoder();\n\tconst overrides: Partial<ToolClient> = {\n\t\tasync run(options) {\n\t\t\tconst resp = await client.run(options);\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\terrors: resp.errors,\n\t\t\t\texitCode: resp.exitCode,\n\t\t\t};\n\t\t},\n\t\tasync request(options) {\n\t\t\tconst resp = await client.request({\n\t\t\t\turl: options.url,\n\t\t\t\tmethod: options.method as PHPRequest['method'],\n\t\t\t\theaders: options.headers,\n\t\t\t\tbody: options.body,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\ttext: decoder.decode(resp.bytes),\n\t\t\t\thttpStatusCode: resp.httpStatusCode,\n\t\t\t\theaders: resp.headers,\n\t\t\t};\n\t\t},\n\t};\n\treturn new Proxy(client as unknown as ToolClient, {\n\t\tget: (target, prop: string) => {\n\t\t\tconst override = (overrides as Record<string, unknown>)[prop];\n\t\t\tif (override !== undefined) {\n\t\t\t\treturn override;\n\t\t\t}\n\t\t\tconst val = (target as unknown as Record<string, unknown>)[prop];\n\t\t\treturn typeof val === 'function' ? val.bind(target) : val;\n\t\t},\n\t});\n}\n"],"names":["executeSiteInfo","client","url","infoText","resp","info","toolExecutors","input","options","createToolClient","decoder","overrides","target","prop","override","val"],"mappings":"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"}