@@ -7,6 +7,18 @@ import log from '../logger';
77import { ConfigStorage , type UserScript , type UserScriptMetadata } from '../storage/config' ;
88import { parseUserScript , matchesUrl , ScriptParsingError } from './script-parser' ;
99
10+ // Type declarations for Trusted Types policy created in webmcp-polyfill.js
11+ // TrustedScriptURL is the return type from createScriptURL()
12+ type TrustedScriptURL = string & { __brand : 'TrustedScriptURL' } ;
13+
14+ declare global {
15+ interface Window {
16+ __agentboardTTPolicy ?: {
17+ createScriptURL : ( url : string ) => string | TrustedScriptURL ;
18+ } ;
19+ }
20+ }
21+
1022const configStorage = ConfigStorage . getInstance ( ) ;
1123
1224export interface InjectionOptions {
@@ -18,13 +30,21 @@ export interface InjectionOptions {
1830/**
1931 * Wraps a user script module for execution in MAIN world
2032 * Converts ES module exports to window.agent.registerTool() calls
33+ * All transformations happen here in the background worker,
34+ * not at runtime in the page.
2135 */
2236function wrapScriptForInjection ( code : string , metadata : UserScriptMetadata ) : string {
2337 // Generate a unique script name for debugging (namespace is now required)
2438 const scriptName = `${ metadata . namespace } :${ metadata . name } ` ;
39+ const toolName = `${ metadata . namespace } _${ metadata . name } ` ;
40+
41+ const transformedCode = code
42+ . replace ( / ^ [ \s \n ] * ' u s e w e b m c p - t o o l v \d + ' ; [ \s \n ] * / m, '' ) // Remove pragma
43+ . replace ( / e x p o r t \s + c o n s t \s + m e t a d a t a \s * = / g, 'const metadata =' )
44+ . replace ( / e x p o r t \s + ( a s y n c \s + ) ? f u n c t i o n \s + e x e c u t e / g, '$1function execute' )
45+ . replace ( / e x p o r t \s + f u n c t i o n \s + s h o u l d R e g i s t e r / g, 'function shouldRegister' ) ;
2546
26- // Wrap module code to execute in MAIN world
27- // Converts ES module exports to tool registration
47+ // Wrap the PRE-TRANSFORMED code for direct execution (no eval/Function needed)
2848 return `
2949(function() {
3050 'use strict';
@@ -34,59 +54,54 @@ function wrapScriptForInjection(code: string, metadata: UserScriptMetadata): str
3454 console.log('[WebMCP] Script already injected:', scriptId);
3555 return;
3656 }
37-
57+
3858 // Mark as injected
3959 window.__webmcpInjected = window.__webmcpInjected || {};
4060 window.__webmcpInjected[scriptId] = true;
41-
42- // Execute module code with export interception
61+
62+ console.log('[WebMCP] Executing user script: ${ scriptName } ');
63+
4364 try {
44- // Transform the module code to work in a non-module context
45- const transformedCode = ${ JSON . stringify ( code ) }
46- .replace(/^\\s*'use webmcp-tool v\\d+';\\s*/, '') // Remove pragma
47- .replace(/export\\s+const\\s+metadata\\s*=/g, 'const metadata =')
48- .replace(/export\\s+(async\\s+)?function\\s+execute/g, '$1function execute')
49- .replace(/export\\s+function\\s+shouldRegister/g, 'function shouldRegister');
50-
51- // Use Function constructor instead of eval for better security
52- const moduleFunc = new Function('exports', transformedCode + '; return { metadata, execute, shouldRegister: typeof shouldRegister !== "undefined" ? shouldRegister : undefined };');
53- const moduleExports = moduleFunc({});
54-
55- // Check if tool should be registered (optional export)
56- if (moduleExports.shouldRegister) {
65+ ${ transformedCode }
66+
67+ console.log('[WebMCP] User script executed, checking exports:', {
68+ hasMetadata: typeof metadata !== 'undefined',
69+ hasExecute: typeof execute !== 'undefined',
70+ hasShouldRegister: typeof shouldRegister !== 'undefined'
71+ });
72+
73+ if (typeof shouldRegister === 'function') {
5774 try {
58- const shouldReg = moduleExports.shouldRegister();
59- if (!shouldReg) {
75+ if (!shouldRegister()) {
6076 console.log('[WebMCP] Tool ${ scriptName } skipped registration (shouldRegister returned false)');
6177 return;
6278 }
6379 } catch (error) {
64- log .error('[WebMCP] Error in shouldRegister for ${ scriptName } :', error);
80+ console .error('[WebMCP] Error in shouldRegister for ${ scriptName } :', error);
6581 // Continue with registration if shouldRegister throws (fail-open)
6682 }
6783 }
68-
69- // Register tool with window.agent using namespace_name format
70- if (window.agent && moduleExports.metadata && moduleExports.execute) {
71- const toolName = moduleExports.metadata.namespace + '_' + moduleExports.metadata.name;
84+
85+ if (window.agent && typeof metadata !== 'undefined' && typeof execute !== 'undefined') {
7286 const tool = {
73- name: toolName,
74- description: moduleExports. metadata.description || '${ metadata . description || '' } ',
75- inputSchema: moduleExports. metadata.inputSchema,
76- execute: moduleExports. execute
87+ name: ' ${ toolName } ' ,
88+ description: metadata.description || '${ metadata . description || '' } ',
89+ inputSchema: metadata.inputSchema || { type: 'object', properties: {} } ,
90+ execute: execute
7791 };
78-
92+
7993 window.agent.registerTool(tool);
80- console.log('[WebMCP] Registered tool ' + toolName + ' v${ metadata . version } ');
94+ console.log('[WebMCP] Registered tool ${ toolName } v${ metadata . version } ');
8195 } else {
82- log .error('[WebMCP] Failed to register tool ${ scriptName } :', {
96+ console .error('[WebMCP] Failed to register ${ scriptName } :', {
8397 hasAgent: !!window.agent,
84- hasMetadata: !!moduleExports. metadata,
85- hasExecute: !!moduleExports. execute
98+ hasMetadata: typeof metadata !== 'undefined' ,
99+ hasExecute: typeof execute !== 'undefined'
86100 });
87101 }
102+
88103 } catch (error) {
89- log .error('[WebMCP] Error executing script ${ scriptName } :', error);
104+ console .error('[WebMCP] Error executing script ${ scriptName } :', error);
90105 }
91106})();
92107//# sourceURL=webmcp-script:${ scriptName } .js` ;
@@ -156,13 +171,53 @@ async function injectSingleScript(
156171 // Always inject at document_idle for consistent behavior
157172 const injectImmediately = false ;
158173
159- // Create an injection function that Chrome can serialize
160- // We'll pass the wrapped code as an argument to avoid string replacement issues
161174 const injectionFunc = ( codeToInject : string ) => {
162- const script = document . createElement ( 'script' ) ;
163- script . textContent = codeToInject ;
164- ( document . head || document . documentElement ) . appendChild ( script ) ;
165- script . remove ( ) ;
175+ console . warn ( '[WebMCP] Creating blob URL for user script injection' ) ;
176+
177+ try {
178+ // Create a Blob with the script code
179+ const blob = new Blob ( [ codeToInject ] , { type : 'application/javascript' } ) ;
180+ const blobUrl = URL . createObjectURL ( blob ) ;
181+
182+ console . warn ( '[WebMCP] Blob URL created:' , blobUrl ) ;
183+
184+ // Load script from blob: URL (external source, not inline)
185+ const script = document . createElement ( 'script' ) ;
186+
187+ // Try to set src - may need Trusted Types policy on strict sites
188+ try {
189+ // Use TT policy if available (created by webmcp-polyfill.js)
190+ if ( window . __agentboardTTPolicy ) {
191+ console . warn ( '[WebMCP] Using Trusted Types policy for user script' ) ;
192+ script . src = window . __agentboardTTPolicy . createScriptURL ( blobUrl ) ;
193+ } else {
194+ script . src = blobUrl ;
195+ }
196+ } catch ( trustedTypesError ) {
197+ console . error ( '[WebMCP] ❌ Trusted Types blocked user script injection' ) ;
198+ console . error ( '[WebMCP] This site requires TrustedScriptURL but policy creation failed' ) ;
199+ console . error ( '[WebMCP] Possible reasons:' ) ;
200+ console . error ( '[WebMCP] 1. CSP restricts policy names (trusted-types directive)' ) ;
201+ console . error ( '[WebMCP] 2. Site blocks all dynamic policy creation' ) ;
202+ console . error ( '[WebMCP] Technical details:' , trustedTypesError ) ;
203+
204+ URL . revokeObjectURL ( blobUrl ) ;
205+ return ;
206+ }
207+
208+ script . onload = ( ) => {
209+ console . warn ( '[WebMCP] ✅ User script loaded successfully via blob URL' ) ;
210+ URL . revokeObjectURL ( blobUrl ) ;
211+ } ;
212+ script . onerror = ( e ) => {
213+ console . error ( '[WebMCP] ❌ Failed to load script from blob URL:' , e ) ;
214+ URL . revokeObjectURL ( blobUrl ) ;
215+ } ;
216+
217+ ( document . head || document . documentElement ) . appendChild ( script ) ;
218+ } catch ( error ) {
219+ console . error ( '[WebMCP] ❌ Unexpected error during blob injection:' , error ) ;
220+ }
166221 } ;
167222
168223 // Inject the script
0 commit comments