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,67 @@
canonical_id: "filter"
display_name: "過濾陣列"
category: "logic"
version: "v1"
wasi_target: "preview1"
stability: "floating"
runtime_compat:
- "cf-workers"
- "workerd"
- "wazero"
constraints:
max_size_kb: 2048
max_cold_start_ms: 50
no_network_syscall: true
no_filesystem_syscall: true
io_model: "stdin_stdout_json"
input_schema:
type: object
required: [items, condition]
properties:
items:
type: array
description: 要過濾的陣列
condition:
type: object
required: [key, op, value]
properties:
key:
type: string
description: 要比較的欄位名稱
op:
type: string
enum: [eq, ne, gt, lt, contains]
value:
type: string
description: 比較值
output_schema:
type: object
properties:
success:
type: boolean
data:
type: object
properties:
items:
type: array
count:
type: number
gherkin_tests:
- scenario: "過濾 status=active 的元素"
given: '{"items":[{"status":"active"},{"status":"inactive"}],"condition":{"key":"status","op":"eq","value":"active"}}'
then_contains: '{"success":true'
- scenario: "空陣列輸入"
given: '{"items":[],"condition":{"key":"status","op":"eq","value":"active"}}'
then_contains: '{"success":true'
- scenario: "缺少 condition.key"
given: '{"items":[],"condition":{"op":"eq","value":"x"}}'
then_contains: '{"success":false'
tags: [builtin, filter, array, condition]
description: "依條件過濾陣列,回傳符合條件的元素。支援 eq/ne/gt/lt/contains 運算子。"
config_example: |
my_filter: # 節點名稱(可自訂)
items: "{{upstream.results}}" # 要過濾的陣列(必填)
condition: # 過濾條件(必填)
key: status # 要比較的欄位名稱(必填)
op: eq # 運算子:eq / ne / gt / lt / contains(必填)
value: active # 比較值(必填)
+3
View File
@@ -0,0 +1,3 @@
module component
go 1.21
+122
View File
@@ -0,0 +1,122 @@
// filter — 依條件過濾陣列
// op 支援: eq, ne, gt, lt, contains
//
//go:build tinygo
package main
import (
"encoding/json"
"io"
"os"
"strconv"
"strings"
)
type Condition struct {
Key string `json:"key"`
Op string `json:"op"`
Value string `json:"value"`
}
type Input struct {
Items []json.RawMessage `json:"items"`
Condition Condition `json:"condition"`
}
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.Condition.Key == "" {
writeError("condition.key 必填")
return
}
var filtered []json.RawMessage
for _, item := range input.Items {
var obj map[string]json.RawMessage
if err := json.Unmarshal(item, &obj); err != nil {
continue
}
fieldRaw, ok := obj[input.Condition.Key]
if !ok {
continue
}
if matchCondition(fieldRaw, input.Condition.Op, input.Condition.Value) {
filtered = append(filtered, item)
}
}
if filtered == nil {
filtered = []json.RawMessage{}
}
out, _ := json.Marshal(map[string]interface{}{
"success": true,
"data": map[string]interface{}{
"items": filtered,
"count": len(filtered),
},
})
os.Stdout.Write(out)
}
func matchCondition(fieldRaw json.RawMessage, op, expected string) bool {
// 取得欄位字串值
var strVal string
var numVal float64
isNum := false
// 嘗試解析為數字
if err := json.Unmarshal(fieldRaw, &numVal); err == nil {
isNum = true
strVal = strconv.FormatFloat(numVal, 'f', -1, 64)
} else {
// 嘗試解析為字串
if err := json.Unmarshal(fieldRaw, &strVal); err != nil {
strVal = string(fieldRaw)
}
}
switch strings.ToLower(op) {
case "eq":
return strVal == expected
case "ne":
return strVal != expected
case "gt":
if !isNum {
return false
}
threshold, err := strconv.ParseFloat(expected, 64)
if err != nil {
return false
}
return numVal > threshold
case "lt":
if !isNum {
return false
}
threshold, err := strconv.ParseFloat(expected, 64)
if err != nil {
return false
}
return numVal < threshold
case "contains":
return strings.Contains(strVal, expected)
default:
return false
}
}
func writeError(msg string) {
out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg})
os.Stdout.Write(out)
}