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:
uncle6me-web
2026-06-03 15:52:38 +08:00
commit 922a57fe34
485 changed files with 89356 additions and 0 deletions
@@ -0,0 +1,75 @@
canonical_id: "claude_api"
display_name: "Claude AI 對話"
category: "ai"
version: "v2"
wasi_target: "preview1"
stability: "floating"
runtime_compat:
- "cf-workers"
- "workerd"
- "wazero"
constraints:
max_size_kb: 2048
max_cold_start_ms: 80
no_network_syscall: false
no_filesystem_syscall: true
io_model: "stdin_stdout_json"
input_schema:
type: object
required: [mira_token, prompt]
properties:
mira_token:
type: string
description: Mira daemon Bearer tokenHetzner cloud-cto Mira daemon 的 MIRA_TOKEN
prompt:
type: string
description: 要送給 Mira 的訊息(已內建 Mira 副駕 persona,不需重複設角色)
mira_url:
type: string
description: Mira daemon URL,預設 https://mira.uncle6.me
default: "https://mira.uncle6.me"
timeout_ms:
type: integer
description: Daemon 協商模式 timeout,預設 25000ms(協商上限)
default: 25000
output_schema:
type: object
properties:
success:
type: boolean
data:
type: object
description: 同步完成時的回應
properties:
text: { type: string, description: Mira 的回覆文字 }
task_id: { type: string }
model: { type: string, description: 「實際 routing 用的模型(haiku / sonnet)」 }
pending:
type: boolean
description: 「true 時表示 daemon 切到非同步模式,task 還在跑,需 polling」
task_id:
type: string
description: pending=true 時用此 id polling
poll_url:
type: string
description: GET 此 URL 查詢任務進度 / 結果
error:
type: string
gherkin_tests:
- scenario: "缺 mira_token"
given: '{"prompt":"hi"}'
then_contains: '{"success":false'
- scenario: "簡短對話 25s 內回完"
given: '{"mira_token":"...","prompt":"1+1=?"}'
then_contains: 'success'
tags: [ai, llm, claude, mira, primitive]
description: "呼叫 Mira daemon (Hetzner cloud-cto) 進行 AI 對話。Daemon 內部用 Claude Agent SDK,內建 Mira 副駕 persona,可長執行任務。所有 mira-app 的 AI workflow(自動回覆、wiki 合成、新聞註解)都用此零件。"
config_example: |
ai_reply:
mira_token: "{{secret.mira_token}}"
prompt: |
用戶 leo 在 mira 河道發了這則貼文:
「{{trigger.post_content}}」
請以副駕 AI 的身份留言回應,簡短繁中,務實。
timeout_ms: 25000
+3
View File
@@ -0,0 +1,3 @@
module claude_api
go 1.21
+180
View File
@@ -0,0 +1,180 @@
// claude_api — 呼叫 Mira daemonHetzner 上跑的 Claude Agent SDK 服務)
//
// 架構決策(2026-05-06):
// 不直打 Anthropic Messages APIOAuth 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"` // 預設 25000daemon 協商模式上限)
Model string `json:"model"` // 'haiku' / 'sonnet' / 'opus',預設 haikudaemon 端)
CallbackURL string `json:"callback_url"` // optionaldaemon 完成 task 時 POST 此 URL 通知(Resumable workflowSDD: 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 {
// 預設 120sdaemon 協商期會在 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)
}