Documentation
¶
Index ¶
- Constants
- Variables
- func ResolveProjectID(ctx context.Context, projectRepo interfaces.ProjectRepository, projectID int64) int64
- func RunOutboxDispatcher(dispatcher *OutboxDispatcher)
- type AISummaryContent
- type AISummaryContentProvider
- type AISummaryEventHandler
- type AISummaryGenerateRequestEvent
- type AISummaryWorker
- type AgentProjectContextProvider
- type AppSessionEventHandler
- type ContainerCreateOptions
- type ContainerCreateWorker
- func (w *ContainerCreateWorker) AttachTerminalRuntime(ctx context.Context, instance *types.ContainerInstance, ...) (*TerminalRuntimeHandle, error)
- func (w *ContainerCreateWorker) ContainerTemplateName(ctx context.Context, templateID int64) (string, string)
- func (w *ContainerCreateWorker) DropTerminalRuntime(ctx context.Context, runtimeName, runtimeID string)
- func (w *ContainerCreateWorker) Handle(evt event.Event)
- func (w *ContainerCreateWorker) StartTerminalRuntime(ctx context.Context, userID, projectID, templateID, sessionID int64, ...) (*TerminalRuntimeHandle, error)
- type ContainerLifecycle
- func (l *ContainerLifecycle) ApplyDagNodeRuntimeSpec(spec *types.ContainerSpec, resolveVars map[string]string)
- func (l *ContainerLifecycle) ApplyRuntimeUser(spec *types.ContainerSpec, resolveVars map[string]string)
- func (l *ContainerLifecycle) GetRuntime() (containerruntime.Runtime, error)
- func (l *ContainerLifecycle) GetRuntimeByInstance(inst *types.ContainerInstance) (containerruntime.Runtime, error)
- func (l *ContainerLifecycle) MaxConcurrency() int
- func (l *ContainerLifecycle) ResolveRuntimeName() string
- func (l *ContainerLifecycle) Transition(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, ...) error
- func (l *ContainerLifecycle) TransitionAndEnqueue(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, ...) error
- type ContainerManager
- func (m *ContainerManager) CreateByTemplateWithOptions(ctx context.Context, opts ContainerCreateOptions) (*types.ContainerInstance, error)
- func (m *ContainerManager) CreateQueueMaxPending() int
- func (m *ContainerManager) Delete(ctx context.Context, id int64) error
- func (m *ContainerManager) Describe(ctx context.Context, id int64) (*containerruntime.RuntimeDescription, error)
- func (m *ContainerManager) GetLogs(ctx context.Context, id int64, tail int) (string, error)
- func (s *ContainerManager) GetMaxConcurrency() int
- func (m *ContainerManager) GetRuntime() (containerruntime.Runtime, error)
- func (s *ContainerManager) Lifecycle() *ContainerLifecycle
- func (m *ContainerManager) OnEvent(e containerruntime.RuntimeEvent)
- func (m *ContainerManager) OpenTerminal(ctx context.Context, id int64, opts containerruntime.TerminalOptions) (containerruntime.TerminalSession, error)
- func (m *ContainerManager) QueueStatus(ctx context.Context) (*CreateQueueStatus, error)
- func (m *ContainerManager) RecoverRuntimeMonitoring(ctx context.Context) (int, error)
- func (m *ContainerManager) ResolveRuntimeName() string
- func (m *ContainerManager) RunRuntimeReconciler(ctx context.Context, interval time.Duration)
- func (s *ContainerManager) SetCreateWorker(worker *ContainerCreateWorker)
- func (s *ContainerManager) SetHub(hub *realtime.Hub)
- func (m *ContainerManager) Start(ctx context.Context, id int64) error
- func (m *ContainerManager) Stop(ctx context.Context, id int64) error
- func (m *ContainerManager) StopByOwner(ctx context.Context, ownerType types.ContainerOwnerType, ownerID int64) error
- func (s *ContainerManager) TransitionContainerAndEnqueueOutbox(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, ...) error
- func (s *ContainerManager) WaitForInstanceStatus(ctx context.Context, instanceID int64, wanted ...types.ContainerStatus) (types.ContainerStatus, error)
- type ContainerRuntimeResolveInput
- type ContainerRuntimeResolver
- type CreateQueueStatus
- type GormTerminalSessionStore
- func (s *GormTerminalSessionStore) Create(ctx context.Context, rec *types.AppSession) error
- func (s *GormTerminalSessionStore) Delete(ctx context.Context, key TerminalSessionKey) error
- func (s *GormTerminalSessionStore) DeleteByID(ctx context.Context, id int64) error
- func (s *GormTerminalSessionStore) FindAgentSession(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (*types.AppSession, error)
- func (s *GormTerminalSessionStore) Get(ctx context.Context, key TerminalSessionKey) (*types.AppSession, error)
- func (s *GormTerminalSessionStore) GetByID(ctx context.Context, id int64) (*types.AppSession, error)
- func (s *GormTerminalSessionStore) List(ctx context.Context) ([]*types.AppSession, error)
- func (s *GormTerminalSessionStore) ListByUser(ctx context.Context, userID int64) ([]*types.AppSession, error)
- func (s *GormTerminalSessionStore) MarkPersistent(ctx context.Context, key TerminalSessionKey) error
- func (s *GormTerminalSessionStore) Touch(ctx context.Context, key TerminalSessionKey, at time.Time) error
- func (s *GormTerminalSessionStore) Update(ctx context.Context, rec *types.AppSession) error
- type ManagedTerminalSession
- func (s *ManagedTerminalSession) Attach(sinceSeq int64) (*TerminalSubscriber, []byte)
- func (s *ManagedTerminalSession) AttachObserver() func()
- func (s *ManagedTerminalSession) AttachWithInfo(sinceSeq int64) (*TerminalSubscriber, TerminalReplayInfo)
- func (s *ManagedTerminalSession) Close()
- func (s *ManagedTerminalSession) Detach(sub *TerminalSubscriber)
- func (s *ManagedTerminalSession) ID() string
- func (s *ManagedTerminalSession) Key() TerminalSessionKey
- func (s *ManagedTerminalSession) Resize(rows, cols uint16) error
- func (s *ManagedTerminalSession) SendEvent(payload map[string]any)
- func (s *ManagedTerminalSession) Size() (rows, cols uint16)
- func (s *ManagedTerminalSession) WriteInput(data []byte) (int, error)
- type OutboxCreateRequestEvent
- type OutboxDeleteRequestEvent
- type OutboxDispatcher
- type OutboxRecreateRequestEvent
- type OutboxStartRequestEvent
- type OutboxStopRequestEvent
- type ProjectContextProvider
- type RuntimeVariableSet
- type TerminalCommand
- type TerminalCommandResult
- type TerminalInstanceStore
- type TerminalRegistryOptions
- type TerminalReplayInfo
- type TerminalRuntimeHandle
- type TerminalRuntimeOpener
- type TerminalSessionKey
- type TerminalSessionRegistry
- func (r *TerminalSessionRegistry) Close(ctx context.Context, terminalID int64)
- func (r *TerminalSessionRegistry) CloseSession(key TerminalSessionKey) bool
- func (r *TerminalSessionRegistry) CloseTerminal(ctx context.Context, terminalID int64)
- func (r *TerminalSessionRegistry) CreateForTemplate(ctx context.Context, userID, projectID, templateID int64, rows, cols uint16) (*ManagedTerminalSession, *types.AppSession, error)
- func (r *TerminalSessionRegistry) Ensure(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (int64, error)
- func (r *TerminalSessionRegistry) EnsureAgentTerminal(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (int64, error)
- func (r *TerminalSessionRegistry) EnsurePersistent(ctx context.Context, key TerminalSessionKey) (*ManagedTerminalSession, bool, error)
- func (r *TerminalSessionRegistry) EnsurePersistentSized(ctx context.Context, key TerminalSessionKey, rows, cols uint16) (*ManagedTerminalSession, bool, error)
- func (r *TerminalSessionRegistry) Exec(ctx context.Context, terminalID int64, command string, timeoutSeconds int) (runtimecontext.ContainerTerminalExecResult, error)
- func (r *TerminalSessionRegistry) ExecInTerminal(ctx context.Context, terminalID int64, command string, timeoutSeconds int) (runtimecontext.ContainerTerminalExecResult, error)
- func (r *TerminalSessionRegistry) Execute(ctx context.Context, key TerminalSessionKey, cmd TerminalCommand) (TerminalCommandResult, error)
- func (r *TerminalSessionRegistry) Lookup(key TerminalSessionKey) *ManagedTerminalSession
- func (r *TerminalSessionRegistry) LookupByID(id int64) *ManagedTerminalSession
- func (r *TerminalSessionRegistry) MarkPersistent(ctx context.Context, key TerminalSessionKey)
- func (r *TerminalSessionRegistry) ReviveByID(ctx context.Context, id int64, rows, cols uint16) (*ManagedTerminalSession, error)
- func (r *TerminalSessionRegistry) RunInTerminal(ctx context.Context, in runtimecontext.ContainerTerminalExecInput) (runtimecontext.ContainerTerminalExecResult, error)
- func (r *TerminalSessionRegistry) SessionRecord(ctx context.Context, id int64) (*types.AppSession, error)
- func (r *TerminalSessionRegistry) StartMaintenance(ctx context.Context)
- func (r *TerminalSessionRegistry) Status(key TerminalSessionKey) TerminalSessionStatus
- type TerminalSessionStatus
- type TerminalSessionStore
- type TerminalSubscriber
Constants ¶
const ( // OutboxEventTypeCreateRequest is the outbox event type for container creation requests. OutboxEventTypeCreateRequest = "ContainerCreateRequest" // OutboxEventTypeStopRequest is the outbox event type for container stop requests. OutboxEventTypeStopRequest = "ContainerStopRequest" // OutboxEventTypeDeleteRequest is the outbox event type for container delete requests. OutboxEventTypeDeleteRequest = "ContainerDeleteRequest" // OutboxEventTypeStartRequest is the outbox event type for container start requests. OutboxEventTypeRecreateRequest = "AppSessionContainerRecreateRequest" OutboxEventTypeStartRequest = "ContainerStartRequest" )
const ContainerQueueRealtimeCategory = "container-queue"
ContainerQueueRealtimeCategory 是「容器队列」实时通知在前端的组件类目。
前端 ContainerQueueMonitor 用 useComponentStore 注册 category=container-queue 的 handler(method=containerChanged),后端每次推送都会触发它重新拉取 /container/queue/status。
const OutboxEventTypeAISummaryGenerateRequest = "AISummaryGenerateRequest"
OutboxEventTypeAISummaryGenerateRequest 是 AI 摘要生成请求的 outbox 事件类型。
Variables ¶
var ( // ErrTerminalSessionNotFound 表示没有可用的会话,且调用方不允许自动创建。 ErrTerminalSessionNotFound = errors.New("terminal session not found") // ErrTerminalSessionClosed 表示会话已经结束(shell 退出或容器被删除)。 ErrTerminalSessionClosed = errors.New("terminal session is closed") // ErrTerminalBusy 表示命令队列已满。 ErrTerminalBusy = errors.New("terminal session is busy") // ErrTerminalSessionQuotaExceeded 表示当前用户的终端会话数已达上限。 ErrTerminalSessionQuotaExceeded = errors.New("terminal session quota exceeded") )
Functions ¶
func ResolveProjectID ¶ added in v0.2.0
func ResolveProjectID(ctx context.Context, projectRepo interfaces.ProjectRepository, projectID int64) int64
ResolveProjectID 校验项目数值主键(t_project.id)存在并返回它。
磁盘上的项目目录统一按项目主键组织(见 utils.GetProjectDir、utils.GetAISummaryFile 等), 而 Analysis / AnalysisNode / AISummary 记录里保存的也是这个数值主键,因此读写项目下的 文件前只需确认该项目存在即可。项目不存在时返回 0。
func RunOutboxDispatcher ¶
func RunOutboxDispatcher(dispatcher *OutboxDispatcher)
Types ¶
type AISummaryContent ¶ added in v0.1.2
type AISummaryContent struct {
// Title 是摘要标题的候选值。
Title string
// SystemPromptVars 是渲染摘要 AgentProfile 系统提示词模板(pongo2)所需的变量。
//
// 与 RuntimeContextResolver 一致:这里只提供业务上下文事实(输出目录、输出文件等),
// 真正的系统提示词由 AgentService 用 Profile 模板 + 这些变量渲染。
SystemPromptVars map[string]any
// WorkingDir 是 Agent 执行任务时的工作目录。
WorkingDir string
// ProjectID 是摘要所属对象(Analysis / AnalysisNode)归属的项目 ID,
// 供 AISummaryWorker 回填到摘要记录。
ProjectID int64
}
AISummaryContent 是生成摘要所需的原始内容。
type AISummaryContentProvider ¶ added in v0.1.2
type AISummaryContentProvider interface {
Resolve(ctx context.Context, ownerType types.SummaryOwnerType, ownerID int64) (AISummaryContent, error)
}
AISummaryContentProvider 根据摘要所属对象解析用于生成摘要的原始内容。 当前基于 Analysis / AnalysisNode 的字段组装文本;后续可扩展为读取 实际 output 文件内容。
func NewAISummaryContentProvider ¶ added in v0.1.2
func NewAISummaryContentProvider(analysisRepo interfaces.AnalysisRepository, workflowRepo interfaces.WorkflowRepository, cfg *config.Config) AISummaryContentProvider
NewAISummaryContentProvider 创建 AISummaryContentProvider。 系统提示词优先读取配置 cfg.AISummary.SystemPrompt,未配置时回退到默认值。
type AISummaryEventHandler ¶ added in v0.1.2
type AISummaryEventHandler struct {
// contains filtered or unexported fields
}
AISummaryEventHandler 订阅 AISummaryStatusEvent,并将摘要状态变更推送给 所属项目用户,前端据此刷新 AISummaryPanel。
func NewAISummaryEventHandler ¶ added in v0.1.2
func NewAISummaryEventHandler(db *gorm.DB, analysisRepo interfaces.AnalysisRepository, projectRepo interfaces.ProjectRepository, hub *realtime.Hub) *AISummaryEventHandler
func (*AISummaryEventHandler) Handle ¶ added in v0.1.2
func (h *AISummaryEventHandler) Handle(evt event.Event)
type AISummaryGenerateRequestEvent ¶ added in v0.1.2
AISummaryGenerateRequestEvent is published to the event bus when an AISummaryGenerateRequest outbox event is picked up.
type AISummaryWorker ¶ added in v0.1.2
type AISummaryWorker struct {
// contains filtered or unexported fields
}
AISummaryWorker 订阅 AISummaryGenerateRequestEvent,异步执行 AI 摘要生成。
func NewAISummaryWorker ¶ added in v0.1.2
func NewAISummaryWorker( summaryRepo interfaces.AISummaryRepository, containerRepo interfaces.ContainerRepository, projectRepo interfaces.ProjectRepository, bus event.Bus, agentService *agentservice.AgentService, content AISummaryContentProvider, cfg *config.Config, ) *AISummaryWorker
NewAISummaryWorker 创建 AISummaryWorker。
func (*AISummaryWorker) Handle ¶ added in v0.1.2
func (w *AISummaryWorker) Handle(evt event.Event)
Handle 分发事件到对应的处理逻辑。
type AgentProjectContextProvider ¶ added in v0.1.2
type AgentProjectContextProvider struct {
// contains filtered or unexported fields
}
AgentProjectContextProvider 实现 ProjectContextProvider: 查询当前用户激活项目下已完成(status=done)的分析节点,以及项目关联的参考文献, 并格式化为上下文的文本。
采用实时查询而非写入 memory repo 的设计:analysis_nodes / project_literature 是领域状态的唯一数据源,状态在运行期间频繁变化,实时查询可保证内容始终最新。
func NewAgentProjectContextProvider ¶ added in v0.1.2
func NewAgentProjectContextProvider( projects interfaces.ProjectRepository, analyses interfaces.AnalysisRepository, cfg *config.Config, ) *AgentProjectContextProvider
NewAgentProjectContextProvider 创建项目上下文提供者。
func (*AgentProjectContextProvider) ProjectContext ¶ added in v0.1.2
func (p *AgentProjectContextProvider) ProjectContext(ctx context.Context, userID int64) (string, error)
ProjectContext 实现 ProjectContextProvider。
type AppSessionEventHandler ¶
type AppSessionEventHandler struct {
// contains filtered or unexported fields
}
AppSessionEventHandler 是「应用会话」对前端的唯一推送出口。
它订阅两类事件:
types.ContainerEvent(容器生命周期,来自 outbox dispatcher): 把容器状态同步到所属 AppSession 的 Status / StartedAt / StoppedAt。 只处理 OwnerType == ContainerOwnerAppSession 的容器,DAG 节点 / 服务忽略。
types.AppSessionEvent(会话运行态,来自 manager.TerminalSessionRegistry): 终端会话(= AppSession)的创建 / 删除 / busy / 订阅者变化,以及 Agent 在终端里 执行命令,都由会话层发布到这里,再统一推给前端。
之所以只保留这一处推送:会话层不再直接持有 realtime.Hub,避免同一次状态变化 被「容器事件」和「会话事件」各推一遍(前端会连续 refetch 两次)。
func NewAppSessionEventHandler ¶
func NewAppSessionEventHandler(repo interfaces.ContainerRepository, hub *realtime.Hub) *AppSessionEventHandler
func (*AppSessionEventHandler) Handle ¶
func (h *AppSessionEventHandler) Handle(evt event.Event)
type ContainerCreateOptions ¶ added in v0.2.0
type ContainerCreateOptions struct {
TemplateID int64
OwnerType types.ContainerOwnerType
OwnerID int64
Name string
// Kind 是容器运行形态(terminal / web / dag_node)。
// 为空时按 OwnerType 推断(见 types.ContainerInstance.NormalizedKind)。
Kind types.ContainerInstanceKind
// Sync 为 true 时同步创建并等待容器 running:
// - 不写 outbox,直接由 worker 调用 runtime.Create + runtime.Start;
// - 创建过程中的状态迁移不计入创建并发额度(长期占用 running 的实例);
// - 返回前等待 ContainerManager.OnEvent 广播的 ContainerStarted(running)。
//
// Sync 为 false(默认)时保持旧行为:写 outbox,由 OutboxDispatcher 异步创建。
Sync bool
// WaitTimeout 是 Sync=true 时等待容器 running 的上限;<=0 使用默认 2 分钟。
WaitTimeout time.Duration
}
return nil }
ContainerCreateOptions 是 CreateByTemplateWithOptions 的参数。
与仅按模板 + owner 创建相比,它额外支持:
- 显式指定容器运行形态(Kind):决定 executeCreateInstance 如何组装运行时 spec; 未指定时按 OwnerType 推断(见 types.ContainerInstance.NormalizedKind), 并随实例一起落库;
- 同步创建:不写 outbox、由 ContainerCreateWorker 直接调用 runtime.Create + runtime.Start,并在返回前等待容器进入 running(终端会话需要立刻 exec shell)。
启动命令、workload 类型、运行用户等只由 Kind 决定(见 executeCreateInstance), 这里不再接受 Command / Entrypoint 覆盖。
type ContainerCreateWorker ¶
type ContainerCreateWorker struct {
// contains filtered or unexported fields
}
ContainerCreateWorker subscribes to OutboxCreateRequestEvent and ContainerEvent from the event bus. For creation requests it executes rt.Create + rt.Start, and updates the active create request count while the request is being handled.
func NewContainerCreateWorker ¶
func NewContainerCreateWorker( repo interfaces.ContainerRepository, projectRepo interfaces.ProjectRepository, analysisRepo interfaces.AnalysisRepository, workflowService interfaces.WorkflowService, reg *containerruntime.Registry, res ContainerRuntimeResolver, lifecycle *ContainerLifecycle, cfg *config.Config, ) *ContainerCreateWorker
NewContainerCreateWorker creates a new worker.
func (*ContainerCreateWorker) AttachTerminalRuntime ¶ added in v0.2.0
func (w *ContainerCreateWorker) AttachTerminalRuntime(ctx context.Context, instance *types.ContainerInstance, opts containerruntime.TerminalOptions) (*TerminalRuntimeHandle, error)
AttachTerminalRuntime 在已有容器上执行交互式 shell(等价于 kubectl/docker exec -it)。
这是进程重启后「认领」原容器的唯一方式:容器还在,但原来的 shell 已经随连接 结束,所以这里得到的是一个新的 bash(cwd / 环境变量不会保留,历史输出由滚动缓冲回放)。
instance 是 ContainerInstance 记录:RuntimeName / RuntimeID 由它提供, Cleanup 也会按 owner 关系把实例与容器一并回收。
func (*ContainerCreateWorker) ContainerTemplateName ¶ added in v0.2.0
func (w *ContainerCreateWorker) ContainerTemplateName(ctx context.Context, templateID int64) (string, string)
ContainerTemplateName 返回模板的展示名与名称,供终端会话生成默认展示名。
模板不可读 / 未配置时返回空串,调用方会回退到模板 ID。
func (*ContainerCreateWorker) DropTerminalRuntime ¶ added in v0.2.0
func (w *ContainerCreateWorker) DropTerminalRuntime(ctx context.Context, runtimeName, runtimeID string)
DropTerminalRuntime 删除一个(可能来自旧进程的)终端容器,失败只记日志。
func (*ContainerCreateWorker) Handle ¶
func (w *ContainerCreateWorker) Handle(evt event.Event)
Handle dispatches events from the event bus. It handles four event types:
- OutboxCreateRequestEvent: executes deferred container creation
- OutboxStopRequestEvent: executes deferred container stop
- OutboxDeleteRequestEvent: executes deferred container delete
- OutboxStartRequestEvent: executes deferred container start
func (*ContainerCreateWorker) StartTerminalRuntime ¶ added in v0.2.0
func (w *ContainerCreateWorker) StartTerminalRuntime(ctx context.Context, userID, projectID, templateID, sessionID int64, opts containerruntime.TerminalOptions) (*TerminalRuntimeHandle, error)
StartTerminalRuntime 创建终端容器并打开交互式 shell。
容器由 ContainerManager.CreateByTemplateWithOptions(Sync=true, Kind=terminal) 同步 创建并启动(复用 ContainerCreateWorker.executeCreateInstance,模板 / 项目 env、挂载、 变量解析、storage 挂载与普通容器完全一致)。启动命令 / workload / 运行用户等 终端容器特有的差异全部由 buildInstanceSpec 按 Kind=terminal 处理。
sessionID 是终端会话(AppSession)主键:容器以 owner_type=app_session + owner_id=sessionID 归属该会话,进程重启后可按 owner 认领原容器。
type ContainerLifecycle ¶ added in v0.2.0
type ContainerLifecycle struct {
// contains filtered or unexported fields
}
ContainerLifecycle 收敛 ContainerManager 与 ContainerCreateWorker 共同依赖的 无状态基础设施,避免两者互相持有指针。
它只负责「实例状态迁移 + 运行时解析 + 运行 spec 组装」这三类地基层能力:
- 状态迁移:transition/TransitionAndEnqueue(FSM 校验 + 落库 + outbox);
- 运行时解析:GetRuntime / GetRuntimeByInstance;
- spec 组装:ApplyRuntimeUser / ApplyDagNodeRuntimeSpec。
依赖方向被固定为单向:ContainerManager → ContainerCreateWorker → ContainerLifecycle, ContainerLifecycle 不依赖任何一方。ContainerManager 仍保留同名导出方法作为薄委托, 对外 API 保持不变。
func NewContainerLifecycle ¶ added in v0.2.0
func NewContainerLifecycle( repo interfaces.ContainerRepository, reg *containerruntime.Registry, cfg *config.Config, ) *ContainerLifecycle
NewContainerLifecycle 构造共享生命周期基础设施。
func (*ContainerLifecycle) ApplyDagNodeRuntimeSpec ¶ added in v0.2.0
func (l *ContainerLifecycle) ApplyDagNodeRuntimeSpec(spec *types.ContainerSpec, resolveVars map[string]string)
ApplyDagNodeRuntimeSpec 按 DAG 节点的约定覆盖容器启动命令(bash ./run.sh)与运行用户。
func (*ContainerLifecycle) ApplyRuntimeUser ¶ added in v0.2.0
func (l *ContainerLifecycle) ApplyRuntimeUser(spec *types.ContainerSpec, resolveVars map[string]string)
ApplyRuntimeUser maps USERID/GROUPID to the runtime's user field. Docker uses uid:gid, while Kubernetes currently maps ContainerSpec.User to runAsUser.
以非 root 用户运行时,镜像里的 HOME(常见是 /root)对该用户不可写, 直接跑会失败(R / python / pip 都要写家目录)。因此这里等价于:
docker run --user "$UID:$GID" \ -e HOME=/home/app \ --tmpfs /home/app:rw,uid=$UID,gid=$GID,mode=700
即:User=uid[:gid]、HomeDir=/home/app、Tmpfs 挂一个属主为该用户的 700 内存盘。 如果调用方 / 模板已经显式设置了 HomeDir 或为同一路径配置了 tmpfs,则不再覆盖。
func (*ContainerLifecycle) GetRuntime ¶ added in v0.2.0
func (l *ContainerLifecycle) GetRuntime() (containerruntime.Runtime, error)
GetRuntime 按当前配置解析默认运行时。
func (*ContainerLifecycle) GetRuntimeByInstance ¶ added in v0.2.0
func (l *ContainerLifecycle) GetRuntimeByInstance(inst *types.ContainerInstance) (containerruntime.Runtime, error)
GetRuntimeByInstance 优先按实例的 RuntimeName 解析运行时,找不到时回退到默认运行时。
func (*ContainerLifecycle) MaxConcurrency ¶ added in v0.2.0
func (l *ContainerLifecycle) MaxConcurrency() int
MaxConcurrency 返回创建并发额度(未配置时默认 3)。
func (*ContainerLifecycle) ResolveRuntimeName ¶ added in v0.2.0
func (l *ContainerLifecycle) ResolveRuntimeName() string
ResolveRuntimeName 解析当前生效的运行时名称。
func (*ContainerLifecycle) Transition ¶ added in v0.2.0
func (l *ContainerLifecycle) Transition(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, eventType string, enforceConcurrency bool, enqueueOutbox bool) error
Transition 是容器实例状态迁移的唯一实现:读取最新状态 → FSM 校验 → 落库 → (可选)写 outbox 事件。
enforceConcurrency 控制是否把本次迁移计入创建并发额度;终端容器这类长期占用 running 的实例不参与该额度(false)。enqueueOutbox 控制是否写 outbox 事件; 由调用方同步驱动的内部迁移不需要 outbox(false)。
func (*ContainerLifecycle) TransitionAndEnqueue ¶ added in v0.2.0
func (l *ContainerLifecycle) TransitionAndEnqueue(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, eventType string) error
TransitionAndEnqueue 以「计入并发额度 + 写 outbox」的默认语义迁移实例状态。
type ContainerManager ¶
type ContainerManager struct {
// contains filtered or unexported fields
}
func NewContainerManager ¶
func NewContainerManager( containerRepo interfaces.ContainerRepository, analysisRepo interfaces.AnalysisRepository, projectRepo interfaces.ProjectRepository, workflowService interfaces.WorkflowService, reg *containerruntime.Registry, bus event.Bus, res ContainerRuntimeResolver, lifecycle *ContainerLifecycle, cfg *config.Config, ) *ContainerManager
func (*ContainerManager) CreateByTemplateWithOptions ¶ added in v0.2.0
func (m *ContainerManager) CreateByTemplateWithOptions(ctx context.Context, opts ContainerCreateOptions) (*types.ContainerInstance, error)
CreateByTemplateWithOptions 按模板创建容器实例(创建容器的唯一入口)。
默认走 outbox 异步链路,Sync=true 时改为同步创建并等待 running (见 ContainerCreateOptions)。
func (*ContainerManager) CreateQueueMaxPending ¶
func (m *ContainerManager) CreateQueueMaxPending() int
func (*ContainerManager) Delete ¶
func (m *ContainerManager) Delete(ctx context.Context, id int64) error
func (*ContainerManager) Describe ¶ added in v0.1.4
func (m *ContainerManager) Describe(ctx context.Context, id int64) (*containerruntime.RuntimeDescription, error)
Describe 按 ContainerInstance ID 从运行时获取详情(docker inspect / kubectl describe)。
func (*ContainerManager) GetMaxConcurrency ¶
func (s *ContainerManager) GetMaxConcurrency() int
func (*ContainerManager) GetRuntime ¶ added in v0.2.2
func (m *ContainerManager) GetRuntime() (containerruntime.Runtime, error)
GetRuntime 按当前配置解析默认运行时,语义与容器实际创建时完全一致 (同为 ContainerLifecycle.GetRuntime,解析顺序见 ResolveContainerRuntime)。 供配置检测接口复用,保证「检测结果」与「真实执行」不会出现口径差异。
func (*ContainerManager) Lifecycle ¶ added in v0.2.0
func (s *ContainerManager) Lifecycle() *ContainerLifecycle
Lifecycle 返回 Manager 持有的共享生命周期基础设施,供 ContainerCreateWorker 注入使用。
func (*ContainerManager) OnEvent ¶
func (m *ContainerManager) OnEvent(e containerruntime.RuntimeEvent)
如果容器运行太快,ContainerStarted与 ContainerExited可能会竞争
func (*ContainerManager) OpenTerminal ¶ added in v0.2.0
func (m *ContainerManager) OpenTerminal(ctx context.Context, id int64, opts containerruntime.TerminalOptions) (containerruntime.TerminalSession, error)
OpenTerminal 为运行中的容器实例打开一条交互式终端(kubectl exec -it / docker exec -it)。
只有容器实例处于 running 状态时才允许连接:命令结束、容器退出后终端也随之不可用。
func (*ContainerManager) QueueStatus ¶
func (m *ContainerManager) QueueStatus(ctx context.Context) (*CreateQueueStatus, error)
func (*ContainerManager) RecoverRuntimeMonitoring ¶
func (m *ContainerManager) RecoverRuntimeMonitoring(ctx context.Context) (int, error)
func (*ContainerManager) ResolveRuntimeName ¶ added in v0.2.2
func (m *ContainerManager) ResolveRuntimeName() string
ResolveRuntimeName 解析当前生效的运行时名称,语义与容器实际执行时一致 (ContainerLifecycle.ResolveRuntimeName)。
func (*ContainerManager) RunRuntimeReconciler ¶
func (m *ContainerManager) RunRuntimeReconciler(ctx context.Context, interval time.Duration)
RunRuntimeReconciler periodically reconnects runtime monitoring.
func (*ContainerManager) SetCreateWorker ¶ added in v0.2.0
func (s *ContainerManager) SetCreateWorker(worker *ContainerCreateWorker)
SetCreateWorker 注入同步创建容器所需的 worker(容器装配阶段调用一次)。
之所以不在构造函数里注入:ContainerCreateWorker 自身依赖 ContainerManager, 直接双向构造会形成循环依赖;装配阶段回填一个指针即可。
同时把 Manager 自己的 lifecycle 共享给 worker(worker 构造期可能未拿到), 并回填 worker.owner,供 StartTerminalRuntime 复用 CreateByTemplateWithOptions。
func (*ContainerManager) SetHub ¶ added in v0.2.4
func (s *ContainerManager) SetHub(hub *realtime.Hub)
SetHub 回填实时推送出口(由容器装配阶段调用一次)。
不走构造函数的原因见 hub 字段的注释:这是装配顺序问题,不是包级循环依赖。
func (*ContainerManager) Start ¶
func (m *ContainerManager) Start(ctx context.Context, id int64) error
func (*ContainerManager) Stop ¶
func (m *ContainerManager) Stop(ctx context.Context, id int64) error
func (*ContainerManager) StopByOwner ¶
func (m *ContainerManager) StopByOwner(ctx context.Context, ownerType types.ContainerOwnerType, ownerID int64) error
func (*ContainerManager) TransitionContainerAndEnqueueOutbox ¶
func (s *ContainerManager) TransitionContainerAndEnqueueOutbox(ctx context.Context, inst *types.ContainerInstance, to types.ContainerStatus, eventType string) error
func (*ContainerManager) WaitForInstanceStatus ¶ added in v0.2.0
func (s *ContainerManager) WaitForInstanceStatus(ctx context.Context, instanceID int64, wanted ...types.ContainerStatus) (types.ContainerStatus, error)
WaitForInstanceStatus 阻塞等待实例进入 wanted 中的任一状态。
状态推进完全由运行时事件驱动(ContainerManager.OnEvent),因此等待方只需要 订阅状态广播:先注册订阅者,再读一次数据库,避免“注册前事件已到”的竞态。 实例进入 failed / stopped / exited 且不在 wanted 中时返回错误。
type ContainerRuntimeResolveInput ¶
type ContainerRuntimeResolveInput struct {
Spec *types.ContainerSpec
Variables map[string]string
}
type ContainerRuntimeResolver ¶
type ContainerRuntimeResolver interface {
Resolve(ctx context.Context, in *ContainerRuntimeResolveInput) (*types.ContainerSpec, error)
}
func NewDefaultContainerRuntimeResolver ¶
func NewDefaultContainerRuntimeResolver() ContainerRuntimeResolver
type CreateQueueStatus ¶
type GormTerminalSessionStore ¶ added in v0.2.0
type GormTerminalSessionStore struct {
// contains filtered or unexported fields
}
GormTerminalSessionStore 是 TerminalSessionStore 的默认实现(底层是 go_container_app_session)。
func NewGormTerminalSessionStore ¶ added in v0.2.0
func NewGormTerminalSessionStore(db *gorm.DB) *GormTerminalSessionStore
NewGormTerminalSessionStore 构造基于 GORM 的会话记录存储。
func (*GormTerminalSessionStore) Create ¶ added in v0.2.0
func (s *GormTerminalSessionStore) Create(ctx context.Context, rec *types.AppSession) error
Create 新增一条会话记录,并回填 rec.ID。
只在新会话创建时调用:name 在这里写入一次,之后 Update 不会修改它。
func (*GormTerminalSessionStore) Delete ¶ added in v0.2.0
func (s *GormTerminalSessionStore) Delete(ctx context.Context, key TerminalSessionKey) error
func (*GormTerminalSessionStore) DeleteByID ¶ added in v0.2.0
func (s *GormTerminalSessionStore) DeleteByID(ctx context.Context, id int64) error
func (*GormTerminalSessionStore) FindAgentSession ¶ added in v0.2.0
func (s *GormTerminalSessionStore) FindAgentSession(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (*types.AppSession, error)
func (*GormTerminalSessionStore) Get ¶ added in v0.2.0
func (s *GormTerminalSessionStore) Get(ctx context.Context, key TerminalSessionKey) (*types.AppSession, error)
func (*GormTerminalSessionStore) GetByID ¶ added in v0.2.0
func (s *GormTerminalSessionStore) GetByID(ctx context.Context, id int64) (*types.AppSession, error)
func (*GormTerminalSessionStore) List ¶ added in v0.2.0
func (s *GormTerminalSessionStore) List(ctx context.Context) ([]*types.AppSession, error)
func (*GormTerminalSessionStore) ListByUser ¶ added in v0.2.0
func (s *GormTerminalSessionStore) ListByUser(ctx context.Context, userID int64) ([]*types.AppSession, error)
func (*GormTerminalSessionStore) MarkPersistent ¶ added in v0.2.0
func (s *GormTerminalSessionStore) MarkPersistent(ctx context.Context, key TerminalSessionKey) error
MarkPersistent 把会话标记为持久(只会 false → true)。
记录不存在时是空操作:真正的持久化由随后的 Upsert 写回。
func (*GormTerminalSessionStore) Touch ¶ added in v0.2.0
func (s *GormTerminalSessionStore) Touch(ctx context.Context, key TerminalSessionKey, at time.Time) error
func (*GormTerminalSessionStore) Update ¶ added in v0.2.0
func (s *GormTerminalSessionStore) Update(ctx context.Context, rec *types.AppSession) error
Update 按主键更新已存在记录的运行时字段。
刻意不包含 name:name 只在 Create 时写入,DB 是它的唯一真相;否则一旦改名 接口改写了 DB,后续任何一次运行态更新都会把旧名字写回去。
type ManagedTerminalSession ¶ added in v0.2.0
type ManagedTerminalSession struct {
// contains filtered or unexported fields
}
ManagedTerminalSession 是注册表持有一个 PTY(容器 + bash -l)。
func (*ManagedTerminalSession) Attach ¶ added in v0.2.0
func (s *ManagedTerminalSession) Attach(sinceSeq int64) (*TerminalSubscriber, []byte)
Attach 注册一个订阅者并返回重放数据。
前端(ContainerTemplateTerminal)接入时调用者还应调用 MarkPersistent, 把会话升级为持久会话。需要知道回放序号区间 / 是否需要清屏时用 AttachWithInfo。
sinceSeq <= 0 表示「尽可能早」(整段缓冲);否则从该序号开始回放。 注意 sinceSeq 是会话输出流的全局序号(见 TerminalReplayInfo),不是「本地收到的 字节数」;超过当前序号时会被当作失效序号整段重放(Reset)。 重放与订阅在同一临界区内完成,不会丢帧。
func (*ManagedTerminalSession) AttachObserver ¶ added in v0.2.0
func (s *ManagedTerminalSession) AttachObserver() func()
AttachObserver 登记一个「只占位、不接收输出」的订阅者,返回幂等的 Detach 函数。
供 terminal_exec 这类「我只需要保证执行期间不被回收」的调用使用:
- 它与 WS 订阅者共用 subs 计数,因此有前端终端在线时 Detach 不会触发回收;
- 它的 Data 是无缓冲 channel 且无人读取,publish 会走 default 分支丢弃, 不会像普通订阅者那样占用 2048 块的缓冲内存。
func (*ManagedTerminalSession) AttachWithInfo ¶ added in v0.2.0
func (s *ManagedTerminalSession) AttachWithInfo(sinceSeq int64) (*TerminalSubscriber, TerminalReplayInfo)
AttachWithInfo 与 Attach 相同,但额外返回本次回放的序号区间与续传状态。
为什么需要:调用方(前端)把「已消费到哪个序号」传回来才能增量回放,而序号是 环形缓冲里的全局偏移(bufStart 可能在缓冲滚动后不为 0)。如果客户端自己按 「收到多少字节」计数,一旦缓冲滚动、或会话被重建 / 认领(序号基准归零), 续传位置就会错位:轻则重复/跳过一段,重则 since 超出当前序号 → 空回放、历史全丢。
因此这里把边界情况显式告知调用方:
- sinceSeq > To:客户端位置超出本会话(会话重建 / 认领后序号基准改变), 回退为整段重放并置 Reset=true,客户端应先清屏;
- sinceSeq < bufStart:中间那段已被缓冲覆盖,置 Truncated=true;
- From/To 是本次回放覆盖的序号区间,客户端下次应从这里继续。
func (*ManagedTerminalSession) Close ¶ added in v0.2.0
func (s *ManagedTerminalSession) Close()
Close 关闭 shell 并删除容器;可重复调用。
func (*ManagedTerminalSession) Detach ¶ added in v0.2.0
func (s *ManagedTerminalSession) Detach(sub *TerminalSubscriber)
Detach 注销订阅者;最后一个订阅者离开后按会话类型决定是否立即回收。
func (*ManagedTerminalSession) ID ¶ added in v0.2.0
func (s *ManagedTerminalSession) ID() string
ID 返回会话 ID(终端会话主键的字符串形式)。
func (*ManagedTerminalSession) Key ¶ added in v0.2.0
func (s *ManagedTerminalSession) Key() TerminalSessionKey
Key 返回会话 key。
func (*ManagedTerminalSession) Resize ¶ added in v0.2.0
func (s *ManagedTerminalSession) Resize(rows, cols uint16) error
Resize 调整 PTY 尺寸(前端 xterm 是尺寸的唯一来源)。
func (*ManagedTerminalSession) SendEvent ¶ added in v0.2.0
func (s *ManagedTerminalSession) SendEvent(payload map[string]any)
SendEvent 向所有订阅者广播一条 JSON 控制事件。
func (*ManagedTerminalSession) Size ¶ added in v0.2.0
func (s *ManagedTerminalSession) Size() (rows, cols uint16)
Size 返回 PTY 当前尺寸(前端据此渲染回放,保证宽度一致)。
func (*ManagedTerminalSession) WriteInput ¶ added in v0.2.0
func (s *ManagedTerminalSession) WriteInput(data []byte) (int, error)
WriteInput 写入用户按键(终端原始输入)。
type OutboxCreateRequestEvent ¶
OutboxCreateRequestEvent is published to the event bus when a ContainerCreateRequest outbox event is picked up. Handlers that need to process deferred container creation subscribe to this.
type OutboxDeleteRequestEvent ¶
OutboxDeleteRequestEvent is published to the event bus when a ContainerDeleteRequest outbox event is picked up.
type OutboxDispatcher ¶
type OutboxDispatcher struct {
// contains filtered or unexported fields
}
func NewOutboxDispatcher ¶
func NewOutboxDispatcher(repo interfaces.ContainerRepository, bus event.Bus) *OutboxDispatcher
func (*OutboxDispatcher) Start ¶
func (d *OutboxDispatcher) Start(ctx context.Context)
type OutboxStartRequestEvent ¶
OutboxStartRequestEvent is published to the event bus when a ContainerStartRequest outbox event is picked up.
type OutboxStopRequestEvent ¶
OutboxStopRequestEvent is published to the event bus when a ContainerStopRequest outbox event is picked up.
type ProjectContextProvider ¶ added in v0.2.0
type ProjectContextProvider interface {
// ProjectContext 返回当前用户激活项目下的上下文文本块;无可用内容时返回空串。
ProjectContext(ctx context.Context, userID int64) (string, error)
}
ProjectContextProvider 是领域上下文提供者接口的窄定义。
它描述「按用户解析当前项目上下文文本块」的能力,具体实现见 AgentProjectContextProvider。当前 Agent 系统提示词的注入调用已被移除,本接口 仅作为保留能力定义,供后续按需接入。
type RuntimeVariableSet ¶ added in v0.1.4
type RuntimeVariableSet struct {
// contains filtered or unexported fields
}
RuntimeVariableSet 是一组已求值的运行时变量,可派生扁平 map 或分类列表。
func (*RuntimeVariableSet) ByCategory ¶ added in v0.1.4
func (s *RuntimeVariableSet) ByCategory(cat types.RuntimeVariableCategory) []types.RuntimeVariable
ByCategory 返回指定分类的变量,供前端分组展示。
func (*RuntimeVariableSet) List ¶ added in v0.1.4
func (s *RuntimeVariableSet) List() []types.RuntimeVariable
List 返回全部变量(含元数据),供 API 返回。
func (*RuntimeVariableSet) ToMap ¶ added in v0.1.4
func (s *RuntimeVariableSet) ToMap() map[string]string
ToMap 派生扁平 map,供 ContainerRuntimeResolver 替换占位符,行为与原 setRuntimeVar 一致(跳过空值)。
type TerminalCommand ¶ added in v0.2.0
type TerminalCommand struct {
// Command 是要执行的 shell 命令,可多行。
Command string
// Wait 为 true 时等待命令结束后返回退出码与输出;false 时只负责投递。
Wait bool
// Timeout <= 0 时使用默认超时。
Timeout time.Duration
// Source 标记来源(ui / agent),用于前端展示与审计。
Source string
// NotifyUI 为 true 时通过 realtime 推送 terminal.exec 事件。
NotifyUI bool
// CreateIfMissing 为 true 时允许自动创建会话。
CreateIfMissing bool
// Persistent 为 true 时,本命令「新建」的会话直接按持久会话创建。
//
// 原因:一次性会话在命令结束且没有订阅者时会被立即回收(容器也会删除),
// 因此「先投递命令、再由前端打开终端看输出」的场景必须建持久会话,
// 否则命令执行完容器就没了,前端随后接入拿到的是另一台新容器(看不到输出)。
Persistent bool
}
TerminalCommand 是一次写入终端的命令。
type TerminalCommandResult ¶ added in v0.2.0
type TerminalCommandResult struct {
SessionID string
Accepted bool
Running bool
ExitCode int
TimedOut bool
DurationSeconds float64
Output string
OutputTruncated bool
}
TerminalCommandResult 是一次命令投递 / 执行的结果。
type TerminalInstanceStore ¶ added in v0.2.0
type TerminalInstanceStore interface {
GetContainerInstanceByOwner(ctx context.Context, ownerType types.ContainerOwnerType, ownerID int64) (*types.ContainerInstance, error)
DeleteContainerInstance(ctx context.Context, id int64) error
}
TerminalInstanceStore 定位「终端会话对应的 ContainerInstance」。
容器实例是会话容器的唯一真相:认领(进程重启后在原容器上重新 exec shell)与 删除(关闭会话时回收容器)都按 (owner_type=app_session, owner_id=会话 ID) 查询。
type TerminalRegistryOptions ¶ added in v0.2.0
type TerminalRegistryOptions struct {
// SessionTTL 是持久会话的空闲上限:超过即回收容器并删除记录。<=0 使用默认 2 小时。
SessionTTL time.Duration
// MaxSessionsPerUser 限制单个用户同时存在的终端会话数;<=0 使用默认 4。
MaxSessionsPerUser int
// ScrollbackDir / ScrollbackBytes 控制滚动缓冲落盘目录与单会话上限。
ScrollbackDir string
ScrollbackBytes int
ReadyWait time.Duration
CreateWait time.Duration
BufferBytes int
ExecTimeout time.Duration
ExecQueueSize int
}
TerminalRegistryOptions 控制会话生命周期,零值使用默认值。
type TerminalReplayInfo ¶ added in v0.2.0
type TerminalReplayInfo struct {
// Data 是从 From 到 To 的输出字节。客户端下次重连应把 To + 本连接新收到的字节数
// 作为新的 since 传回来(不要自己从 0 开始数字节,否则缓冲滚动后会错位)。
Data []byte
// From / To 是本次回放在会话输出流里的序号区间([From, To))。
From int64
To int64
// Truncated 表示客户端请求的位置早于环形缓冲起点:中间有一段输出已被滚动覆盖。
Truncated bool
// Reset 表示客户端持有的序号超出了本会话(会话被重建 / 认领后序号基准改变),
// 已回退为「整段重放」,客户端应先清屏再写入 Data,否则会出现重复内容。
Reset bool
}
TerminalReplayInfo 描述一次回放覆盖的序号区间。
type TerminalRuntimeHandle ¶ added in v0.2.0
type TerminalRuntimeHandle struct {
Runtime containerruntime.Runtime
RuntimeName string
RuntimeID string
ContainerName string
ContainerInstanceID int64
Session containerruntime.TerminalSession
Cleanup func()
}
TerminalRuntimeHandle 是一次「终端容器 + 交互式 shell」的句柄。
它把创建/认领两条路径统一起来:
- StartTerminalRuntime 同步创建容器(keep-alive 常驻)后打开 shell;
- AttachTerminalRuntime 在已有容器上重新 exec 一个 shell(进程重启后的认领)。
Cleanup 负责删除容器(关闭 shell 之后调用);调用方必须在会话 Close 时执行它。
type TerminalRuntimeOpener ¶ added in v0.2.0
type TerminalRuntimeOpener interface {
StartTerminalRuntime(ctx context.Context, userID, projectID, templateID, sessionID int64, opts containerruntime.TerminalOptions) (*TerminalRuntimeHandle, error)
AttachTerminalRuntime(ctx context.Context, instance *types.ContainerInstance, opts containerruntime.TerminalOptions) (*TerminalRuntimeHandle, error)
DropTerminalRuntime(ctx context.Context, runtimeName, runtimeID string)
// ContainerTemplateName 返回模板的展示名与名称,供新建会话生成默认展示名。
// 模板不可读时返回空串,调用方回退到模板 ID。
ContainerTemplateName(ctx context.Context, templateID int64) (displayName, name string)
}
TerminalRuntimeOpener 抽象「创建终端容器 / 认领已有容器 + 打开交互式 shell」的能力。 *ContainerCreateWorker 实现它;单测可注入 fake PTY。
sessionID 是终端会话主键(= AppSession.ID):容器以 owner_type=app_session、 owner_id=sessionID 归属该会话。instance 是认领时使用的 ContainerInstance 记录, RuntimeName / RuntimeID 由它提供。
type TerminalSessionKey ¶ added in v0.2.0
type TerminalSessionKey struct {
UserID int64
ProjectID int64
TemplateID int64
// AgentSessionID 是创建该终端会话的 Agent 会话 ID(agent_sessions.id);
// 0 表示用户从 UI 手动创建。
AgentSessionID int64
// ID 是终端会话主键(= AppSession.ID)。0 表示尚未定位到具体会话(创建请求);
// 落库前的新会话使用进程内负数占位 ID(见 newTerminalSessionPlaceholderID)。
ID int64
}
TerminalSessionKey 定位一个终端会话。
会话的对外标识是 AppSession.ID;这里保留 (userID, projectID, templateID, agentSessionID) 是为了:
- Agent 侧按 (templateID, agentSessionID) 查找 / 复用会话;
- UI 手动创建时按模板新建会话;
- 由 key 派生出 SessionKey(滚动缓冲文件名)。
func NewAgentKey ¶ added in v0.2.0
func NewAgentKey(userID, projectID, templateID, agentSessionID int64) TerminalSessionKey
NewAgentKey 返回「某个 Agent 会话在某个模板上的终端」的 key。
同一 (templateID, agentSessionID) 只会对应一个终端会话(见 registry 的 resolveRecordKey),因此 Agent 侧不需要关心终端会话 ID。
func NewUIKey ¶ added in v0.2.0
func NewUIKey(userID, projectID, templateID int64) TerminalSessionKey
NewUIKey 返回「用户从 UI 在某个模板上新建会话」的 key(尚未分配终端会话 ID)。
func (TerminalSessionKey) ReplaceID ¶ added in v0.2.0
func (k TerminalSessionKey) ReplaceID(id int64) TerminalSessionKey
ReplaceID 返回把终端会话主键替换为 id 的 key 副本。
func (TerminalSessionKey) SessionID ¶ added in v0.2.0
func (k TerminalSessionKey) SessionID() string
SessionID 返回稳定的会话 ID(= SessionKey,仅供日志 / 内部使用)。
func (TerminalSessionKey) SessionKey ¶ added in v0.2.0
func (k TerminalSessionKey) SessionKey() string
SessionKey 返回稳定的会话标识。
只作为唯一键与滚动缓冲文件名,对外(接口 / 前端)一律使用 ID。 ID == 0 表示「尚未分配」(创建请求),此时用 "new" 占位。
func (TerminalSessionKey) UIKey ¶ added in v0.2.0
func (k TerminalSessionKey) UIKey() TerminalSessionKey
UIKey 返回「去掉终端会话 ID」后的 key:用于从 ID 反向定位 (user/template/agent session)。
type TerminalSessionRegistry ¶ added in v0.2.0
type TerminalSessionRegistry struct {
// contains filtered or unexported fields
}
TerminalSessionRegistry 维护终端会话 ID → PTY 的映射。
func NewTerminalSessionRegistry ¶ added in v0.2.0
func NewTerminalSessionRegistry(worker *ContainerCreateWorker, bus event.Bus, store TerminalSessionStore, instances TerminalInstanceStore, cfg *config.Config) *TerminalSessionRegistry
NewTerminalSessionRegistry 构造会话注册表。
worker 复用 ContainerCreateWorker 的容器组装与创建链路;bus 可为 nil(例如单测), 此时不推送前端事件;store 记录会话(= AppSession)并供进程重启后 认领;instances 用于按会话定位 / 删除 ContainerInstance;cfg 决定回收策略 / 配额 / 滚动缓冲目录。
func (*TerminalSessionRegistry) Close ¶ added in v0.2.3
func (r *TerminalSessionRegistry) Close(ctx context.Context, terminalID int64)
Close 实现 hostshell.ContainerShellService。
func (*TerminalSessionRegistry) CloseSession ¶ added in v0.2.0
func (r *TerminalSessionRegistry) CloseSession(key TerminalSessionKey) bool
CloseSession 主动结束会话(删除容器),并删除持久层记录。
只按终端会话主键寻址:key.ID 必须 > 0。
func (*TerminalSessionRegistry) CloseTerminal ¶ added in v0.2.3
func (r *TerminalSessionRegistry) CloseTerminal(ctx context.Context, terminalID int64)
CloseTerminal 按终端会话主键关闭会话并删除容器。
与 ClosePersistentShell 对应:Agent 请求结束时调用。
func (*TerminalSessionRegistry) CreateForTemplate ¶ added in v0.2.0
func (r *TerminalSessionRegistry) CreateForTemplate(ctx context.Context, userID, projectID, templateID int64, rows, cols uint16) (*ManagedTerminalSession, *types.AppSession, error)
CreateForTemplate 按容器模板新建一条「UI 手动创建」的终端会话并立即启动。
一个模板可以有多条这样的会话(每次点「新建」都新建一条),彼此独立。 持久会话(只能由空闲 TTL 或显式关闭回收);展示名由 persistRecord 按模板生成。
func (*TerminalSessionRegistry) Ensure ¶ added in v0.2.3
func (r *TerminalSessionRegistry) Ensure(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (int64, error)
Ensure 实现 hostshell.ContainerShellService。
func (*TerminalSessionRegistry) EnsureAgentTerminal ¶ added in v0.2.3
func (r *TerminalSessionRegistry) EnsureAgentTerminal(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (int64, error)
EnsureAgentTerminal 按 (userID, projectID, templateID, agentSessionID) 创建或复用 Agent 会话的终端容器,返回终端会话主键。
与 GetPersistentShell 对应:供 hostshell.ContainerShell 在第一次执行命令时调用, 之后同一 Agent 会话的多次调用复用同一个容器与 PTY。
会话一律标记为持久(persistent=true):容器 shell 的生命周期等于一次 Agent 请求, 由调用方在请求结束时通过 Close / CloseShell 回收(见 providers/custom.go 的 OnFinish)。 若按一次性会话创建,命令一结束 execLoop 就会 scheduleReap 掉容器与会话记录, 下一次 Exec 只能拿到 ErrTerminalSessionNotFound。
func (*TerminalSessionRegistry) EnsurePersistent ¶ added in v0.2.0
func (r *TerminalSessionRegistry) EnsurePersistent(ctx context.Context, key TerminalSessionKey) (*ManagedTerminalSession, bool, error)
EnsurePersistent 创建(或复用)会话,并把会话标记为持久会话(见 types.AppSession.Persistent)。
WebSocket 接入(ContainerTemplateTerminal)走这个入口:无论会话是否新建、是否复用, 只要前端接入了,此后就只能由空闲 TTL 或显式 DELETE 回收。
func (*TerminalSessionRegistry) EnsurePersistentSized ¶ added in v0.2.0
func (r *TerminalSessionRegistry) EnsurePersistentSized(ctx context.Context, key TerminalSessionKey, rows, cols uint16) (*ManagedTerminalSession, bool, error)
EnsurePersistentSized 与 EnsurePersistent 相同,但会带上前端 xterm 的行列: 新建的会话直接以这个尺寸创建 PTY;已存活的会话保持原 PTY,接入方再调用 Resize 同步。
func (*TerminalSessionRegistry) Exec ¶ added in v0.2.3
func (r *TerminalSessionRegistry) Exec(ctx context.Context, terminalID int64, command string, timeoutSeconds int) (runtimecontext.ContainerTerminalExecResult, error)
Exec 实现 hostshell.ContainerShellService。
func (*TerminalSessionRegistry) ExecInTerminal ¶ added in v0.2.3
func (r *TerminalSessionRegistry) ExecInTerminal(ctx context.Context, terminalID int64, command string, timeoutSeconds int) (runtimecontext.ContainerTerminalExecResult, error)
ExecInTerminal 按终端会话主键执行命令并等待结束。
与 PersistentShell.Exec 对应:会话存活期间 cd / export / 已安装的包在多次调用之间保留。
func (*TerminalSessionRegistry) Execute ¶ added in v0.2.0
func (r *TerminalSessionRegistry) Execute(ctx context.Context, key TerminalSessionKey, cmd TerminalCommand) (TerminalCommandResult, error)
Execute 把命令写入会话的串行队列。
定位规则(见 ensure):
- key.ID > 0:直接投递到该终端会话;
- 否则按 (templateID, agentSessionID) 查找 Agent 会话的终端(agentSessionID=0 时新建);
- 会话不存在且 cmd.CreateIfMissing 时按 cmd.Persistent 创建。
Wait=false 时只投递并立即返回;Wait=true 时等待命令结束(靠 OSC 标记判定), 超时返回 TimedOut=true 与已产生的输出。
func (*TerminalSessionRegistry) Lookup ¶ added in v0.2.0
func (r *TerminalSessionRegistry) Lookup(key TerminalSessionKey) *ManagedTerminalSession
Lookup 返回存活会话(不创建)。
定位顺序:
- key.ID != 0:按终端会话主键查找;
- key.AgentSessionID > 0:按 (userID, projectID, templateID, agentSessionID) 查找 (Agent 侧只关心「这个 Agent 会话在该模板上的终端」,不关心会话 ID);
- 否则无法定位:会话以主键为唯一标识。
func (*TerminalSessionRegistry) LookupByID ¶ added in v0.2.0
func (r *TerminalSessionRegistry) LookupByID(id int64) *ManagedTerminalSession
LookupByID 按终端会话主键返回存活会话(不创建)。
新会话在落库前使用负数占位 ID,因此这里只拒绝 0。
func (*TerminalSessionRegistry) MarkPersistent ¶ added in v0.2.0
func (r *TerminalSessionRegistry) MarkPersistent(ctx context.Context, key TerminalSessionKey)
MarkPersistent 把会话标记为持久,并同步到持久层。
只会 false → true:已经持久的会话不会被改回一次性。
func (*TerminalSessionRegistry) ReviveByID ¶ added in v0.2.0
func (r *TerminalSessionRegistry) ReviveByID(ctx context.Context, id int64, rows, cols uint16) (*ManagedTerminalSession, error)
ReviveByID 按终端会话主键「重建」一个本进程里还不存在的会话。 典型场景:进程重启后会话(AppSession)与容器都还在,但 shell 随进程结束。 这里走与创建相同的 ensure 路径:能认领原容器就重新 exec 一个 shell(adoptSession), 容器已不存在则新建;历史输出由落盘的滚动缓冲回放。
rows/cols 是前端 xterm 当前尺寸:会话已存活时直接 Resize 到该尺寸; 需要重新 exec shell 时用它建 PTY。传 0 时保持原尺寸 / 使用默认 24x80。
返回 ErrTerminalSessionNotFound 表示记录不存在,由调用方报 404。 id 只拒绝 0(未分配):落库前的新会话使用负数占位 ID,它们同样可以被定位。
func (*TerminalSessionRegistry) RunInTerminal ¶ added in v0.2.0
func (r *TerminalSessionRegistry) RunInTerminal(ctx context.Context, in runtimecontext.ContainerTerminalExecInput) (runtimecontext.ContainerTerminalExecResult, error)
RunInTerminal 实现 runtimecontext.ContainerTerminalRunner,供 Agent 工具注入。
会话定位:按 (userID, projectID, templateID, agentSessionID) 查找该 Agent 会话在该模板上的 终端会话;不存在时新建(同一 agentSessionID + templateID 唯一),因此同一个 Agent 会话 多次调用 terminal_exec 会复用同一个容器与 PTY。
语义:
- 创建新会话时,按 in.Persistent 决定它是一次性还是持久会话;
- 命令结束(含超时 / 失败)后总是 Detach:一次性会话因此立即回收容器, 持久会话则保留,等待空闲 TTL 或用户显式关闭。
func (*TerminalSessionRegistry) SessionRecord ¶ added in v0.2.0
func (r *TerminalSessionRegistry) SessionRecord(ctx context.Context, id int64) (*types.AppSession, error)
SessionRecord 按终端会话主键返回持久层记录(不存在时返回 nil)。
供 handler 在内存注册表里找不到会话时做归属校验 / 判断会话是否仍然存在。
func (*TerminalSessionRegistry) StartMaintenance ¶ added in v0.2.0
func (r *TerminalSessionRegistry) StartMaintenance(ctx context.Context)
StartMaintenance 启动后台维护:空闲会话回收 + 跨进程残留对账。 传入的 ctx 取消时退出。
func (*TerminalSessionRegistry) Status ¶ added in v0.2.0
func (r *TerminalSessionRegistry) Status(key TerminalSessionKey) TerminalSessionStatus
Status 返回会话状态;不存在时 Exists=false。
type TerminalSessionStatus ¶ added in v0.2.0
type TerminalSessionStatus struct {
// SessionID 是终端会话主键(= types.AppSession.ID)的字符串形式,
// 也是 WS / exec / status / close 接口里使用的 {id}。
SessionID string `json:"session_id"`
Exists bool `json:"exists"`
Alive bool `json:"alive"`
Subscribers int `json:"subscribers"`
Seq int64 `json:"seq"`
// 以下字段只在「按用户列出会话」时填充,供左侧栏列表 join 容器模板展示。
UserID int64 `json:"user_id,omitempty,string"`
ProjectID int64 `json:"project_id,omitempty,string"`
TemplateID int64 `json:"template_id,omitempty,string"`
// AgentSessionID 是创建该终端会话的 Agent 会话 ID;0 表示 UI 手动创建。
AgentSessionID int64 `json:"agent_session_id,omitempty,string"`
// Name 是会话展示名(UI 创建时写入;Agent 创建时取模板展示名)。
Name string `json:"name,omitempty"`
// Persistent 为 true 表示会话只能由空闲 TTL 或显式关闭回收。
Persistent bool `json:"persistent"`
// Busy 表示当前有命令在执行(此时空闲 TTL 不会回收容器)。
Busy bool `json:"busy"`
// RuntimeID / RuntimeName 是底层容器标识(docker id / k8s workload),便于排查。
RuntimeID string `json:"runtime_id,omitempty"`
RuntimeName string `json:"runtime_name,omitempty"`
// CreatedAt 是会话在内存注册表里的创建时间;LastActive 是最近一次操作时间。
CreatedAt time.Time `json:"created_at,omitempty"`
LastActive time.Time `json:"last_active,omitempty"`
}
TerminalSessionStatus 是会话的只读状态。
type TerminalSessionStore ¶ added in v0.2.0
type TerminalSessionStore interface {
// Get 按会话 key 读取记录;不存在返回 (nil, nil)。
Get(ctx context.Context, key TerminalSessionKey) (*types.AppSession, error)
// GetByID 按会话主键读取记录;不存在返回 (nil, nil)。
GetByID(ctx context.Context, id int64) (*types.AppSession, error)
// FindAgentSession 按 (userID, projectID, templateID, agentSessionID) 查找 Agent 会话对应的终端会话;
// agentSessionID <= 0(UI 手动创建)时永远返回 (nil, nil)。
FindAgentSession(ctx context.Context, userID, projectID, templateID, agentSessionID int64) (*types.AppSession, error)
// Create 新增一条会话记录,并回填 rec.ID。
//
// name 只在创建时写入:之后 Update 不会再改它(DB 是 name 的唯一真相,
// 改名必须走独立接口)。
Create(ctx context.Context, rec *types.AppSession) error
// Update 按主键更新已存在记录的运行时字段(status / persistent / last_access_at 等),
// 不会修改 name。
Update(ctx context.Context, rec *types.AppSession) error
// Touch 刷新 last_access_at。
Touch(ctx context.Context, key TerminalSessionKey, at time.Time) error
// MarkPersistent 把会话标记为持久(幂等,只会 false → true)。
MarkPersistent(ctx context.Context, key TerminalSessionKey) error
// Delete 按会话 key 删除记录。
Delete(ctx context.Context, key TerminalSessionKey) error
// DeleteByID 按会话主键删除记录。
DeleteByID(ctx context.Context, id int64) error
// List 返回全部记录(用于启动/定期对账)。
List(ctx context.Context) ([]*types.AppSession, error)
// ListByUser 返回某用户的全部会话记录,供「终端会话列表」使用。
ListByUser(ctx context.Context, userID int64) ([]*types.AppSession, error)
}
TerminalSessionStore 是终端会话持久层的最小契约。
终端会话与「应用会话」是同一个实体(types.AppSession,表 go_container_app_session),这里只暴露注册表需要的能力:定位会话、落库 持久标记、刷新活跃时间、删除会话。注册表不依赖完整 repository 层, 单测可以用内存 fake 或直接传 nil。
会话的对外标识是它的主键 ID(= AppSession.ID),因此 Get / Delete / Touch / MarkPersistent 既可以按 ID,也可以按 (userID, projectID, templateID, agentSessionID) 定位。
type TerminalSubscriber ¶ added in v0.2.0
type TerminalSubscriber struct {
ID int64
Data chan []byte
// Events 承载 JSON 控制事件(exec start/end、exit 等),由 handler 以文本帧发送。
Events chan []byte
// contains filtered or unexported fields
}
TerminalSubscriber 是一个终端输出订阅者(浏览器 WebSocket)。
func (*TerminalSubscriber) Done ¶ added in v0.2.0
func (s *TerminalSubscriber) Done() <-chan struct{}
Done 在订阅者被关闭时关闭。
Source Files
¶
- agent_project_context.go
- ai_summary_content_provider.go
- ai_summary_event_handler.go
- ai_summary_worker.go
- app_session_event_handler.go
- container_create_worker.go
- container_lifecycle.go
- container_manager.go
- container_manager_runtime_reconciler.go
- container_runtime_resolver.go
- create_queue_status.go
- file_gid_unix.go
- outbox_dispatcher.go
- project_code.go
- project_context.go
- runtime_variable.go
- terminal_scrollback.go
- terminal_session.go
- terminal_session_store.go