@@ -19,6 +19,37 @@ function sanitizeImageRef(ref) {
1919 return trimmed || null ;
2020}
2121
22+ function isSuccessfulCommandResult ( result ) {
23+ return result ?. success && ( result . code === 0 || result . code === null || result . code === undefined ) ;
24+ }
25+
26+ function dockerCommandError ( result , fallback ) {
27+ return ( result ?. stderr || result ?. error || "" ) . trim ( ) || fallback ;
28+ }
29+
30+ function isDockerSocketPermissionError ( result ) {
31+ const text = `${ result ?. stderr || "" } \n${ result ?. stdout || "" } \n${ result ?. error || "" } ` . toLowerCase ( ) ;
32+ if ( ! text . includes ( "permission denied" ) ) return false ;
33+ return text . includes ( "docker daemon" )
34+ || text . includes ( "docker.sock" )
35+ || text . includes ( "/var/run/docker.sock" )
36+ || text . includes ( "connect to the docker daemon" ) ;
37+ }
38+
39+ function getSessionSudoPassword ( session ) {
40+ return typeof session ?. systemManagerSudoPassword === "string" && session . systemManagerSudoPassword . length > 0
41+ ? session . systemManagerSudoPassword
42+ : null ;
43+ }
44+
45+ function buildDockerCommand ( args ) {
46+ return `docker ${ args } ` . trim ( ) ;
47+ }
48+
49+ function buildSudoDockerCommand ( args ) {
50+ return `sudo -S -p '' ${ buildDockerCommand ( args ) } ` ;
51+ }
52+
2253function parseDockerContainers ( stdout ) {
2354 const containers = [ ] ;
2455 for ( const line of ( stdout || "" ) . split ( "\n" ) ) {
@@ -132,39 +163,49 @@ function summarizeContainerInspect(info) {
132163 } ;
133164}
134165
135- function createDockerOpsApi ( { execOnSession } ) {
166+ function createDockerOpsApi ( { execOnSession, getSession } ) {
136167 async function runDocker ( event , sessionId , args , timeoutMs = 15000 ) {
137- const cmd = `docker ${ args } ` ;
168+ const cmd = buildDockerCommand ( args ) ;
138169 const result = await execOnSession ( event , sessionId , cmd , timeoutMs ) ;
170+ if ( isSuccessfulCommandResult ( result ) ) return result ;
171+
172+ const sudoPassword = getSessionSudoPassword ( getSession ?. ( sessionId ) ) ;
173+
174+ if ( sudoPassword && isDockerSocketPermissionError ( result ) ) {
175+ const sudoResult = await execOnSession (
176+ event ,
177+ sessionId ,
178+ buildSudoDockerCommand ( args ) ,
179+ timeoutMs ,
180+ { stdin : `${ sudoPassword } \n` } ,
181+ ) ;
182+ if ( isSuccessfulCommandResult ( sudoResult ) ) return sudoResult ;
183+ return {
184+ success : false ,
185+ error : dockerCommandError ( sudoResult , `sudo docker exited with code ${ sudoResult ?. code } ` ) ,
186+ stderr : sudoResult ?. stderr ,
187+ } ;
188+ }
189+
139190 if ( ! result . success ) return result ;
140191 if ( result . code !== 0 && result . code !== null && result . code !== undefined ) {
141192 return {
142193 success : false ,
143- error : ( result . stderr || "" ) . trim ( ) || `docker exited with code ${ result . code } ` ,
194+ error : dockerCommandError ( result , `docker exited with code ${ result . code } ` ) ,
144195 stderr : result . stderr ,
145196 } ;
146197 }
147198 return result ;
148199 }
149200
150201 async function listContainers ( event , sessionId ) {
151- const result = await execOnSession (
152- event ,
153- sessionId ,
154- "docker ps -a --format '{{json .}}'" ,
155- 12000 ,
156- ) ;
202+ const result = await runDocker ( event , sessionId , "ps -a --format '{{json .}}'" , 12000 ) ;
157203 if ( ! result . success ) return { success : false , error : result . error } ;
158204 return { success : true , containers : parseDockerContainers ( result . stdout ) } ;
159205 }
160206
161207 async function listImages ( event , sessionId ) {
162- const result = await execOnSession (
163- event ,
164- sessionId ,
165- "docker images --format '{{json .}}'" ,
166- 12000 ,
167- ) ;
208+ const result = await runDocker ( event , sessionId , "images --format '{{json .}}'" , 12000 ) ;
168209 if ( ! result . success ) return { success : false , error : result . error } ;
169210 return { success : true , images : parseDockerImages ( result . stdout ) } ;
170211 }
@@ -174,10 +215,10 @@ function createDockerOpsApi({ execOnSession }) {
174215 if ( ! sessionId ) return { success : false , error : "Missing sessionId" } ;
175216 const ids = Array . isArray ( payload ?. ids ) ? payload . ids . filter ( Boolean ) : [ ] ;
176217 const idArg = ids . map ( ( id ) => sanitizeDockerId ( id ) ) . filter ( Boolean ) . join ( " " ) ;
177- const result = await execOnSession (
218+ const result = await runDocker (
178219 event ,
179220 sessionId ,
180- `docker stats --no-stream --format '{{json .}}' ${ idArg } ` . trim ( ) ,
221+ `stats --no-stream --format '{{json .}}' ${ idArg } ` . trim ( ) ,
181222 15000 ,
182223 ) ;
183224 if ( ! result . success ) return { success : false , error : result . error } ;
@@ -188,7 +229,7 @@ function createDockerOpsApi({ execOnSession }) {
188229 const { sessionId, containerId } = payload || { } ;
189230 if ( ! sessionId || ! containerId ) return { success : false , error : "Missing params" } ;
190231 const safeId = sanitizeDockerId ( containerId ) ;
191- const result = await execOnSession ( event , sessionId , `docker inspect ${ safeId } ` , 10000 ) ;
232+ const result = await runDocker ( event , sessionId , `inspect ${ safeId } ` , 10000 ) ;
192233 if ( ! result . success ) return { success : false , error : result . error } ;
193234 try {
194235 const parsed = JSON . parse ( result . stdout || "[]" ) ;
@@ -203,7 +244,7 @@ function createDockerOpsApi({ execOnSession }) {
203244 const { sessionId, imageId } = payload || { } ;
204245 if ( ! sessionId || ! imageId ) return { success : false , error : "Missing params" } ;
205246 const safeId = sanitizeDockerId ( imageId ) ;
206- const result = await execOnSession ( event , sessionId , `docker image inspect ${ safeId } ` , 10000 ) ;
247+ const result = await runDocker ( event , sessionId , `image inspect ${ safeId } ` , 10000 ) ;
207248 if ( ! result . success ) return { success : false , error : result . error } ;
208249 try {
209250 const parsed = JSON . parse ( result . stdout || "[]" ) ;
0 commit comments