feat(process): add async process start task

This commit is contained in:
Virgil 2026-04-04 02:42:45 +00:00
parent 6c1d53a237
commit ceea10fc7a
3 changed files with 66 additions and 0 deletions

View file

@ -4,6 +4,29 @@ import "time"
// --- ACTION messages (broadcast via Core.ACTION) ---
// TaskProcessStart requests asynchronous process execution through Core.PERFORM.
// The handler returns a snapshot of the started process immediately.
//
// Example:
//
// c.PERFORM(process.TaskProcessStart{Command: "sleep", Args: []string{"10"}})
type TaskProcessStart struct {
Command string
Args []string
Dir string
Env []string
// DisableCapture skips buffering process output before returning it.
DisableCapture bool
// Detach runs the command in its own process group.
Detach bool
// Timeout bounds the execution duration.
Timeout time.Duration
// GracePeriod controls SIGTERM-to-SIGKILL escalation.
GracePeriod time.Duration
// KillGroup terminates the entire process group instead of only the leader.
KillGroup bool
}
// TaskProcessRun requests synchronous command execution through Core.PERFORM.
// The handler returns the combined command output on success.
//

View file

@ -555,6 +555,22 @@ func (s *Service) RunWithOptions(ctx context.Context, opts RunOptions) (string,
// handleTask dispatches Core.PERFORM messages for the process service.
func (s *Service) handleTask(c *core.Core, task core.Task) core.Result {
switch m := task.(type) {
case TaskProcessStart:
proc, err := s.StartWithOptions(c.Context(), RunOptions{
Command: m.Command,
Args: m.Args,
Dir: m.Dir,
Env: m.Env,
DisableCapture: m.DisableCapture,
Detach: m.Detach,
Timeout: m.Timeout,
GracePeriod: m.GracePeriod,
KillGroup: m.KillGroup,
})
if err != nil {
return core.Result{Value: err, OK: false}
}
return core.Result{Value: proc.Info(), OK: true}
case TaskProcessRun:
output, err := s.RunWithOptions(c.Context(), RunOptions{
Command: m.Command,

View file

@ -596,6 +596,33 @@ func TestService_OnShutdown(t *testing.T) {
}
func TestService_OnStartup(t *testing.T) {
t.Run("registers process.start task", func(t *testing.T) {
svc, c := newTestService(t)
err := svc.OnStartup(context.Background())
require.NoError(t, err)
result := c.PERFORM(TaskProcessStart{
Command: "sleep",
Args: []string{"1"},
})
require.True(t, result.OK)
info, ok := result.Value.(Info)
require.True(t, ok)
assert.NotEmpty(t, info.ID)
assert.Equal(t, StatusRunning, info.Status)
assert.True(t, info.Running)
proc, err := svc.Get(info.ID)
require.NoError(t, err)
assert.True(t, proc.IsRunning())
<-proc.Done()
assert.Equal(t, StatusExited, proc.Status)
})
t.Run("registers process.run task", func(t *testing.T) {
svc, c := newTestService(t)