tahamajs/IE / Projects /cli /agent.go
tahamajs's picture
download
raw
50.2 kB
package main
import (
"archive/zip"
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
)
type Message struct {
Role string `json:"role"`
Content interface{} `json:"content"` // can be string or null
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
}
type ToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
} `json:"function"`
}
var agentTools = []map[string]interface{}{
{
"type": "function",
"function": map[string]interface{}{
"name": "run_command",
"description": "Run a bash shell command on the user's local machine.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"command": map[string]interface{}{"type": "string", "description": "The bash command to run."}},
"required": []string{"command"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "execute_python",
"description": "Execute arbitrary Python 3 code for data processing, web scraping, or testing.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"code": map[string]interface{}{"type": "string", "description": "The Python code to execute."}},
"required": []string{"code"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "search_web",
"description": "Search the web using DuckDuckGo HTML parser to find current information.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"query": map[string]interface{}{"type": "string", "description": "The search query."}},
"required": []string{"query"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "list_directory",
"description": "List files and folders in a specific directory.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"path": map[string]interface{}{"type": "string", "description": "Path to list, e.g., '.'"}},
"required": []string{"path"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "read_file",
"description": "Read the contents of a local file.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"path": map[string]interface{}{"type": "string", "description": "Path to the file."}},
"required": []string{"path"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "write_file",
"description": "Write text to a local file (overwrites existing).",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"path": map[string]interface{}{"type": "string"}, "content": map[string]interface{}{"type": "string"}},
"required": []string{"path", "content"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "ask_user",
"description": "Pause execution and ask the user a question to get their input or permission.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"question": map[string]interface{}{"type": "string", "description": "The question to ask the user."}},
"required": []string{"question"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "call_python_agent",
"description": "Delegates a complex task to the Python LangGraph agent. Useful for advanced multi-step reasoning, LangGraph workflows, and complex tool orchestrations.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"prompt": map[string]interface{}{"type": "string", "description": "The specific task prompt for the Python agent."}},
"required": []string{"prompt"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "deploy_tool",
"description": "Deploys the tool in the current directory to the server using the CLI's deploy function.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "get_webpage_content",
"description": "Downloads a webpage and extracts clean, human-readable text.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"url": map[string]interface{}{"type": "string", "description": "The URL of the webpage."}},
"required": []string{"url"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "scaffold_tool_template",
"description": "Generate standard metadata and code files (tool.json and tool.html) based on category templates.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"name": map[string]interface{}{"type": "string", "description": "Name of the tool"},
"category": map[string]interface{}{"type": "string", "description": "Template category", "enum": []string{"data-visualization", "interactive-form", "calculator", "map-location"}},
},
"required": []string{"name", "category"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "validate_tool_schema",
"description": "Perform structural and type validation on tool.json structure.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.json (defaults to 'tool.json')"},
},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "lint_tool_html",
"description": "Audit tool.html script tags, layout, sandbox messaging protocol, and styling warnings.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.html (defaults to 'tool.html')"},
},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "add_tool_parameter",
"description": "Add a new configuration parameter dynamically to tool.json.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.json"},
"name": map[string]interface{}{"type": "string", "description": "Parameter identifier name"},
"paramType": map[string]interface{}{"type": "string", "description": "Type of parameter", "enum": []string{"string", "number", "boolean", "array", "object"}},
"label": map[string]interface{}{"type": "string", "description": "User friendly input label"},
"required": map[string]interface{}{"type": "boolean", "description": "Whether the parameter is required"},
},
"required": []string{"path", "name", "paramType", "label", "required"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "inject_javascript_library",
"description": "Embed a popular script/library CDN into tool.html header automatically.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.html"},
"libraryName": map[string]interface{}{"type": "string", "description": "Library to inject", "enum": []string{"TailwindCSS", "ChartJS", "KaTeX", "jQuery", "Bootstrap"}},
},
"required": []string{"path", "libraryName"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "generate_mock_payload",
"description": "Produce a mock postMessage payload matching parameters defined in tool.json.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.json"},
},
"required": []string{"path"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "preview_tool_in_dev",
"description": "Registers tool variables in the active dev server configuration for direct browser validation.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to current directory"},
},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "export_tool_package",
"description": "Package the local sandbox tool into a standard distributable ZIP package.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"dirPath": map[string]interface{}{"type": "string", "description": "Directory containing tool files"},
},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "optimize_tool_html",
"description": "Lint, format, and minify HTML and JavaScript inside tool.html.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to tool.html"},
},
"required": []string{"path"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "get_tool_templates_list",
"description": "Retrieve list of all available templates supported by scaffold_tool_template.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "modify_file_content",
"description": "Replaces a targeted block of text inside a file. Use this for precise edits.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"file_path": map[string]interface{}{"type": "string", "description": "Path to the file to modify"},
"target_text": map[string]interface{}{"type": "string", "description": "Exact text block to search and replace"},
"replacement_text": map[string]interface{}{"type": "string", "description": "The new text block to insert"},
},
"required": []string{"file_path", "target_text", "replacement_text"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "get_project_tree",
"description": "Recursively returns a visual tree structure of directories and files, ignoring build artifacts.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"root_path": map[string]interface{}{"type": "string", "description": "Root path to list (defaults to '.')"},
},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "search_in_files",
"description": "Searches files recursively for a string query.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"query": map[string]interface{}{"type": "string", "description": "The search term"},
"path": map[string]interface{}{"type": "string", "description": "Path to search in (defaults to '.')"},
},
"required": []string{"query"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "http_request",
"description": "Makes a network HTTP request (GET, POST, etc.) to query APIs or local dev servers.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"method": map[string]interface{}{"type": "string", "description": "HTTP Method, e.g., GET, POST"},
"url": map[string]interface{}{"type": "string", "description": "The full request URL"},
"headers": map[string]interface{}{"type": "object", "description": "Optional HTTP headers mapping"},
"json_body": map[string]interface{}{"type": "object", "description": "Optional JSON body payload"},
},
"required": []string{"method", "url"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "execute_go",
"description": "Execute arbitrary Go code by compilation and execution.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"code": map[string]interface{}{"type": "string", "description": "The Go code to run."}},
"required": []string{"code"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "find_files_by_name",
"description": "Finds files recursively by matching their name against a pattern (case-insensitive).",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"pattern": map[string]interface{}{"type": "string", "description": "The name pattern to match, e.g., *.go"},
"path": map[string]interface{}{"type": "string", "description": "Path to search in (defaults to '.')"},
},
"required": []string{"pattern"},
},
},
},
{
"type": "function",
"function": map[string]interface{}{
"name": "read_file_lines",
"description": "Reads specific lines from a file (1-indexed, inclusive). Use to read parts of large files.",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"path": map[string]interface{}{"type": "string", "description": "Path to file"},
"start_line": map[string]interface{}{"type": "integer", "description": "1-based start line index"},
"end_line": map[string]interface{}{"type": "integer", "description": "1-based end line index"},
},
"required": []string{"path", "start_line", "end_line"},
},
},
},
}
func startAgentSession() {
if parsedArgs.Has("--list-tools", "") {
fmt.Println(hdr("Registered Agent Tools"))
for _, tool := range agentTools {
fn, _ := tool["function"].(map[string]interface{})
name, _ := fn["name"].(string)
desc, _ := fn["description"].(string)
fmt.Printf(" • %s%-25s%s %s\n", Bold+Green, name, Reset, desc)
}
return
}
fmt.Println(hdr("MioBook Autonomous AI Agent"))
// Load key from config or environment
var apiKey string
cfg, errCfg := loadConfig()
if errCfg == nil && cfg != nil {
apiKey = cfg.OpenAIKey
}
if envKey := os.Getenv("OPENAI_API_KEY"); envKey != "" {
apiKey = envKey
}
if apiKey == "" {
fmt.Printf("%s[OPENAI KEY]%s OpenAI API Key is not set.\n", Yellow, Reset)
fmt.Printf("Please enter your OpenAI API Key: ")
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
apiKey = strings.TrimSpace(scanner.Text())
if apiKey != "" {
if cfg == nil {
cfg = &Config{Host: DefaultHost}
}
cfg.OpenAIKey = apiKey
saveConfig(cfg)
fmt.Printf("%s✔ API key successfully saved to configuration!%s\n\n", Green, Reset)
} else {
fmt.Println(err("API Key entry cancelled."))
return
}
}
}
fmt.Printf("%sWelcome! I am your autonomous AI assistant. I have advanced platform tool-making tools enabled!%s\n", Cyan, Reset)
fmt.Println("Type 'exit' to leave.")
systemPrompt := `You are an expert autonomous developer agent specializing in sandboxed React-like widgets and components.
You possess a suite of 10 powerful tool-making tools: scaffold_tool_template, validate_tool_schema, lint_tool_html, add_tool_parameter, inject_javascript_library, generate_mock_payload, preview_tool_in_dev, export_tool_package, optimize_tool_html, and get_tool_templates_list.
Use these specific tools directly when the user requests generating, validating, updating, or previewing extensions or widgets.
PROMPT ENGINEERING GUIDELINES FOR HTML SANDBOX CREATION (tool.html):
- Treat the single HTML file like a React/Vue application. Implement a reactive state object (e.g. const state = { data: [] };) and a render() function that updates the DOM dynamically when state changes.
- Always implement the sandboxed handshake protocol:
1. Listen for the 'init' postMessage event to retrieve inputs (window.addEventListener('message', (e) => { if (e.data.type === 'init') { ... } })).
2. Send a 'ready' postMessage to the parent immediately after load (window.parent.postMessage({ type: 'ready' }, '*')).
3. Send interactive responses or final values back to the parent using window.parent.postMessage({ type: 'submit' / 'result', data: ... }, '*').
- Apply modern, premium aesthetics:
- Import Tailwind CSS or define beautiful custom styles.
- Use elegant typography (e.g. Outfit, Inter or Roboto from Google Fonts).
- Use professional color palettes (dark modes, translucent backgrounds, glassmorphism).
- Add smooth CSS transitions and micro-animations for hovered elements.
- Never use raw default browser inputs/buttons; style them to look modern and premium.`
customSession := parsedArgs.Get("--session", "")
messages := loadSession(customSession)
if len(messages) == 0 {
messages = append(messages, Message{Role: "system", Content: systemPrompt})
} else {
fmt.Printf("%s[Shared Session] Loaded %d messages from history.%s\n", Green, len(messages), Reset)
}
// Single-shot prompt mode from CLI
if len(parsedArgs.Positional) > 1 {
prompt := strings.Join(parsedArgs.Positional[1:], " ")
fmt.Printf("%s[Single-Shot Mode] Prompt: %s%s\n", Cyan, prompt, Reset)
messages = append(messages, Message{Role: "user", Content: prompt})
processAgentTurn(apiKey, &messages)
saveSession(customSession, messages)
exportSession(&messages)
return
}
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("\n%s> %s", Green, Reset)
if !scanner.Scan() {
break
}
input := strings.TrimSpace(scanner.Text())
if input == "exit" || input == "quit" {
break
}
if input == "" {
continue
}
messages = append(messages, Message{Role: "user", Content: input})
processAgentTurn(apiKey, &messages)
saveSession(customSession, messages)
}
exportSession(&messages)
}
func processAgentTurn(apiKey string, messages *[]Message) {
for {
spin := startSpinner("Agent is thinking...")
respBytes, errAPI := callOpenAI(apiKey, *messages)
spin.Stop("")
if errAPI != nil {
fmt.Println(err("Error: " + errAPI.Error()))
return
}
var openAIResp map[string]interface{}
json.Unmarshal(respBytes, &openAIResp)
choices, _ := openAIResp["choices"].([]interface{})
choice := choices[0].(map[string]interface{})
msg := choice["message"].(map[string]interface{})
finishReason, _ := choice["finish_reason"].(string)
// Parse standard content
var contentStr string
if c, ok := msg["content"].(string); ok && c != "" {
contentStr = c
fmt.Printf("\n┌── %s🤖 AGENT RESPONSE%s ──────────────────────────────────────────────────\n", Purple+Bold, Reset)
lines := strings.Split(contentStr, "\n")
for _, line := range lines {
fmt.Printf("│ %s\n", line)
}
fmt.Printf("└──────────────────────────────────────────────────────────────────────────\n")
}
// Append assistant message
assistantMsg := Message{Role: "assistant", Content: msg["content"]}
// Check for tool calls
if finishReason == "tool_calls" || msg["tool_calls"] != nil {
toolCallsRaw, _ := json.Marshal(msg["tool_calls"])
var tCalls []ToolCall
json.Unmarshal(toolCallsRaw, &tCalls)
assistantMsg.ToolCalls = tCalls
*messages = append(*messages, assistantMsg)
for _, tc := range tCalls {
fmt.Printf("\n┌── %s⚙️ EXECUTING TOOL: %s%s ──────────────────────────────────────────────\n", Yellow+Bold, tc.Function.Name, Reset)
fmt.Printf("│ Arguments: %s\n", tc.Function.Arguments)
result := executeLocalTool(tc.Function.Name, tc.Function.Arguments)
truncated := result
if len(truncated) > 300 {
truncated = truncated[:300] + "... (truncated)"
}
truncated = strings.ReplaceAll(truncated, "\n", "\n│ ")
fmt.Printf("│ Result: %s\n", truncated)
fmt.Printf("└──────────────────────────────────────────────────────────────────────────\n")
*messages = append(*messages, Message{
Role: "tool",
ToolCallID: tc.ID,
Name: tc.Function.Name,
Content: result,
})
}
// Loop continues to send tool results back to OpenAI
} else {
*messages = append(*messages, assistantMsg)
break // conversation turn ended
}
}
}
func executeLocalTool(name, argsJSON string) string {
if !confirmTool(name, argsJSON) {
return "Error: User denied execution of this tool."
}
var args map[string]interface{}
json.Unmarshal([]byte(argsJSON), &args)
switch name {
case "run_command":
cmdStr, _ := args["command"].(string)
cmd := exec.Command("sh", "-c", cmdStr)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("Error: %v\nOutput: %s", err, string(out))
}
return string(out)
case "execute_python":
code, _ := args["code"].(string)
// Write to temp file
tmpFile := "temp_agent_script.py"
os.WriteFile(tmpFile, []byte(code), 0644)
defer os.Remove(tmpFile)
cmd := exec.Command("python3", tmpFile)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("Python Error: %v\nOutput: %s", err, string(out))
}
return string(out)
case "search_web":
query, _ := args["query"].(string)
// Assuming we can call duckduckgo locally like we do in commands.go
// We'll just run our own CLI command for web-search as a shortcut!
cmd := exec.Command("ie-cli", "web-search", "-q", query, "--json")
out, _ := cmd.CombinedOutput()
return string(out)
case "get_webpage_content":
url, _ := args["url"].(string)
return getWebpageContent(url)
case "list_directory":
path, _ := args["path"].(string)
entries, err := os.ReadDir(path)
if err != nil {
return err.Error()
}
var list []string
for _, e := range entries {
list = append(list, e.Name())
}
return strings.Join(list, "\n")
case "ask_user":
question, _ := args["question"].(string)
fmt.Printf("\n%s[Agent Question]%s %s\n> ", Yellow, Reset, question)
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
return scanner.Text()
}
return "No response"
case "read_file":
path, _ := args["path"].(string)
data, err := os.ReadFile(path)
if err != nil {
return err.Error()
}
return string(data)
case "write_file":
path, _ := args["path"].(string)
content, _ := args["content"].(string)
err := os.WriteFile(path, []byte(content), 0644)
if err != nil {
return err.Error()
}
return "Success"
case "call_python_agent":
prompt, _ := args["prompt"].(string)
return callPythonAgent(prompt)
case "deploy_tool":
os.Args = []string{"ie-cli", "deploy"}
handleDeploy()
return "Deploy command triggered locally. Check user console for results."
case "scaffold_tool_template":
n, _ := args["name"].(string)
cat, _ := args["category"].(string)
return scaffoldToolTemplate(n, cat)
case "validate_tool_schema":
p, _ := args["path"].(string)
return validateToolSchema(p)
case "lint_tool_html":
p, _ := args["path"].(string)
return lintToolHTML(p)
case "add_tool_parameter":
p, _ := args["path"].(string)
n, _ := args["name"].(string)
t, _ := args["paramType"].(string)
lbl, _ := args["label"].(string)
req, _ := args["required"].(bool)
return addToolParameter(p, n, t, lbl, req)
case "inject_javascript_library":
p, _ := args["path"].(string)
lib, _ := args["libraryName"].(string)
return injectJSClass(p, lib)
case "generate_mock_payload":
p, _ := args["path"].(string)
return generateMockPayload(p)
case "preview_tool_in_dev":
p, _ := args["path"].(string)
return previewToolDev(p)
case "export_tool_package":
dp, _ := args["dirPath"].(string)
return exportToolPackage(dp)
case "optimize_tool_html":
p, _ := args["path"].(string)
return optimizeToolHTML(p)
case "get_tool_templates_list":
return getToolTemplatesList()
case "modify_file_content":
path, _ := args["file_path"].(string)
target, _ := args["target_text"].(string)
replacement, _ := args["replacement_text"].(string)
data, err := os.ReadFile(path)
if err != nil {
return err.Error()
}
content := string(data)
if !strings.Contains(content, target) {
return fmt.Sprintf("Error: Target text not found in %s", path)
}
updated := strings.Replace(content, target, replacement, 1)
err = os.WriteFile(path, []byte(updated), 0644)
if err != nil {
return err.Error()
}
return "Success"
case "get_project_tree":
rootPath, _ := args["root_path"].(string)
if rootPath == "" {
rootPath = "."
}
var treeBuilder strings.Builder
treeBuilder.WriteString(rootPath + "\n")
ignored := map[string]bool{
".git": true, "node_modules": true, "target": true, ".gradle": true,
"bin": true, ".idea": true, "dist": true, "build": true,
}
var walk func(string, string)
walk = func(path string, prefix string) {
entries, err := os.ReadDir(path)
if err != nil {
return
}
var filtered []os.DirEntry
for _, entry := range entries {
if !ignored[entry.Name()] {
filtered = append(filtered, entry)
}
}
for i, entry := range filtered {
isLast := i == len(filtered)-1
connector := "├── "
if isLast {
connector = "└── "
}
treeBuilder.WriteString(prefix + connector + entry.Name() + "\n")
if entry.IsDir() {
newPrefix := prefix + "│ "
if isLast {
newPrefix = prefix + " "
}
walk(filepath.Join(path, entry.Name()), newPrefix)
}
}
}
walk(rootPath, "")
return treeBuilder.String()
case "search_in_files":
query, _ := args["query"].(string)
path, _ := args["path"].(string)
if path == "" {
path = "."
}
ignored := map[string]bool{
".git": true, "node_modules": true, "target": true, ".gradle": true,
"bin": true, ".idea": true, "dist": true, "build": true,
}
var results []string
err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() {
if ignored[info.Name()] {
return filepath.SkipDir
}
return nil
}
data, err := os.ReadFile(filePath)
if err != nil {
return nil
}
lines := strings.Split(string(data), "\n")
for i, line := range lines {
if strings.Contains(line, query) {
results = append(results, fmt.Sprintf("%s:%d: %s", filePath, i+1, strings.TrimSpace(line)))
if len(results) >= 50 {
return io.EOF // Stop walking
}
}
}
return nil
})
if err != nil && err != io.EOF {
return err.Error()
}
if len(results) == 0 {
return "No matches found."
}
ret := strings.Join(results, "\n")
if len(results) >= 50 {
ret += "\n\n(Truncated to 50 results)"
}
return ret
case "http_request":
method, _ := args["method"].(string)
url, _ := args["url"].(string)
headersRaw, _ := args["headers"].(map[string]interface{})
jsonBodyRaw, _ := args["json_body"].(map[string]interface{})
var body io.Reader
if jsonBodyRaw != nil {
bodyBytes, _ := json.Marshal(jsonBodyRaw)
body = bytes.NewBuffer(bodyBytes)
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return err.Error()
}
if jsonBodyRaw != nil {
req.Header.Set("Content-Type", "application/json")
}
for k, v := range headersRaw {
if vStr, ok := v.(string); ok {
req.Header.Set(k, vStr)
}
}
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Sprintf("Network request failed: %v", err)
}
defer resp.Body.Close()
respBytes, _ := io.ReadAll(resp.Body)
return fmt.Sprintf("Status Code: %d\nResponse:\n%s", resp.StatusCode, string(respBytes))
case "execute_go":
code, _ := args["code"].(string)
tmpFile := "temp_agent_script.go"
if !strings.HasPrefix(strings.TrimSpace(code), "package ") {
code = "package main\n\nimport \"fmt\"\n\nfunc main() {\n" + code + "\n}"
}
os.WriteFile(tmpFile, []byte(code), 0644)
defer os.Remove(tmpFile)
cmd := exec.Command("go", "run", tmpFile)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("Go Run Error: %v\nOutput: %s", err, string(out))
}
return string(out)
case "find_files_by_name":
pattern, _ := args["pattern"].(string)
path, _ := args["path"].(string)
if path == "" {
path = "."
}
ignored := map[string]bool{
".git": true, "node_modules": true, "target": true, ".gradle": true,
"bin": true, ".idea": true, "dist": true, "build": true,
}
var results []string
filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() && ignored[info.Name()] {
return filepath.SkipDir
}
name := strings.ToLower(info.Name())
pat := strings.ToLower(pattern)
matched := false
if strings.Contains(pat, "*") {
cleanPat := strings.ReplaceAll(pat, "*", "")
if strings.HasPrefix(pat, "*") && strings.HasSuffix(pat, "*") {
matched = strings.Contains(name, cleanPat)
} else if strings.HasPrefix(pat, "*") {
matched = strings.HasSuffix(name, cleanPat)
} else if strings.HasSuffix(pat, "*") {
matched = strings.HasPrefix(name, cleanPat)
} else {
matched = strings.Contains(name, cleanPat)
}
} else {
matched = strings.Contains(name, pat)
}
if matched {
results = append(results, filePath)
if len(results) >= 100 {
return io.EOF
}
}
return nil
})
if len(results) == 0 {
return "No matching files found."
}
return strings.Join(results, "\n")
case "read_file_lines":
path, _ := args["path"].(string)
start, _ := args["start_line"].(float64)
end, _ := args["end_line"].(float64)
data, err := os.ReadFile(path)
if err != nil {
return err.Error()
}
lines := strings.Split(string(data), "\n")
startIdx := int(start) - 1
endIdx := int(end)
if startIdx < 0 {
startIdx = 0
}
if endIdx > len(lines) {
endIdx = len(lines)
}
if startIdx >= len(lines) || startIdx > endIdx {
return "Error: Invalid line range."
}
return strings.Join(lines[startIdx:endIdx], "\n")
default:
return "Unknown tool"
}
}
func scaffoldToolTemplate(name, category string) string {
var toolJSON, toolHTML string
switch category {
case "data-visualization":
toolJSON = fmt.Sprintf(`{
"name": "%s",
"version": "1.0.0",
"description": "Dynamic Chart Visualization",
"entrypoint": "tool.html",
"parameters": [
{"name": "title", "type": "string", "label": "Chart Title", "required": true},
{"name": "values", "type": "string", "label": "Values (Comma Separated)", "required": true}
]
}`, name)
toolHTML = `<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap');
body { font-family: 'Outfit', sans-serif; background-color: #0f172a; color: #f8fafc; }
</style>
</head>
<body class="p-6">
<div id="root"></div>
<script type="text/babel">
function App() {
const [title, setTitle] = React.useState('Data Visualization');
const [values, setValues] = React.useState([10, 20, 30, 40]);
const chartRef = React.useRef(null);
const chartInstance = React.useRef(null);
React.useEffect(() => {
const handleMsg = (e) => {
if (e.data && e.data.type === 'init') {
const data = e.data.data || {};
if (data.title) setTitle(data.title);
if (data.values) {
const parsed = data.values.split(',').map(Number);
setValues(parsed);
}
}
};
window.addEventListener('message', handleMsg);
window.parent.postMessage({ type: 'ready' }, '*');
return () => window.removeEventListener('message', handleMsg);
}, []);
React.useEffect(() => {
if (!chartRef.current) return;
if (chartInstance.current) chartInstance.current.destroy();
const ctx = chartRef.current.getContext('2d');
chartInstance.current = new Chart(ctx, {
type: 'bar',
data: {
labels: values.map((_, i) => 'Item ' + (i + 1)),
datasets: [{
label: 'Values',
data: values,
backgroundColor: 'rgba(56, 189, 248, 0.5)',
borderColor: 'rgba(56, 189, 248, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { labels: { color: '#94a3b8' } }
},
scales: {
x: { grid: { color: '#334155' }, ticks: { color: '#94a3b8' } },
y: { grid: { color: '#334155' }, ticks: { color: '#94a3b8' } }
}
}
});
}, [title, values]);
return (
<div class="max-w-xl mx-auto bg-slate-800 border border-slate-700 rounded-2xl p-6 shadow-2xl">
<h2 class="text-xl font-bold mb-4 text-sky-400">{title}</h2>
<canvas ref={chartRef}></canvas>
<div class="mt-4 flex justify-between items-center">
<span class="text-xs text-slate-400">Powered by React & Tailwind</span>
<button
onClick={() => {
console.log("Submitting chart result...");
window.parent.postMessage({ type: 'submit', data: { title, values } }, '*');
}}
class="bg-sky-500 hover:bg-sky-600 text-slate-950 font-bold px-4 py-2 rounded-lg text-sm transition"
>
Submit Result
</button>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>`
case "interactive-form":
toolJSON = fmt.Sprintf(`{
"name": "%s",
"version": "1.0.0",
"description": "Dynamic Feedback Form",
"entrypoint": "tool.html",
"parameters": [
{"name": "title", "type": "string", "label": "Form Title", "required": true},
{"name": "placeholder", "type": "string", "label": "Input Placeholder", "required": false}
]
}`, name)
toolHTML = `<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-slate-50 p-6">
<div class="max-w-md mx-auto bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
<h2 id="form-title" class="text-lg font-semibold text-slate-800 mb-4">Feedback Form</h2>
<div class="space-y-4">
<input type="text" id="userInput" class="w-full px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Type here...">
<button onclick="submitFeedback()" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">Submit</button>
</div>
</div>
<script>
window.addEventListener('message', function(event) {
if (event.data.type === 'init') {
const payload = event.data.payload || {};
document.getElementById('form-title').innerText = payload.title || 'Form';
document.getElementById('userInput').placeholder = payload.placeholder || 'Type here...';
}
});
function submitFeedback() {
const val = document.getElementById('userInput').value;
window.parent.postMessage({
type: 'submit',
data: { value: val }
}, '*');
}
window.parent.postMessage({ type: 'ready' }, '*');
</script>
</body>
</html>`
default:
toolJSON = fmt.Sprintf(`{
"name": "%s",
"version": "1.0.0",
"description": "Simple mathematical operations",
"entrypoint": "tool.html",
"parameters": [
{"name": "operation", "type": "string", "label": "Operation (add, sub, mul, div)", "required": true}
]
}`, name)
toolHTML = `<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="p-6 bg-gray-100">
<div class="bg-white p-6 rounded-lg shadow-md max-w-sm mx-auto text-center">
<h2 class="text-xl font-bold mb-4">Smart Sandbox Calculator</h2>
<div class="flex justify-center gap-4 mb-4">
<input type="number" id="num1" value="10" class="w-20 px-2 py-1 border rounded text-center">
<span id="op-symbol" class="text-xl font-bold">+</span>
<input type="number" id="num2" value="5" class="w-20 px-2 py-1 border rounded text-center">
</div>
<button onclick="calculate()" class="bg-emerald-500 text-white px-4 py-2 rounded hover:bg-emerald-600">Calculate</button>
<div id="result" class="mt-4 text-2xl font-bold text-emerald-600">Result: 15</div>
</div>
<script>
let op = 'add';
window.addEventListener('message', function(event) {
if (event.data.type === 'init') {
op = event.data.payload.operation || 'add';
const symbols = { 'add': '+', 'sub': '-', 'mul': '*', 'div': '/' };
document.getElementById('op-symbol').innerText = symbols[op] || '+';
}
});
function calculate() {
const n1 = parseFloat(document.getElementById('num1').value);
const n2 = parseFloat(document.getElementById('num2').value);
let res = 0;
if (op === 'add') res = n1 + n2;
else if (op === 'sub') res = n1 - n2;
else if (op === 'mul') res = n1 * n2;
else if (op === 'div') res = n1 / n2;
document.getElementById('result').innerText = 'Result: ' + res;
window.parent.postMessage({ type: 'result', value: res }, '*');
}
window.parent.postMessage({ type: 'ready' }, '*');
</script>
</body>
</html>`
}
os.WriteFile("tool.json", []byte(toolJSON), 0644)
os.WriteFile("tool.html", []byte(toolHTML), 0644)
return fmt.Sprintf("Successfully scaffolded tool '%s' with category '%s' in current folder.", name, category)
}
func validateToolSchema(path string) string {
if path == "" {
path = "tool.json"
}
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading tool.json: %v", err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return fmt.Sprintf("JSON parse error: %v", err)
}
requiredFields := []string{"name", "version", "entrypoint", "parameters"}
for _, f := range requiredFields {
if _, exists := m[f]; !exists {
return fmt.Sprintf("Validation failed: missing required field '%s'", f)
}
}
return "Validation Succeeded: tool.json is well formed."
}
func lintToolHTML(path string) string {
if path == "" {
path = "tool.html"
}
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading html: %v", err)
}
content := string(data)
var warnings []string
if !strings.Contains(content, "window.addEventListener('message'") {
warnings = append(warnings, "Missing iframe handshake message listener: 'window.addEventListener(\"message\", ...)'")
}
if !strings.Contains(content, "window.parent.postMessage") {
warnings = append(warnings, "Missing message post back to parent page: 'window.parent.postMessage(...)'")
}
if !strings.Contains(content, "cdn.tailwindcss.com") && !strings.Contains(content, "<style>") {
warnings = append(warnings, "Recommendation: Use TailwindCSS or styles for a beautiful layout.")
}
if len(warnings) == 0 {
return "Lint Succeeded: No issues found. Extension is sandbox compatible."
}
return "Lint Warnings:\n- " + strings.Join(warnings, "\n- ")
}
func addToolParameter(path, name, paramType, label string, required bool) string {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading tool.json: %v", err)
}
var m map[string]interface{}
json.Unmarshal(data, &m)
params, _ := m["parameters"].([]interface{})
newParam := map[string]interface{}{
"name": name,
"type": paramType,
"label": label,
"required": required,
}
params = append(params, newParam)
m["parameters"] = params
updated, _ := json.MarshalIndent(m, "", " ")
os.WriteFile(path, updated, 0644)
return fmt.Sprintf("Successfully added parameter '%s' to '%s'.", name, path)
}
func injectJSClass(path, libName string) string {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading html: %v", err)
}
content := string(data)
var injection string
switch libName {
case "TailwindCSS":
injection = `<script src="https://cdn.tailwindcss.com"></script>`
case "ChartJS":
injection = `<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>`
case "KaTeX":
injection = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css"><script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>`
case "jQuery":
injection = `<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>`
case "Bootstrap":
injection = `<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">`
}
if strings.Contains(content, "</head>") {
content = strings.Replace(content, "</head>", injection+"\n</head>", 1)
} else if strings.Contains(content, "<body>") {
content = strings.Replace(content, "<body>", "<body>\n"+injection, 1)
} else {
content = injection + "\n" + content
}
os.WriteFile(path, []byte(content), 0644)
return fmt.Sprintf("Injected %s script tags successfully.", libName)
}
func generateMockPayload(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading tool.json: %v", err)
}
var m map[string]interface{}
json.Unmarshal(data, &m)
params, _ := m["parameters"].([]interface{})
mockData := make(map[string]interface{})
for _, p := range params {
paramMap, _ := p.(map[string]interface{})
name, _ := paramMap["name"].(string)
pType, _ := paramMap["type"].(string)
switch pType {
case "string":
mockData[name] = "Mock String Value"
case "number":
mockData[name] = 42
case "boolean":
mockData[name] = true
case "array":
mockData[name] = []string{"item1", "item2"}
case "object":
mockData[name] = map[string]interface{}{"key": "value"}
}
}
payload := map[string]interface{}{
"type": "init",
"payload": mockData,
}
res, _ := json.MarshalIndent(payload, "", " ")
return string(res)
}
func previewToolDev(path string) string {
cmd := exec.Command("ie-cli", "dev")
err := cmd.Start()
if err != nil {
return fmt.Sprintf("Failed to run local dev server: %v", err)
}
return "Preview Server started on http://localhost:3000. Access it now."
}
func exportToolPackage(dirPath string) string {
if dirPath == "" {
dirPath = "."
}
zipFile, err := os.Create("tool-package.zip")
if err != nil {
return fmt.Sprintf("Error creating zip: %v", err)
}
defer zipFile.Close()
w := zip.NewWriter(zipFile)
defer w.Close()
files := []string{"tool.json", "tool.html"}
for _, file := range files {
fPath := filepath.Join(dirPath, file)
f, err := os.Open(fPath)
if err != nil {
continue
}
zf, err := w.Create(file)
if err != nil {
continue
}
io.Copy(zf, f)
f.Close()
}
return "Tool package exported to tool-package.zip successfully."
}
func optimizeToolHTML(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("Error reading HTML: %v", err)
}
content := string(data)
lines := strings.Split(content, "\n")
var result []string
for _, l := range lines {
trimmed := strings.TrimSpace(l)
if trimmed != "" {
result = append(result, l)
}
}
os.WriteFile(path, []byte(strings.Join(result, "\n")), 0644)
return "HTML formatting cleaned up and optimized."
}
func getToolTemplatesList() string {
return "Templates List:\n1. data-visualization (TailwindCSS, Chart.js, init message bindings)\n2. interactive-form (Input submissions, postMessage bindings)\n3. calculator (Default basic tool framework)"
}
func callOpenAI(apiKey string, messages []Message) ([]byte, error) {
modelName := parsedArgs.Get("--model", "-m")
if modelName == "" {
modelName = "gpt-4o-mini"
}
payload := map[string]interface{}{
"model": modelName,
"messages": messages,
"temperature": 0.5,
"tools": agentTools,
}
payloadBytes, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(payloadBytes))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 120 * time.Second}
resp, errReq := client.Do(req)
if errReq != nil {
return nil, errReq
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return nil, fmt.Errorf("API Error (%d): %s", resp.StatusCode, string(bodyBytes))
}
return bodyBytes, nil
}
func runPythonAgent() {
args := []string{"cli/python_agent/agent.py"}
if len(os.Args) > 2 {
args = append(args, os.Args[2:]...)
}
cmd := exec.Command("python3", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Printf("%sError running Python LangGraph agent: %v%s\n", Red, err, Reset)
}
}
func confirmTool(name, argsJSON string) bool {
if parsedArgs.Has("--confirm", "-c") {
if name == "run_command" || name == "execute_python" || name == "execute_go" || name == "deploy_tool" || name == "write_file" || name == "modify_file_content" {
fmt.Printf("\n%s[Confirm Tool]%s Agent wants to execute %s with args: %s\n", Yellow, Reset, name, argsJSON)
confirm := ask("Allow execution? [y/N]: ")
return strings.ToLower(confirm) == "y"
}
}
return true
}
func exportSession(messages *[]Message) {
exportFile := parsedArgs.Get("--export", "-e")
if exportFile != "" {
historyBytes, _ := json.MarshalIndent(messages, "", " ")
os.WriteFile(exportFile, historyBytes, 0644)
fmt.Println(ok(fmt.Sprintf("Session history exported to %s", exportFile)))
}
}
func getSessionFilePath(customPath string) string {
if customPath != "" {
return customPath
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".ie-research-agent-session.json")
}
func loadSession(customPath string) []Message {
if parsedArgs.Has("--clear-session", "") {
return nil
}
path := getSessionFilePath(customPath)
data, err := os.ReadFile(path)
if err != nil {
return nil
}
var msgs []Message
if err := json.Unmarshal(data, &msgs); err == nil {
return msgs
}
return nil
}
func saveSession(customPath string, msgs []Message) {
path := getSessionFilePath(customPath)
data, err := json.MarshalIndent(msgs, "", " ")
if err == nil {
os.WriteFile(path, data, 0644)
}
}
func getWebpageContent(url string) string {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)")
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Sprintf("Error fetching webpage: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Sprintf("Error fetching webpage: status code %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Sprintf("Error reading response: %v", err)
}
// Strip script, style, and HTML tags
re := regexp.MustCompile("(?i)<script[^>]*>[\\s\\S]*?</script>|<style[^>]*>[\\s\\S]*?</style>|<[^>]+>")
plainText := re.ReplaceAllString(string(body), " ")
// Collapse multiple spaces/newlines
reSpaces := regexp.MustCompile(`\s+`)
plainText = reSpaces.ReplaceAllString(plainText, " ")
plainText = strings.TrimSpace(plainText)
if len(plainText) > 10000 {
return plainText[:10000] + "\n\n(Truncated...)"
}
return plainText
}
func callPythonAgent(prompt string) string {
sessionPath := parsedArgs.Get("--session", "")
pythonScript := "cli/python_agent/agent.py"
if _, err := os.Stat(pythonScript); os.IsNotExist(err) {
pythonScript = "python_agent/agent.py"
}
cmdArgs := []string{pythonScript, "agent", prompt}
if sessionPath != "" {
cmdArgs = append(cmdArgs, "--session", sessionPath)
}
modelName := parsedArgs.Get("--model", "-m")
if modelName != "" {
cmdArgs = append(cmdArgs, "--model", modelName)
}
fmt.Printf("\n%s[Go Agent] Invoking Python LangGraph Agent with prompt: \"%s\"...%s\n", Yellow+Bold, prompt, Reset)
cmd := exec.Command("python3", cmdArgs...)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("Python agent execution failed: %v\nOutput: %s", err, string(out))
}
fmt.Printf("%s[Go Agent] Python Agent execution complete.%s\n", Green+Bold, Reset)
return string(out)
}

Xet Storage Details

Size:
50.2 kB
·
Xet hash:
d9cc94cd406f3c73253ed87139301eb429afc77dc7afc401cce8ad42ee0f7d18

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.