// claude_api — 呼叫 Mira daemon(Hetzner 上跑的 Claude Agent SDK 服務) // // 架構決策(2026-05-06): // 不直打 Anthropic Messages API(OAuth token 限制 system prompt 角色 → rate_limit_error) // 改透過已部署的 cloud-cto Mira daemon (https://mira.uncle6.me/mira/execute) // 該 daemon 用 Claude Agent SDK,已內建 Mira persona,可長執行任務 // // SDD: polaris/mira/.agents/specs/mira-app/design.md §6(五個 P0 零件) // //go:build tinygo package main import ( "encoding/json" "io" "os" "unsafe" ) //go:wasmimport u6u http_request func hostHttpRequest( urlPtr uintptr, urlLen uint32, methodPtr uintptr, methodLen uint32, headersPtr uintptr, headersLen uint32, bodyPtr uintptr, bodyLen uint32, outPtr uintptr, outLenPtr uintptr, ) uint32 type Input struct { MiraURL string `json:"mira_url"` // 預設 https://mira.uncle6.me MiraToken string `json:"mira_token"` // Mira daemon Bearer token Prompt string `json:"prompt"` // 必填:要傳給 Mira 的訊息 TimeoutMS int `json:"timeout_ms"` // 預設 25000(daemon 協商模式上限) Model string `json:"model"` // 'haiku' / 'sonnet' / 'opus',預設 haiku(daemon 端) CallbackURL string `json:"callback_url"` // optional:daemon 完成 task 時 POST 此 URL 通知(Resumable workflow,SDD: resumable-workflow) } var dummy [1]byte func safePtr(b []byte) (uintptr, uint32) { if len(b) == 0 { return uintptr(unsafe.Pointer(&dummy[0])), 0 } return uintptr(unsafe.Pointer(&b[0])), uint32(len(b)) } func main() { raw, err := io.ReadAll(os.Stdin) if err != nil { writeError("failed to read stdin: " + err.Error()) return } var input Input if err := json.Unmarshal(raw, &input); err != nil { writeError("invalid input JSON: " + err.Error()) return } if input.MiraToken == "" { writeError("mira_token 必填(Mira daemon Bearer token)") return } if input.Prompt == "" { writeError("prompt 必填") return } miraURL := input.MiraURL if miraURL == "" { miraURL = "https://mira.uncle6.me" } timeoutMS := input.TimeoutMS if timeoutMS <= 0 { // 預設 120s:daemon 協商期會在 25s 切非同步 + callback; // callback_url 存在時,timeout 上限不重要(daemon 會 fire callback 不論多久) timeoutMS = 120000 } // Mira daemon /execute 介面 body := map[string]interface{}{ "prompt": input.Prompt, "timeout_ms": timeoutMS, } if input.Model != "" { body["model"] = input.Model } if input.CallbackURL != "" { body["callback_url"] = input.CallbackURL } bodyBytes, _ := json.Marshal(body) headers := map[string]string{ "Content-Type": "application/json", "Authorization": "Bearer " + input.MiraToken, } headersBytes, _ := json.Marshal(headers) url := miraURL + "/mira/execute" urlBytes := []byte(url) methodBytes := []byte("POST") outBuf := make([]byte, 1024*1024) // 1MB var outLen uint32 urlPtr, urlLen := safePtr(urlBytes) methodPtr, methodLen := safePtr(methodBytes) headersPtr, headersLen := safePtr(headersBytes) bodyPtr, bodyLenU := safePtr(bodyBytes) result := hostHttpRequest( urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLenU, uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)), ) if result != 0 { writeError("Mira daemon request failed (host_http_request returned non-zero)") return } respStr := string(outBuf[:outLen]) var resp map[string]interface{} if err := json.Unmarshal([]byte(respStr), &resp); err != nil { writeError("Mira returned non-JSON: " + respStr) return } // 偵測錯誤回應 if errObj, hasErr := resp["error"]; hasErr { errBytes, _ := json.Marshal(errObj) writeError("Mira error: " + string(errBytes)) return } // daemon 回應格式: // 同步完成: {"task_id":"...","status":"done","output":"...","model":"..."} // 非同步: {"task_id":"...","status":"running","estimated_seconds":N} status, _ := resp["status"].(string) if status == "running" { // 還沒完成,回傳 task_id 給 caller 自己 polling out, _ := json.Marshal(map[string]interface{}{ "success": true, "pending": true, "task_id": resp["task_id"], "estimated_seconds": resp["estimated_seconds"], "poll_url": miraURL + "/mira/execute/" + toString(resp["task_id"]), }) os.Stdout.Write(out) return } // status == "done" 的場景 out := map[string]interface{}{ "success": true, "data": map[string]interface{}{ "text": resp["output"], "task_id": resp["task_id"], "model": resp["model"], }, } outJSON, _ := json.Marshal(out) os.Stdout.Write(outJSON) } func toString(v interface{}) string { if s, ok := v.(string); ok { return s } return "" } func writeError(msg string) { out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg}) os.Stdout.Write(out) }