feat: add logs propagation from server to client
diff --git a/go.mod b/go.mod
index 6babf99..fd25ffc 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,6 @@
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	golang.org/x/sys v0.25.0 // indirect
+	golang.org/x/sys v0.26.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 83fa656..43a9a28 100644
--- a/go.sum
+++ b/go.sum
@@ -21,8 +21,8 @@
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/openwhisk/forward_proxy.go b/openwhisk/forward_proxy.go
index 1b6b991..08927e3 100644
--- a/openwhisk/forward_proxy.go
+++ b/openwhisk/forward_proxy.go
@@ -74,6 +74,31 @@
 		sendError(w, http.StatusBadGateway, "Error forwarding run request. Check logs for details.")
 	}
 
+	proxy.ModifyResponse = func(response *http.Response) error {
+		if response.StatusCode == http.StatusOK {
+			// Decode the response
+			var remoteReponse RemoteRunResponse
+			err := json.NewDecoder(response.Body).Decode(&remoteReponse)
+			if err != nil {
+				Debug("Error decoding remote response: %v", err)
+				return err
+			}
+
+			// Write the logs to the client logs
+			if _, err := ap.outFile.WriteString(remoteReponse.Out); err != nil {
+				Debug("Error writing remote response out to client: %v", err)
+			}
+			if _, err := ap.errFile.WriteString(remoteReponse.Err); err != nil {
+				Debug("Error writing remote response err to client: %v", err)
+			}
+
+			// Keep the response body only
+			response.Body = io.NopCloser(bytes.NewReader(remoteReponse.Response))
+		}
+
+		return nil
+	}
+
 	Debug("Forwarding run request with id %s to %s", newBody.ProxiedActionID, ap.clientProxyData.ProxyURL.String())
 	proxy.ServeHTTP(w, r)
 	if f, ok := w.(http.Flusher); ok {
diff --git a/openwhisk/forward_proxy_test.go b/openwhisk/forward_proxy_test.go
index 45e98c3..42a3a60 100644
--- a/openwhisk/forward_proxy_test.go
+++ b/openwhisk/forward_proxy_test.go
@@ -65,13 +65,13 @@
 }
 
 func Example_forwardRunRequest() {
+	clientLog, _ := os.CreateTemp("", "log")
 	// create a client ActionProxy
-	clientAP := NewActionProxy("", "", nil, nil, ProxyModeClient)
+	clientAP := NewActionProxy("", "", clientLog, clientLog, ProxyModeClient)
 
 	// create a server ActionProxy
 	compiler, _ := filepath.Abs("common/gobuild.py")
-	log, _ := os.CreateTemp("", "log")
-	serverAP := NewActionProxy("./action", compiler, log, log, ProxyModeServer)
+	serverAP := NewActionProxy("./action", compiler, nil, nil, ProxyModeServer)
 
 	// start the server
 	ts := httptest.NewServer(serverAP)
@@ -95,8 +95,9 @@
 	// wait 2 seconds before declaring a test done
 	time.Sleep(2 * time.Second)
 	ts.Close()
-	dump(log)
+	dump(clientLog)
 	fmt.Println(runW.Body.String())
+	os.Remove(clientLog.Name())
 
 	// Output:
 	// {"ok":true}
diff --git a/openwhisk/initHandler.go b/openwhisk/initHandler.go
index c937b47..0ad8cc7 100644
--- a/openwhisk/initHandler.go
+++ b/openwhisk/initHandler.go
@@ -70,7 +70,15 @@
 			ap.serverProxyData = &ServerProxyData{actions: make(map[string]*ActionProxy)}
 		}
 
-		innerActionProxy := NewActionProxy(ap.baseDir, ap.compiler, ap.outFile, ap.errFile, ProxyModeNone)
+		outLog, err := os.CreateTemp("", "out-log")
+		if err != nil {
+			outLog = ap.outFile
+		}
+		errLog, err := os.CreateTemp("", "err-log")
+		if err != nil {
+			errLog = ap.errFile
+		}
+		innerActionProxy := NewActionProxy(ap.baseDir, ap.compiler, outLog, errLog, ProxyModeNone)
 		id, err := innerActionProxy.doInit(r, w)
 		if err != nil {
 			return
diff --git a/openwhisk/runHandler.go b/openwhisk/runHandler.go
index a32b58d..621af69 100644
--- a/openwhisk/runHandler.go
+++ b/openwhisk/runHandler.go
@@ -23,6 +23,7 @@
 	"fmt"
 	"io"
 	"net/http"
+	"os"
 )
 
 type runRequest struct {
@@ -35,6 +36,12 @@
 	Error string `json:"error"`
 }
 
+type RemoteRunResponse struct {
+	Response json.RawMessage `json:"response"`
+	Out      string          `json:"out"`
+	Err      string          `json:"err"`
+}
+
 func sendError(w http.ResponseWriter, code int, cause string) {
 	errResponse := ErrResponse{Error: cause}
 	b, err := json.Marshal(errResponse)
@@ -71,20 +78,21 @@
 		}
 
 		actionID := runRequest.ProxiedActionID
-
 		innerActionProxy, ok := ap.serverProxyData.actions[actionID]
 		if !ok {
 			Debug("Action %s not found in server proxy data", actionID)
 			sendError(w, http.StatusNotFound, "Action not found in remote runtime. Check logs for details.")
 		}
 
-		innerActionProxy.doProxiedRun(w, &runRequest)
+		innerActionProxy.doServerModeRun(w, &runRequest)
+
 		return
 	}
 
 	ap.doRun(w, r)
 }
-func (ap *ActionProxy) doProxiedRun(w http.ResponseWriter, bodyRequest *runRequest) {
+
+func (ap *ActionProxy) doServerModeRun(w http.ResponseWriter, bodyRequest *runRequest) {
 	var bodyBuf bytes.Buffer
 	err := json.NewEncoder(&bodyBuf).Encode(bodyRequest)
 	if err != nil {
@@ -93,7 +101,87 @@
 	}
 	body := bytes.Replace(bodyBuf.Bytes(), []byte("\n"), []byte(""), -1)
 
-	ap.executeAction(w, body)
+	// check if you have an action
+	if ap.theExecutor == nil {
+		sendError(w, http.StatusInternalServerError, "no action defined yet")
+		return
+	}
+
+	// check if the process exited
+	if ap.theExecutor.Exited() {
+		sendError(w, http.StatusInternalServerError, "command exited")
+		return
+	}
+
+	// execute the action
+	response, err := ap.theExecutor.Interact(body)
+
+	// check for early termination
+	if err != nil {
+		Debug("WARNING! Command exited")
+		ap.theExecutor = nil
+		sendError(w, http.StatusBadRequest, "command exited")
+		return
+	}
+	DebugLimit("received:", response, 120)
+
+	// check if the answer is an object map or array
+	var objmap map[string]*json.RawMessage
+	var objarray []interface{}
+	err = json.Unmarshal(response, &objmap)
+	if err != nil {
+		err = json.Unmarshal(response, &objarray)
+		if err != nil {
+			sendError(w, http.StatusBadGateway, "The action did not return a dictionary or array.")
+			return
+		}
+	}
+
+	// Get the stdout and stderr from the executor
+	outStr, err := os.ReadFile(ap.outFile.Name())
+	if err != nil {
+		outStr = []byte(fmt.Sprintf("Error reading stdout: %v", err))
+	}
+
+	errStr, err := os.ReadFile(ap.errFile.Name())
+	if err != nil {
+		errStr = []byte(fmt.Sprintf("Error reading stderr: %v", err))
+	}
+
+	// create the response
+	remoteResponse := RemoteRunResponse{
+		Response: response,
+		Out:      string(outStr),
+		Err:      string(errStr),
+	}
+
+	// turn response struct into json
+	responsePayload, err := json.Marshal(remoteResponse)
+	if err != nil {
+		sendError(w, http.StatusInternalServerError, fmt.Sprintf("Error marshalling response: %v", err))
+		return
+	}
+
+	// write response
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(responsePayload)))
+	numBytesWritten, err := w.Write(responsePayload)
+
+	// flush output if possible
+	if f, ok := w.(http.Flusher); ok {
+		f.Flush()
+	}
+
+	// handle writing errors
+	if err != nil {
+		sendError(w, http.StatusInternalServerError, fmt.Sprintf("Error writing response: %v", err))
+		return
+	}
+	if numBytesWritten != len(response) {
+		sendError(w, http.StatusInternalServerError, fmt.Sprintf("Only wrote %d of %d bytes to response", numBytesWritten, len(response)))
+		return
+	}
+
 }
 
 func (ap *ActionProxy) doRun(w http.ResponseWriter, r *http.Request) {
@@ -107,10 +195,6 @@
 
 	body = bytes.Replace(body, []byte("\n"), []byte(""), -1)
 
-	ap.executeAction(w, body)
-}
-
-func (ap *ActionProxy) executeAction(w http.ResponseWriter, body []byte) {
 	// check if you have an action
 	if ap.theExecutor == nil {
 		sendError(w, http.StatusInternalServerError, "no action defined yet")
diff --git a/openwhisk/version.go b/openwhisk/version.go
index 81e6f1c..191b162 100644
--- a/openwhisk/version.go
+++ b/openwhisk/version.go
@@ -17,4 +17,4 @@
 package openwhisk
 
 // Version number - internal
-var Version = "1.18.1"
+var Version = "1.18.2"