启动项目逻辑

This commit is contained in:
2026-06-09 22:43:29 +08:00
parent d5a28b11a3
commit f4a7c770af
43 changed files with 3797 additions and 42 deletions
+18
View File
@@ -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"`
+19 -3
View File
@@ -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
-23
View File
@@ -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()
}
}
-14
View File
@@ -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()
}