159 lines
4.4 KiB
Go
159 lines
4.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
|
|
"iop/apps/node/internal/runtime"
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
var antigravityConversationPatterns = []*regexp.Regexp{
|
|
regexp.MustCompile(`(?m)Created conversation (` + uuidPattern + `)`),
|
|
regexp.MustCompile(`(?m)Streaming conversation (` + uuidPattern + `)`),
|
|
regexp.MustCompile(`(?m)Print mode: conversation=(` + uuidPattern + `)`),
|
|
regexp.MustCompile(`(?m)conversationID="(` + uuidPattern + `)"`),
|
|
}
|
|
|
|
const uuidPattern = `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`
|
|
|
|
func (e *antigravityExecutor) Execute(ctx context.Context, spec runtime.ExecutionSpec, profile config.CLIProfileConf, sink runtime.EventSink) error {
|
|
if len(profile.ResumeArgs) == 0 {
|
|
return fmt.Errorf("cli adapter: antigravity-print mode requires resume_args in profile %q", spec.Target)
|
|
}
|
|
|
|
sess, err := e.resolveAntigravitySession(spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sess.mu.Lock()
|
|
defer sess.mu.Unlock()
|
|
|
|
logFile, cleanup, err := createAntigravityLogFile()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cleanup()
|
|
|
|
prompt := extractPrompt(spec.Input)
|
|
args := antigravityPrintArgs(profile, sess.conversationID, logFile, prompt)
|
|
output, err := e.cli.executeCommand(ctx, spec, profile, args, prompt, sink)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logOutput, readErr := os.ReadFile(logFile)
|
|
if readErr != nil {
|
|
return fmt.Errorf("cli adapter: read antigravity log: %w", readErr)
|
|
}
|
|
if conversationID := parseAntigravityConversationID(string(logOutput) + output); conversationID != "" {
|
|
sess.conversationID = conversationID
|
|
}
|
|
if sess.conversationID == "" {
|
|
return fmt.Errorf("cli adapter: antigravity-print did not report a conversation id")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *antigravityExecutor) resolveAntigravitySession(spec runtime.ExecutionSpec) (*antigravitySession, error) {
|
|
target := cliTargetName(spec)
|
|
key := newSessionKey(spec)
|
|
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if sess, ok := e.sessions[key]; ok {
|
|
return sess, nil
|
|
}
|
|
if spec.SessionMode == runtime.SessionModeRequireExisting {
|
|
return nil, fmt.Errorf("cli adapter: no antigravity conversation for target %q session %q", target, key.sessionID)
|
|
}
|
|
sess := &antigravitySession{key: key}
|
|
e.sessions[key] = sess
|
|
return sess, nil
|
|
}
|
|
|
|
func (e *antigravityExecutor) Sessions() []sessionListEntry {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
snaps := make([]sessionListEntry, 0, len(e.sessions))
|
|
for k := range e.sessions {
|
|
snaps = append(snaps, sessionListEntry{"antigravity-print", k.target, k.sessionID, k.workspace})
|
|
}
|
|
return snaps
|
|
}
|
|
|
|
// Terminate removes every workspace variant of the given target/sessionID.
|
|
func (e *antigravityExecutor) Terminate(_ context.Context, target, sessionID string) (bool, error) {
|
|
sid := normalizeSessionID(sessionID)
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
removed := false
|
|
for k := range e.sessions {
|
|
if k.target == target && k.sessionID == sid {
|
|
delete(e.sessions, k)
|
|
removed = true
|
|
}
|
|
}
|
|
return removed, nil
|
|
}
|
|
|
|
func (e *antigravityExecutor) Stop(ctx context.Context) error {
|
|
e.mu.Lock()
|
|
e.sessions = make(map[sessionKey]*antigravitySession)
|
|
e.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func antigravityPrintArgs(profile config.CLIProfileConf, conversationID, logFile, prompt string) []string {
|
|
args := []string{"--log-file", logFile}
|
|
if conversationID == "" {
|
|
args = append(args, profile.Args...)
|
|
} else {
|
|
args = append(args, removeAntigravityPrintFlag(profile.ResumeArgs)...)
|
|
args = append(args, conversationID)
|
|
args = append(args, "--print")
|
|
}
|
|
return append(args, prompt)
|
|
}
|
|
|
|
func removeAntigravityPrintFlag(args []string) []string {
|
|
filtered := make([]string, 0, len(args))
|
|
for _, arg := range args {
|
|
switch arg {
|
|
case "--print", "--prompt", "-p":
|
|
continue
|
|
default:
|
|
filtered = append(filtered, arg)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func createAntigravityLogFile() (string, func(), error) {
|
|
f, err := os.CreateTemp("", "iop-antigravity-*.log")
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("cli adapter: create antigravity log file: %w", err)
|
|
}
|
|
path := f.Name()
|
|
if err := f.Close(); err != nil {
|
|
_ = os.Remove(path)
|
|
return "", nil, fmt.Errorf("cli adapter: close antigravity log file: %w", err)
|
|
}
|
|
return path, func() { _ = os.Remove(path) }, nil
|
|
}
|
|
|
|
func parseAntigravityConversationID(s string) string {
|
|
var id string
|
|
for _, re := range antigravityConversationPatterns {
|
|
for _, match := range re.FindAllStringSubmatch(s, -1) {
|
|
if len(match) >= 2 {
|
|
id = match[1]
|
|
}
|
|
}
|
|
}
|
|
return id
|
|
}
|