feat(arcrun): http_request body_json + error heuristic; mira feed fire-and-forget

http_request 零件擴展(registry/components/http_request):
- 加 body_json 物件欄位(內部 JSON.stringify),yaml 端不用手組 JSON 字串
- 新增 JSON 回應的 error 欄位偵測 → 若 body 含 `{"error":"..."}` 則零件回 success=false
  解 cascade bug:mira_feed_watcher 用 http_request trigger wiki_synthesis,
  原本 4xx response 也被當 success,ON_SUCCESS 鏈會誤觸發
  根因架構債:host fn 沒回 HTTP status code(arcrun.md 列為 P1 follow-up)

landing 河道 feed (landing/app/mira/feed/page.tsx):
- 加回 triggerWikiSynthesis fire-and-forget 對 cypher.arcrun.dev/webhooks/named/
  wiki_synthesis/trigger 公開觸發 endpoint(arcrun-native,非 mira-specific route)
- 不走 watcher 是因為 cypher-executor 自己 fetch 自己 workers.dev URL = CF 1042
  self-fetch 擋

watcher 仍存在當 cron backup,但目前因 self-fetch 1042 不會真正觸發下游
wiki_synthesis(arcrun.md 列為 P1 follow-up)。
This commit is contained in:
2026-05-14 16:06:46 +08:00
parent 8ab6f8a66b
commit bc6360ccfc
3 changed files with 65 additions and 8 deletions
@@ -31,7 +31,11 @@ input_schema:
additionalProperties:
type: string
body:
description: 請求 body(任意 JSON
type: string
description: 模式 A — body 字串(自行 stringify 後傳)
body_json:
type: object
description: 模式 B — body 物件,零件內部 JSON.stringifyyaml 端不用手組字串)
output_schema:
type: object
properties:
+33 -5
View File
@@ -24,10 +24,11 @@ func hostHttpRequest(
) uint32
type Input struct {
URL string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Body string `json:"body"`
URL string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Body string `json:"body"` // 模式 A:直接 string body
BodyJSON map[string]interface{} `json:"body_json"` // 模式 B:物件,內部 stringify(避免 yaml 端要自己組 JSON 字串)
}
// dummy byte for safe zero-length unsafe.Pointer operations
@@ -71,10 +72,19 @@ func main() {
headersJSON = string(b)
}
// body 來源優先順序:body_json(物件 → JSON 字串)> body(直接 string
bodyStr := input.Body
if input.BodyJSON != nil {
b, err := json.Marshal(input.BodyJSON)
if err == nil {
bodyStr = string(b)
}
}
urlBytes := []byte(input.URL)
methodBytes := []byte(method)
headersBytes := []byte(headersJSON)
bodyBytes := []byte(input.Body)
bodyBytes := []byte(bodyStr)
outBuf := make([]byte, 65536) // 64KB output buffer
var outLen uint32
@@ -97,6 +107,24 @@ func main() {
}
responseStr := string(outBuf[:outLen])
// 2026-05-14:偵測 JSON `{"error":"..."}` 模式視為 4xx 失敗
// 理由:host function 沒回 HTTP status code(架構債),先用 body 啟發式 catch。
// 標準 APIcypher-executor / KBDB / 多數 REST)失敗時都回 {"error":...} JSON。
// 對應 SDD: arcrun.md 三-A P1 #4「http_request status code 缺乏 surface」。
var parsed map[string]interface{}
if err := json.Unmarshal([]byte(responseStr), &parsed); err == nil {
if errVal, ok := parsed["error"]; ok && errVal != nil {
out, _ := json.Marshal(map[string]interface{}{
"success": false,
"error": errVal,
"data": map[string]interface{}{"body": responseStr},
})
os.Stdout.Write(out)
return
}
}
out, _ := json.Marshal(map[string]interface{}{
"success": true,
"data": map[string]interface{}{"body": responseStr},