package main import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "strings" "time" ) const defaultAPIKey = "opx-57ffcb013651f7acae558cd9ab094dbd719fdec980a6ad53" type visionRequest struct { Model string `json:"model"` Messages []visionMessage `json:"messages"` Stream bool `json:"stream"` } type visionMessage struct { Role string `json:"role"` Content interface{} `json:"content"` } type visionResponse struct { ID string `json:"id"` Object string `json:"object"` Created int64 `json:"created"` Model string `json:"model"` Choices []visionChoice `json:"choices"` } type visionChoice struct { Index int `json:"index"` Message visionContent `json:"message"` FinishReason string `json:"finish_reason"` } type visionContent struct { Role string `json:"role"` Content interface{} `json:"content"` } func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "Usage: vision [prompt]\n") fmt.Fprintf(os.Stderr, "Example: vision C:\\path\\to\\photo.jpg \"Describe this image\"\n") os.Exit(1) } imagePath := os.Args[1] prompt := "Describe esta imagen en detalle." if len(os.Args) > 2 { prompt = strings.Join(os.Args[2:], " ") } ext := strings.ToLower(filepath.Ext(imagePath)) var mediaType string switch ext { case ".jpg", ".jpeg": mediaType = "image/jpeg" case ".png": mediaType = "image/png" case ".webp": mediaType = "image/webp" default: fmt.Fprintf(os.Stderr, "Unsupported image format: %s (supported: jpg, png, webp)\n", ext) os.Exit(1) } data, err := os.ReadFile(imagePath) if err != nil { fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err) os.Exit(1) } b64 := base64.StdEncoding.EncodeToString(data) dataURL := fmt.Sprintf("data:%s;base64,%s", mediaType, b64) reqBody := visionRequest{ Model: "mimo-v2.5-free", Messages: []visionMessage{ { Role: "user", Content: []map[string]interface{}{ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": map[string]string{"url": dataURL}}, }, }, }, } body, _ := json.Marshal(reqBody) httpReq, err := http.NewRequest("POST", "https://opencode.ai/zen/v1/chat/completions", bytes.NewReader(body)) if err != nil { fmt.Fprintf(os.Stderr, "Error creating request: %v\n", err) os.Exit(1) } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+defaultAPIKey) httpReq.Header.Set("User-Agent", "vision-tool/1.0") httpReq.Header.Set("x-opencode-client", "cli") httpReq.Header.Set("x-opencode-project", "vision") httpReq.Header.Set("x-opencode-request", fmt.Sprintf("req_%d", time.Now().UnixNano())) httpReq.Header.Set("x-opencode-session", fmt.Sprintf("ses_%d", time.Now().UnixNano())) client := &http.Client{Timeout: 2 * time.Minute} resp, err := client.Do(httpReq) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } defer resp.Body.Close() respData, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { fmt.Fprintf(os.Stderr, "API error %d: %s\n", resp.StatusCode, string(respData)) os.Exit(1) } var result visionResponse if err := json.Unmarshal(respData, &result); err != nil { fmt.Fprintf(os.Stderr, "Error parsing response: %v\n%s\n", err, string(respData)) os.Exit(1) } if len(result.Choices) > 0 { switch c := result.Choices[0].Message.Content.(type) { case string: fmt.Println(c) default: b, _ := json.MarshalIndent(c, "", " ") fmt.Println(string(b)) } } }