@@ -49,7 +49,10 @@ function requireValue<T>(value: T | null | undefined, message: string): T {
4949 }
5050 return value ;
5151}
52- const { stageOptimizedSandboxBuildContext } = require ( "./sandbox-build-context" ) ;
52+ const {
53+ collectBuildContextStats,
54+ stageOptimizedSandboxBuildContext,
55+ } = require ( "./sandbox-build-context" ) ;
5356const { buildSubprocessEnv } = require ( "./subprocess-env" ) ;
5457const {
5558 DASHBOARD_PORT ,
@@ -75,6 +78,8 @@ const { DEFAULT_CLOUD_MODEL, getProviderSelectionConfig, parseGatewayInference }
7578
7679const onboardProviders = require ( "./onboard-providers" ) ;
7780
81+ const CUSTOM_BUILD_CONTEXT_WARN_BYTES = 100 * 1024 * 1024 ;
82+
7883type RemoteProviderConfigEntry = {
7984 label : string ;
8085 providerName : string ;
@@ -112,8 +117,16 @@ const {
112117 getEffectiveProviderName : ( key : string | null | undefined ) => string | null ;
113118 getNonInteractiveProvider : ( ) => string | null ;
114119 getNonInteractiveModel : ( providerKey : string ) => string | null ;
115- getSandboxInferenceConfig : ( model : string , provider ?: string | null , preferredInferenceApi ?: string | null ) => {
116- providerKey : string ; primaryModelRef : string ; inferenceBaseUrl : string ; inferenceApi : string ; inferenceCompat : LooseObject | null ;
120+ getSandboxInferenceConfig : (
121+ model : string ,
122+ provider ?: string | null ,
123+ preferredInferenceApi ?: string | null ,
124+ ) => {
125+ providerKey : string ;
126+ primaryModelRef : string ;
127+ inferenceBaseUrl : string ;
128+ inferenceApi : string ;
129+ inferenceCompat : LooseObject | null ;
117130 } ;
118131} ;
119132const { sleepSeconds } = require ( "./wait" ) ;
@@ -253,7 +266,11 @@ const BRAVE_SEARCH_HELP_URL = "https://brave.com/search/api/";
253266
254267// Re-export shared JSON types under the names used throughout this module.
255268// See src/lib/json-types.ts for the canonical definitions.
256- import type { JsonScalar as LooseScalar , JsonValue as LooseValue , JsonObject as LooseObject } from "./json-types" ;
269+ import type {
270+ JsonScalar as LooseScalar ,
271+ JsonValue as LooseValue ,
272+ JsonObject as LooseObject ,
273+ } from "./json-types" ;
257274
258275type OnboardOptions = {
259276 nonInteractive ?: boolean ;
@@ -837,7 +854,13 @@ async function promptValidationRecovery(
837854
838855// Provider CRUD — thin wrappers that inject runOpenshell to avoid circular deps.
839856const { buildProviderArgs } = onboardProviders ;
840- function upsertProvider ( name : string , type : string , credentialEnv : string , baseUrl : string | null , env : NodeJS . ProcessEnv = { } ) {
857+ function upsertProvider (
858+ name : string ,
859+ type : string ,
860+ credentialEnv : string ,
861+ baseUrl : string | null ,
862+ env : NodeJS . ProcessEnv = { } ,
863+ ) {
841864 return onboardProviders . upsertProvider ( name , type , credentialEnv , baseUrl , env , runOpenshell ) ;
842865}
843866
@@ -1460,7 +1483,6 @@ const {
14601483// nvcfFunctionNotFoundMessage — see validation import above. They live in
14611484// src/lib/validation.ts so they can be unit-tested independently.
14621485
1463-
14641486async function validateOpenAiLikeSelection (
14651487 label : string ,
14661488 endpointUrl : string ,
@@ -1641,7 +1663,6 @@ function getRequestedProviderHint(nonInteractive = isNonInteractive()) {
16411663}
16421664function getRequestedModelHint ( nonInteractive = isNonInteractive ( ) ) {
16431665 return onboardProviders . getRequestedModelHint ( nonInteractive ) ;
1644-
16451666}
16461667
16471668function getResumeConfigConflicts (
@@ -2091,25 +2112,19 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
20912112 console . warn (
20922113 " ⚠ Container DNS probe inconclusive: docker couldn't pull the busybox test image." ,
20932114 ) ;
2094- console . warn (
2095- " This usually means the docker daemon itself can't reach Docker Hub," ,
2096- ) ;
2115+ console . warn ( " This usually means the docker daemon itself can't reach Docker Hub," ) ;
20972116 console . warn (
20982117 " but doesn't prove container DNS is broken — the sandbox build may still succeed." ,
20992118 ) ;
21002119 } else {
2101- console . warn (
2102- ` ⚠ Container DNS probe inconclusive (reason: ${ dns . reason ?? "unknown" } ).` ,
2103- ) ;
2120+ console . warn ( ` ⚠ Container DNS probe inconclusive (reason: ${ dns . reason ?? "unknown" } ).` ) ;
21042121 }
21052122 if ( dns . details ) {
21062123 for ( const line of String ( dns . details ) . split ( "\n" ) . slice ( - 3 ) ) {
21072124 if ( line . trim ( ) ) console . warn ( ` ${ line . trim ( ) } ` ) ;
21082125 }
21092126 }
2110- console . warn (
2111- " Proceeding. If the sandbox build later hangs at `npm ci`, see issue #2101." ,
2112- ) ;
2127+ console . warn ( " Proceeding. If the sandbox build later hangs at `npm ci`, see issue #2101." ) ;
21132128 } else {
21142129 console . error ( " ✗ DNS resolution from inside a docker container failed." ) ;
21152130 if ( dns . details ) {
@@ -2119,18 +2134,10 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
21192134 }
21202135 console . error ( "" ) ;
21212136 {
2122- console . error (
2123- " The sandbox build runs `npm ci` inside a container and needs to resolve" ,
2124- ) ;
2125- console . error (
2126- " registry.npmjs.org. On networks that block outbound UDP:53 to public DNS" ,
2127- ) ;
2128- console . error (
2129- " (common in corporate environments that force DNS-over-TLS on the host)," ,
2130- ) ;
2131- console . error (
2132- " the build appears to hang for ~15 minutes and then prints the cryptic" ,
2133- ) ;
2137+ console . error ( " The sandbox build runs `npm ci` inside a container and needs to resolve" ) ;
2138+ console . error ( " registry.npmjs.org. On networks that block outbound UDP:53 to public DNS" ) ;
2139+ console . error ( " (common in corporate environments that force DNS-over-TLS on the host)," ) ;
2140+ console . error ( " the build appears to hang for ~15 minutes and then prints the cryptic" ) ;
21342141 console . error ( " `npm error Exit handler never called`. See issue #2101." ) ;
21352142 console . error ( "" ) ;
21362143 console . error ( " Fix options:" ) ;
@@ -2179,9 +2186,7 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
21792186 console . error ( " 1. Make systemd-resolved reachable from containers (recommended):" ) ;
21802187 printLinuxFix ( bridgeIp , bridgeNote ) ;
21812188 console . error ( "" ) ;
2182- console . error (
2183- " 2. Configure an explicit UDP:53-capable DNS in /etc/docker/daemon.json" ,
2184- ) ;
2189+ console . error ( " 2. Configure an explicit UDP:53-capable DNS in /etc/docker/daemon.json" ) ;
21852190 console . error ( " (ask your IT team for an internal DNS server IP)." ) ;
21862191 } else if ( host . platform === "darwin" ) {
21872192 // On macOS, branch by the detected runtime (host.runtime) so users get
@@ -2190,9 +2195,7 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
21902195 console . error ( " Configure Colima's DNS (macOS):" ) ;
21912196 console . error ( " colima stop" ) ;
21922197 console . error ( " colima start --dns <corp-dns-ip>" ) ;
2193- console . error (
2194- " (or edit ~/.colima/default/colima.yaml and `colima restart`)" ,
2195- ) ;
2198+ console . error ( " (or edit ~/.colima/default/colima.yaml and `colima restart`)" ) ;
21962199 } else if ( host . runtime === "docker-desktop" || host . runtime === "docker" ) {
21972200 console . error ( " Configure Docker Desktop's DNS (macOS):" ) ;
21982201 console . error (
@@ -2210,21 +2213,17 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
22102213 console . error ( " Configure your container runtime's DNS (macOS):" ) ;
22112214 console . error ( " - Docker Desktop:" ) ;
22122215 console . error (
2213- " { jq '. + {\ "dns\ ":[\ "<corp-dns-ip>\ "]}' ~/.docker/daemon.json 2>/dev/null || echo '{\ "dns\ ":[\ "<corp-dns-ip>\ "]}'; } > ~/.docker/daemon.json.new && mv ~/.docker/daemon.json.new ~/.docker/daemon.json" ,
2216+ ' { jq \ '. + {"dns":["<corp-dns-ip>"]}\ ' ~/.docker/daemon.json 2>/dev/null || echo \'{ "dns":["<corp-dns-ip>"]}\ '; } > ~/.docker/daemon.json.new && mv ~/.docker/daemon.json.new ~/.docker/daemon.json' ,
22142217 ) ;
22152218 console . error ( " osascript -e 'quit app \"Docker\"' && sleep 3 && open -a Docker" ) ;
22162219 console . error ( " - Colima:" ) ;
22172220 console . error ( " colima stop && colima start --dns <corp-dns-ip>" ) ;
22182221 console . error ( " - Rancher Desktop / Podman: edit the runtime's DNS config" ) ;
22192222 console . error ( " and restart it." ) ;
22202223 }
2221- console . error (
2222- " Ask your IT team for an internal DNS server IP that accepts UDP:53." ,
2223- ) ;
2224+ console . error ( " Ask your IT team for an internal DNS server IP that accepts UDP:53." ) ;
22242225 } else if ( host . platform === "win32" || host . isWsl ) {
2225- console . error (
2226- " 1. Configure Docker Desktop's DNS (Windows / WSL via Docker Desktop):" ,
2227- ) ;
2226+ console . error ( " 1. Configure Docker Desktop's DNS (Windows / WSL via Docker Desktop):" ) ;
22282227 console . error (
22292228 " Docker Desktop for Windows → Settings → Docker Engine — edit the JSON to add:" ,
22302229 ) ;
@@ -2249,9 +2248,7 @@ async function preflight(): Promise<ReturnType<typeof nim.detectGpu>> {
22492248 }
22502249 printLinuxFix ( wslBridgeIp || "172.17.0.1" , wslBridgeNote ) ;
22512250 } else {
2252- console . error (
2253- " Configure your docker daemon to use a DNS server that accepts UDP:53." ,
2254- ) ;
2251+ console . error ( " Configure your docker daemon to use a DNS server that accepts UDP:53." ) ;
22552252 console . error (
22562253 ' Add { "dns": ["<corp-dns-ip>"] } to your docker daemon.json and restart the daemon.' ,
22572254 ) ;
@@ -3424,6 +3421,17 @@ async function createSandbox(
34243421 fs . copyFileSync ( fromResolved , stagedDockerfile ) ;
34253422 }
34263423 console . log ( ` Using custom Dockerfile: ${ fromResolved } ` ) ;
3424+ console . log ( ` Docker build context: ${ path . dirname ( fromResolved ) } ` ) ;
3425+ const buildContextStats = collectBuildContextStats ( buildCtx ) ;
3426+ if ( buildContextStats . totalBytes > CUSTOM_BUILD_CONTEXT_WARN_BYTES ) {
3427+ const sizeMb = Math . round ( buildContextStats . totalBytes / ( 1024 * 1024 ) ) ;
3428+ console . warn (
3429+ ` WARN: build context contains about ${ sizeMb } MB across ${ buildContextStats . fileCount } files.` ,
3430+ ) ;
3431+ console . warn (
3432+ " The --from flag sends the Dockerfile's parent directory to Docker; use a dedicated directory if this is not intentional." ,
3433+ ) ;
3434+ }
34273435 } else if ( agent ) {
34283436 const agentBuild = agentOnboard . createAgentSandbox ( agent ) ;
34293437 buildCtx = agentBuild . buildCtx ;
@@ -3757,7 +3765,14 @@ async function createSandbox(
37573765 const openshellBin = getOpenshellBinary ( ) ;
37583766 for ( let i = 0 ; i < 15 ; i ++ ) {
37593767 const readyMatch = runCaptureOpenshell (
3760- [ "sandbox" , "exec" , sandboxName , "curl" , "-sf" , `http://localhost:${ effectiveDashboardPort } /` ] ,
3768+ [
3769+ "sandbox" ,
3770+ "exec" ,
3771+ sandboxName ,
3772+ "curl" ,
3773+ "-sf" ,
3774+ `http://localhost:${ effectiveDashboardPort } /` ,
3775+ ] ,
37613776 { ignoreError : true } ,
37623777 ) ;
37633778 if ( readyMatch ) {
@@ -5044,7 +5059,9 @@ async function setupMessagingChannels(): Promise<string[]> {
50445059 console . log ( ` ${ ch . help } ` ) ;
50455060 const token = normalizeCredentialValue ( await prompt ( ` ${ ch . label } : ` , { secret : true } ) ) ;
50465061 if ( token && ch . tokenFormat && ! ch . tokenFormat . test ( token ) ) {
5047- console . log ( ` ✗ Invalid format. ${ ch . tokenFormatHint || "Check the token and try again." } ` ) ;
5062+ console . log (
5063+ ` ✗ Invalid format. ${ ch . tokenFormatHint || "Check the token and try again." } ` ,
5064+ ) ;
50485065 console . log ( ` Skipped ${ ch . name } (invalid token format)` ) ;
50495066 enabled . delete ( ch . name ) ;
50505067 continue ;
@@ -5898,7 +5915,9 @@ async function setupPoliciesWithSelection(
58985915 // the sandbox with no presets. Warn, optionally suggest the intended
58995916 // variable, and fall through to the tier-derived suggestions list.
59005917 console . warn ( ` Unsupported NEMOCLAW_POLICY_MODE: ${ policyMode } ` ) ;
5901- console . warn ( " Valid values: suggested, custom, skip (aliases: default/auto, list, none/no)." ) ;
5918+ console . warn (
5919+ " Valid values: suggested, custom, skip (aliases: default/auto, list, none/no)." ,
5920+ ) ;
59025921 if ( tiers . getTier ( policyMode ) ) {
59035922 console . warn (
59045923 ` '${ policyMode } ' is a policy tier — did you mean NEMOCLAW_POLICY_TIER=${ policyMode } ?` ,
@@ -6228,10 +6247,12 @@ function getWslHostAddress(
62286247 }
62296248 const runCaptureFn = options . runCapture || runCapture ;
62306249 const output = runCaptureFn ( [ "hostname" , "-I" ] , { ignoreError : true } ) ;
6231- return String ( output || "" )
6232- . trim ( )
6233- . split ( / \s + / )
6234- . filter ( Boolean ) [ 0 ] || null ;
6250+ return (
6251+ String ( output || "" )
6252+ . trim ( )
6253+ . split ( / \s + / )
6254+ . filter ( Boolean ) [ 0 ] || null
6255+ ) ;
62356256}
62366257
62376258function getDashboardAccessInfo (
@@ -6316,17 +6337,21 @@ function printDashboard(
63166337 const chain = buildChain ( { chatUiUrl, isWsl : isWsl ( ) , wslHostAddress : wslAddr } ) ;
63176338
63186339 // Build access info inline — uses chain instead of re-deriving from env
6319- const dashboardAccess = buildControlUiUrls ( token , chain . port , chain . accessUrl ) . map (
6320- ( url , i ) => ( { label : i === 0 ? "Dashboard" : `Alt ${ i } ` , url } ) ,
6321- ) ;
6340+ const dashboardAccess = buildControlUiUrls ( token , chain . port , chain . accessUrl ) . map ( ( url , i ) => ( {
6341+ label : i === 0 ? "Dashboard" : `Alt ${ i } ` ,
6342+ url,
6343+ } ) ) ;
63226344 if ( wslAddr ) {
63236345 const wslUrl = `http://${ wslAddr } :${ chain . port } /${ token ? `#token=${ encodeURIComponent ( token ) } ` : "" } ` ;
63246346 const existing = dashboardAccess . find ( ( a ) => a . url === wslUrl ) ;
63256347 if ( existing ) existing . label = "VS Code/WSL" ;
63266348 else dashboardAccess . push ( { label : "VS Code/WSL" , url : wslUrl } ) ;
63276349 }
63286350 const guidanceLines = [ `Port ${ chain . port } must be forwarded before opening these URLs.` ] ;
6329- if ( isWsl ( ) ) guidanceLines . push ( "WSL detected: if localhost fails in Windows, use the WSL host IP shown by `hostname -I`." ) ;
6351+ if ( isWsl ( ) )
6352+ guidanceLines . push (
6353+ "WSL detected: if localhost fails in Windows, use the WSL host IP shown by `hostname -I`." ,
6354+ ) ;
63306355 if ( dashboardAccess . length === 0 ) guidanceLines . push ( "No dashboard URLs were generated." ) ;
63316356
63326357 console . log ( "" ) ;
0 commit comments