arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。 此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在 richblack/arcrun 與本地 backup 分支)。含: - acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe) - recipe push 把關(資料外流提醒 + 打通檢查) - 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1) - CLI / cypher-executor / registry / 完整 SDD Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user