11import { WebContainer , type WebContainerProcess } from '@webcontainer/api' ;
22import type { TerminalManager } from './terminal.js' ;
3- import { buildWorkspaceFiles , buildContainerPackageJson , GIT_STUB_JS } from './workspace.js' ;
3+ import { buildWorkspaceFiles , buildContainerPackageJson , buildStubLoaderRegister , buildStubLoaderHooks , GIT_STUB_JS , OPENCLAW_START_SCRIPT , OPENCLAW_SETUP_SCRIPT } from './workspace.js' ;
44import { AuditLog , type AuditSource } from './audit.js' ;
55import { NETWORK_HOOK_CJS } from './network-hook.js' ;
66import { PolicyEngine , PolicyDeniedError , type PolicyAction } from './policy.js' ;
@@ -148,7 +148,13 @@ export class ContainerManager {
148148 }
149149
150150 /** Boot the WebContainer and mount all workspace files. */
151- async boot ( opts ?: { workspace ?: Record < string , string > ; services ?: Record < string , string > } ) : Promise < void > {
151+ async boot ( opts ?: {
152+ workspace ?: Record < string , string > ;
153+ services ?: Record < string , string > ;
154+ agentPackage ?: string ;
155+ agentVersion ?: string ;
156+ agentOverrides ?: Record < string , string > ;
157+ } ) : Promise < void > {
152158 this . setStatus ( 'booting' ) ;
153159 this . wc = await WebContainer . boot ( ) ;
154160
@@ -165,12 +171,28 @@ export class ContainerManager {
165171 for ( const fn of this . serverListeners ) fn ( port , url ) ;
166172 } ) ;
167173
168- await this . wc . mount ( {
169- 'package.json' : { file : { contents : buildContainerPackageJson ( opts ?. services ) } } ,
174+ const mountTree : Record < string , any > = {
175+ 'package.json' : { file : { contents : buildContainerPackageJson ( {
176+ agentPackage : opts ?. agentPackage ,
177+ agentVersion : opts ?. agentVersion ,
178+ extraDeps : opts ?. services ,
179+ extraOverrides : opts ?. agentOverrides ,
180+ } ) } } ,
170181 'git-stub.js' : { file : { contents : GIT_STUB_JS } } ,
171182 'network-hook.cjs' : { file : { contents : NETWORK_HOOK_CJS } } ,
172183 workspace : { directory : buildWorkspaceFiles ( opts ?. workspace ) } ,
173- } ) ;
184+ } ;
185+
186+ // Mount stub loader hooks when native deps are stubbed via overrides
187+ if ( opts ?. agentOverrides && Object . keys ( opts . agentOverrides ) . length > 0 ) {
188+ const pkgs = Object . keys ( opts . agentOverrides ) ;
189+ mountTree [ 'stub-loader.mjs' ] = { file : { contents : buildStubLoaderRegister ( pkgs ) } } ;
190+ mountTree [ 'stub-loader-hooks.mjs' ] = { file : { contents : buildStubLoaderHooks ( pkgs ) } } ;
191+ mountTree [ 'openclaw-start.mjs' ] = { file : { contents : OPENCLAW_START_SCRIPT } } ;
192+ mountTree [ 'openclaw-setup.cjs' ] = { file : { contents : OPENCLAW_SETUP_SCRIPT } } ;
193+ }
194+
195+ await this . wc . mount ( mountTree ) ;
174196
175197 this . audit ?. log ( 'boot.mount' , 'mounted workspace files' , {
176198 files : [ 'package.json' , 'git-stub.js' , 'network-hook.cjs' , 'workspace/' ] ,
@@ -627,14 +649,19 @@ export class ContainerManager {
627649 this . audit ?. log ( 'process.spawn' , spawnCmd , undefined , { source : 'agent' } ) ;
628650 this . activeProcessCount ++ ;
629651
652+ const nodeOptions = [ `--require ${ homeDir } /network-hook.cjs` ] ;
653+ if ( config . overrides && Object . keys ( config . overrides ) . length > 0 ) {
654+ nodeOptions . push ( `--import ${ homeDir } /stub-loader.mjs` ) ;
655+ }
656+
630657 this . shellProcess = await this . wc . spawn ( 'node' , [ entry , ...args ] , {
631658 terminal : { cols, rows } ,
632659 env : {
633660 ...this . apiEnvVars ,
634661 ...config . env ,
635662 PATH : `${ homeDir } /node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` ,
636663 HOME : homeDir ,
637- NODE_OPTIONS : `--require ${ homeDir } /network-hook.cjs` ,
664+ NODE_OPTIONS : nodeOptions . join ( ' ' ) ,
638665 } ,
639666 } ) ;
640667
0 commit comments