You've already forked agentic-coding-workflow
启动项目逻辑
This commit is contained in:
@@ -313,6 +313,24 @@ type Workspace struct {
|
||||
ActiveMainSessionID pgtype.UUID `json:"active_main_session_id"`
|
||||
}
|
||||
|
||||
type WorkspaceRunProfile struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Command string `json:"command"`
|
||||
Args []string `json:"args"`
|
||||
EncryptedEnv []byte `json:"encrypted_env"`
|
||||
Enabled bool `json:"enabled"`
|
||||
LastStartedAt pgtype.Timestamptz `json:"last_started_at"`
|
||||
LastExitCode *int32 `json:"last_exit_code"`
|
||||
LastError string `json:"last_error"`
|
||||
CreatedBy pgtype.UUID `json:"created_by"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WorkspaceWorktree struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/yan1h/agent-coding-workflow/internal/audit"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/crypto"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/notify"
|
||||
procgrp "github.com/yan1h/agent-coding-workflow/internal/infra/proc"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/mcp"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/workspace"
|
||||
)
|
||||
@@ -95,6 +96,7 @@ type Process struct {
|
||||
WorktreeID *uuid.UUID // 子 worktree 时填,用于 release
|
||||
|
||||
Cmd *exec.Cmd
|
||||
group procgrp.Group // 进程树句柄(整树终止),Spawn 时 Prepare+Adopt
|
||||
Stdin io.WriteCloser
|
||||
Stdout io.ReadCloser
|
||||
Stderr io.ReadCloser
|
||||
@@ -173,6 +175,11 @@ func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind,
|
||||
cmd.Dir = sess.CwdPath
|
||||
cmd.Env = osEnv
|
||||
|
||||
// 进程树管理:Prepare 须在 Start 前(Unix Setpgid),Adopt 须在 Start 后
|
||||
// (Windows Job Object)。终止时整树清理,避免 agent 派生的子孙进程残留。
|
||||
group := procgrp.NewGroup()
|
||||
group.Prepare(cmd)
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stdin pipe: %w", err)
|
||||
@@ -189,6 +196,10 @@ func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind,
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("start: %w", err)
|
||||
}
|
||||
if err := group.Adopt(cmd); err != nil {
|
||||
_ = cmd.Process.Kill()
|
||||
return nil, fmt.Errorf("adopt process group: %w", err)
|
||||
}
|
||||
|
||||
var decider handlers.PermissionDecider
|
||||
if s.permSvc != nil {
|
||||
@@ -215,6 +226,7 @@ func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind,
|
||||
UserID: sess.UserID,
|
||||
IsMain: sess.IsMainWorktree,
|
||||
Cmd: cmd,
|
||||
group: group,
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
@@ -307,9 +319,9 @@ func (s *Supervisor) Kill(ctx context.Context, sid uuid.UUID, grace time.Duratio
|
||||
|
||||
proc.KilledByUs.Store(true)
|
||||
|
||||
// 平台差异:Unix 走 SIGTERM + grace + 强 Kill;Windows 直接 Kill。
|
||||
// 实现见 supervisor_unix.go / supervisor_windows.go。
|
||||
s.unixGracefulTerm(proc, grace)
|
||||
// 整树终止:Unix 走 SIGTERM(-pgid) + grace + SIGKILL(-pgid);Windows 关闭
|
||||
// Job 句柄触发 KILL_ON_JOB_CLOSE。实现下沉至 internal/infra/proc。
|
||||
proc.group.TerminateGroup(proc.done, grace)
|
||||
|
||||
select {
|
||||
case <-proc.done:
|
||||
@@ -375,6 +387,10 @@ func (s *Supervisor) monitorProcess(proc *Process) {
|
||||
delete(s.procs, proc.SessionID)
|
||||
s.mu.Unlock()
|
||||
|
||||
// 释放进程树句柄(Windows 关闭 Job handle;Unix no-op)。done 已 close、
|
||||
// 进程已 Wait 返回,此处 Close 不影响 onExit-must-not-relock 不变量。
|
||||
proc.group.Close()
|
||||
|
||||
status := SessionExited
|
||||
if !proc.KilledByUs.Load() {
|
||||
status = SessionCrashed
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package acp
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// unixGracefulTerm 在 Unix 平台上先发 SIGTERM,等待 grace 时间内退出;
|
||||
// 否则 Process.Kill。done channel 由 monitorProcess 关闭。
|
||||
func (s *Supervisor) unixGracefulTerm(proc *Process, grace time.Duration) {
|
||||
if proc.Cmd.Process == nil {
|
||||
return
|
||||
}
|
||||
_ = proc.Cmd.Process.Signal(syscall.SIGTERM)
|
||||
select {
|
||||
case <-proc.done:
|
||||
return
|
||||
case <-time.After(grace):
|
||||
_ = proc.Cmd.Process.Kill()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package acp
|
||||
|
||||
import "time"
|
||||
|
||||
// unixGracefulTerm 在 Windows 上没有可靠的 SIGTERM 等价信号(CTRL_BREAK 仅
|
||||
// 对 console 子进程生效)。直接 Process.Kill,grace 参数不参与,仅签名一致。
|
||||
func (s *Supervisor) unixGracefulTerm(proc *Process, grace time.Duration) {
|
||||
if proc.Cmd.Process == nil {
|
||||
return
|
||||
}
|
||||
_ = proc.Cmd.Process.Kill()
|
||||
}
|
||||
Reference in New Issue
Block a user