@@ -8,6 +8,7 @@ type FakeFsEntry = { kind: "file"; content: string } | { kind: "dir" };
88const state = vi . hoisted ( ( ) => ( {
99 entries : new Map < string , FakeFsEntry > ( ) ,
1010 realpaths : new Map < string , string > ( ) ,
11+ runCommandWithTimeout : vi . fn ( ) ,
1112} ) ) ;
1213
1314const abs = ( p : string ) => path . resolve ( p ) ;
@@ -69,7 +70,11 @@ vi.mock("./openclaw-root.js", () => ({
6970 resolveOpenClawPackageRoot : vi . fn ( async ( ) => null ) ,
7071 resolveOpenClawPackageRootSync : vi . fn ( ( ) => null ) ,
7172} ) ) ;
73+ vi . mock ( "../process/exec.js" , ( ) => ( {
74+ runCommandWithTimeout : state . runCommandWithTimeout ,
75+ } ) ) ;
7276
77+ let ensureControlUiAssetsBuilt : typeof import ( "./control-ui-assets.js" ) . ensureControlUiAssetsBuilt ;
7378let resolveControlUiRepoRoot : typeof import ( "./control-ui-assets.js" ) . resolveControlUiRepoRoot ;
7479let resolveControlUiDistIndexPath : typeof import ( "./control-ui-assets.js" ) . resolveControlUiDistIndexPath ;
7580let resolveControlUiDistIndexHealth : typeof import ( "./control-ui-assets.js" ) . resolveControlUiDistIndexHealth ;
@@ -81,6 +86,7 @@ let openclawRoot: typeof import("./openclaw-root.js");
8186describe ( "control UI assets helpers (fs-mocked)" , ( ) => {
8287 beforeAll ( async ( ) => {
8388 ( {
89+ ensureControlUiAssetsBuilt,
8490 resolveControlUiRepoRoot,
8591 resolveControlUiDistIndexPath,
8692 resolveControlUiDistIndexHealth,
@@ -94,6 +100,7 @@ describe("control UI assets helpers (fs-mocked)", () => {
94100 beforeEach ( ( ) => {
95101 state . entries . clear ( ) ;
96102 state . realpaths . clear ( ) ;
103+ state . runCommandWithTimeout . mockReset ( ) ;
97104 vi . clearAllMocks ( ) ;
98105 } ) ;
99106
@@ -179,6 +186,39 @@ describe("control UI assets helpers (fs-mocked)", () => {
179186 } ) ;
180187 } ) ;
181188
189+ it ( "keeps a truncated build failure diagnostic within its UTF-16 limit" , async ( ) => {
190+ const root = abs ( "fixtures/build-failure" ) ;
191+ const argv1 = path . join ( root , "src" , "index.ts" ) ;
192+ const originalArgv1 = process . argv [ 1 ] ;
193+ setFile ( path . join ( root , "ui" , "vite.config.ts" ) , "export {};\n" ) ;
194+ setFile ( path . join ( root , "scripts" , "ui.js" ) , "" ) ;
195+ state . runCommandWithTimeout . mockResolvedValueOnce ( {
196+ stdout : "" ,
197+ stderr : `${ "y" . repeat ( 238 ) } 🚀xx` ,
198+ code : 1 ,
199+ signal : null ,
200+ killed : false ,
201+ termination : "exit" ,
202+ } ) ;
203+ process . argv [ 1 ] = argv1 ;
204+
205+ try {
206+ const result = await ensureControlUiAssetsBuilt ( {
207+ log : vi . fn ( ) ,
208+ error : vi . fn ( ) ,
209+ exit : vi . fn ( ) ,
210+ } ) ;
211+
212+ expect ( result ) . toEqual ( {
213+ ok : false ,
214+ built : false ,
215+ message : `Control UI build failed: ${ "y" . repeat ( 238 ) } …` ,
216+ } ) ;
217+ } finally {
218+ process . argv [ 1 ] = originalArgv1 ;
219+ }
220+ } ) ;
221+
182222 it ( "resolves control-ui root from override file or directory" , ( ) => {
183223 const root = abs ( "fixtures/override" ) ;
184224 const uiDir = path . join ( root , "dist" , "control-ui" ) ;
0 commit comments