From 155f216a7cb284dcfce842360c220606f879a09a Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 02:49:37 +0000 Subject: [PATCH] feat(process): add stdin close task --- actions.go | 10 ++++++++++ service.go | 15 +++++++++++++++ service_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/actions.go b/actions.go index 88a9095..dbd26c7 100644 --- a/actions.go +++ b/actions.go @@ -94,6 +94,16 @@ type TaskProcessInput struct { Input string } +// TaskProcessCloseStdin closes the stdin pipe of a managed process through Core.PERFORM. +// +// Example: +// +// c.PERFORM(process.TaskProcessCloseStdin{ID: "proc-1"}) +type TaskProcessCloseStdin struct { + // ID identifies a managed process started by this service. + ID string +} + // TaskProcessList requests a snapshot of managed processes through Core.PERFORM. // If RunningOnly is true, only active processes are returned. // diff --git a/service.go b/service.go index 5d0a465..a96b456 100644 --- a/service.go +++ b/service.go @@ -638,6 +638,21 @@ func (s *Service) handleTask(c *core.Core, task core.Task) core.Result { return core.Result{Value: err, OK: false} } + return core.Result{OK: true} + case TaskProcessCloseStdin: + if m.ID == "" { + return core.Result{Value: coreerr.E("Service.handleTask", "task process close stdin requires an id", nil), OK: false} + } + + proc, err := s.Get(m.ID) + if err != nil { + return core.Result{Value: err, OK: false} + } + + if err := proc.CloseStdin(); err != nil { + return core.Result{Value: err, OK: false} + } + return core.Result{OK: true} case TaskProcessList: procs := s.List() diff --git a/service_test.go b/service_test.go index 7e17775..58ac612 100644 --- a/service_test.go +++ b/service_test.go @@ -776,6 +776,33 @@ func TestService_OnStartup(t *testing.T) { assert.Contains(t, proc.Output(), "typed-through-core") }) + + t.Run("registers process.close_stdin task", func(t *testing.T) { + svc, c := newTestService(t) + + err := svc.OnStartup(context.Background()) + require.NoError(t, err) + + proc, err := svc.Start(context.Background(), "cat") + require.NoError(t, err) + + result := c.PERFORM(TaskProcessInput{ + ID: proc.ID, + Input: "close-through-core\n", + }) + require.True(t, result.OK) + + result = c.PERFORM(TaskProcessCloseStdin{ID: proc.ID}) + require.True(t, result.OK) + + select { + case <-proc.Done(): + case <-time.After(2 * time.Second): + t.Fatal("process should have exited after stdin was closed") + } + + assert.Contains(t, proc.Output(), "close-through-core") + }) } func TestService_RunWithOptions(t *testing.T) {