iop/apps/node/internal/adapters/cli/codex_exec.go
toki d764adb390 chore: agent-task 변경 사항 반영
- edge node store 및 console 업데이트
- node adapters(cli, ollama, vllm, mock) 리팩토링
- node router, run_manager, store 개선
- proto 파일(control.proto, runtime.proto) 및 생성 코드 업데이트
- config 업데이트
2026-05-10 20:41:33 +09:00

88 lines
2.4 KiB
Go

package cli
import (
"context"
"encoding/json"
"fmt"
"regexp"
"iop/apps/node/internal/runtime"
"iop/packages/config"
)
var (
codexSessionIDPattern = regexp.MustCompile(`(?mi)^session id:\s*(\S+)\s*$`)
codexThreadStartedRegex = regexp.MustCompile(`\{[^{}]*"type"\s*:\s*"thread\.started"[^{}]*\}`)
)
func (c *CLI) executeCodexExec(ctx context.Context, spec runtime.ExecutionSpec, profile config.CLIProfileConf, sink runtime.EventSink) error {
if profile.ResumeArgs == nil {
return fmt.Errorf("cli adapter: codex-exec mode requires resume_args in profile %q", spec.Target)
}
sess, err := c.resolveCodexExecSession(spec)
if err != nil {
return err
}
sess.mu.Lock()
defer sess.mu.Unlock()
prompt := extractPrompt(spec.Input)
args := codexExecArgs(profile, sess.externalID, prompt)
output, err := c.executeCommand(ctx, spec, profile, args, prompt, sink)
if err != nil {
return err
}
if externalID := parseCodexSessionID(output); externalID != "" {
sess.externalID = externalID
}
if sess.externalID == "" {
return fmt.Errorf("cli adapter: codex exec did not report a session id")
}
return nil
}
func (c *CLI) resolveCodexExecSession(spec runtime.ExecutionSpec) (*codexExecSession, error) {
agent := cliAgentName(spec)
key := sessionKey{agent: agent, sessionID: normalizeSessionID(spec.SessionID)}
c.mu.Lock()
defer c.mu.Unlock()
if sess, ok := c.codexSessions[key]; ok {
return sess, nil
}
if spec.SessionMode == runtime.SessionModeRequireExisting {
return nil, fmt.Errorf("cli adapter: no persistent session for agent %q session %q", agent, key.sessionID)
}
sess := &codexExecSession{key: key}
c.codexSessions[key] = sess
return sess, nil
}
func codexExecArgs(profile config.CLIProfileConf, externalSessionID, prompt string) []string {
if externalSessionID == "" {
args := append([]string{}, profile.Args...)
return append(args, prompt)
}
args := append([]string{}, profile.ResumeArgs...)
return append(args, externalSessionID, prompt)
}
func parseCodexSessionID(output string) string {
if matches := codexSessionIDPattern.FindStringSubmatch(output); len(matches) >= 2 {
return matches[1]
}
for _, raw := range codexThreadStartedRegex.FindAllString(output, -1) {
var ev struct {
Type string `json:"type"`
ThreadID string `json:"thread_id"`
}
if err := json.Unmarshal([]byte(raw), &ev); err == nil && ev.Type == "thread.started" && ev.ThreadID != "" {
return ev.ThreadID
}
}
return ""
}