# Contributing to arcrun 感謝你考慮貢獻 arcrun!本文件說明如何新增零件(WASM component)並提交至公眾零件庫。 arcrun 的零件**主要由 AI 撰寫**。你不需要是 TinyGo 或 AssemblyScript 專家,只需要把這份文件和你的 API 文件或需求貼給 AI,讓它生成源碼,你負責編譯、測試、提交。 --- ## 選擇開發語言 零件只需要輸出符合 **WASI preview1** 的 `.wasm` 檔案,與使用哪個語言無關。 | 語言 | 輸出大小 | AI 撰寫品質 | 說明 | |------|---------|------------|------| | **TinyGo** | 極小(10–80KB) | 優秀 | 官方零件使用;語法簡單,AI 出錯率低 | | **AssemblyScript** | 小(20–150KB) | 良好 | TypeScript 語法,前端開發者最快上手 | | **Rust** | 小–中(30–300KB) | 良好 | 效能最強;適合複雜演算法,工具鏈稍複雜 | **AI 開發建議:** - 選 **TinyGo**:Go 語法與 TypeScript 差異夠大,AI 不易把 TS 邏輯直接搬過來造成錯誤,是最穩的選擇。 - 選 **AssemblyScript**:適合已熟悉 TypeScript 的開發者,但要注意 AS 不是 TS — 提示 AI 時明確說「AssemblyScript,不是 TypeScript」。 - 選 **Rust**:效能要求高時使用;需要更詳細的提示和更仔細的審查。 --- ## 零件規格:共通規則 無論使用哪個語言,零件必須遵守: - **I/O 模型**:從 `stdin` 讀取 JSON,往 `stdout` 輸出 JSON,不使用 return value - **回傳格式**:成功 `{"success":true,"result":...}`,失敗 `{"success":false,"error":"..."}` - **不 panic**:任何錯誤都應輸出 `success:false` JSON,不讓執行器收到空輸出 - **不使用網路 / 檔案系統**(功能類零件):`no_network_syscall: true` - **允許網路**(整合類零件):`no_network_syscall: false`,必須宣告 `credentials_required` --- ## 目錄結構 ``` registry/components/my_component/ ├── component.contract.yaml # 零件規格宣告(必填) ├── main.go # TinyGo 源碼(TinyGo 零件) ├── assembly/index.ts # AssemblyScript 源碼(AS 零件) ├── src/lib.rs # Rust 源碼(Rust 零件) └── my_component.wasm # 編譯產出(不提交至 git,CI 自動產生) ``` --- ## component.contract.yaml 所有語言共用相同的合約格式: ```yaml canonical_id: "my_component" # 小寫底線,全庫唯一 display_name: "我的零件" category: "data" # api / logic / data / ai / style / anim / ui version: "v1" author: "@your-github-username" wasi_target: "preview1" stability: "floating" # floating / stable / pinned runtime_compat: - "cf-workers" - "workerd" - "wazero" constraints: max_size_kb: 2048 max_cold_start_ms: 50 no_network_syscall: true # 功能類 true,整合類 false io_model: "stdin_stdout_json" input_schema: type: object required: [text] properties: text: type: string description: 輸入文字 output_schema: type: object properties: result: type: string gherkin_tests: - scenario: "基本轉換" given: '{"text":"hello"}' then_contains: '"result"' - scenario: "缺少必填欄位" given: '{}' then_contains: '"success":false' config_example: | transform: text: "{{input.text}}" description: "我的零件功能說明。" ``` 整合類零件額外加入: ```yaml credentials_required: - key: my_api_token type: api_key description: "My Service API token" inject_as: api_token ``` --- ## TinyGo 零件開發 ### 環境安裝 ```bash # TinyGo brew install tinygo # macOS # 其他平台:https://tinygo.org/getting-started/ # 本機測試執行器 brew install wasmtime # macOS ``` ### 給 AI 的提示範本 ``` 請幫我用 TinyGo 寫一個 arcrun WASM 零件。 需求:[你的需求] 規則: - 從 stdin 讀取 JSON,往 stdout 輸出 JSON - 成功回傳 {"success":true,"result":...} - 失敗回傳 {"success":false,"error":"..."},不 panic - 不使用網路、不使用檔案系統(純功能類零件) - import 只用標準庫(encoding/json, os, fmt, strings 等) 請生成 main.go 和 component.contract.yaml。 ``` ### main.go 範本 ```go package main import ( "encoding/json" "fmt" "os" ) type Input struct { Text string `json:"text"` } type Output struct { Success bool `json:"success"` Result string `json:"result,omitempty"` Error string `json:"error,omitempty"` } func main() { var input Input if err := json.NewDecoder(os.Stdin).Decode(&input); err != nil { writeError("invalid input: " + err.Error()) return } if input.Text == "" { writeError("text is required") return } // 你的邏輯 result := "[transformed] " + input.Text out, _ := json.Marshal(Output{Success: true, Result: result}) fmt.Println(string(out)) } func writeError(msg string) { out, _ := json.Marshal(Output{Success: false, Error: msg}) fmt.Println(string(out)) } ``` ### 編譯 ```bash cd registry/components/my_component tinygo build -o my_component.wasm -target wasi . ``` ### 本機測試 ```bash echo '{"text":"hello world"}' | wasmtime run my_component.wasm # 預期:{"success":true,"result":"[transformed] hello world"} echo '{}' | wasmtime run my_component.wasm # 預期:{"success":false,"error":"text is required"} ``` --- ## AssemblyScript 零件開發 ### 環境安裝 ```bash # Node.js >= 18 node --version # 初始化 AS 專案 npm init -y npm install --save-dev assemblyscript npx asinit . # 本機測試執行器 brew install wasmtime # macOS ``` ### 給 AI 的提示範本 ``` 請幫我用 AssemblyScript(不是 TypeScript)寫一個 arcrun WASM 零件。 需求:[你的需求] 規則: - AssemblyScript 是 TypeScript 的子集,編譯為 WASM,不能使用 DOM / Node.js API - 從 stdin 讀取 JSON(使用 WASI fd_read),往 stdout 輸出 JSON(使用 Console.log) - 成功回傳 {"success":true,"result":...} - 失敗回傳 {"success":false,"error":"..."} - 不使用網路、不使用檔案系統 請生成 assembly/index.ts 和 component.contract.yaml。 注意:AssemblyScript 沒有 JSON.parse,需要手動解析或使用 as-json 套件。 ``` ### assembly/index.ts 範本 ```typescript // AssemblyScript — 注意:這不是 Node.js / TypeScript! // 沒有 DOM、沒有 fetch、沒有 require import { Console } from "as-wasi/assembly"; import { JSON } from "assemblyscript-json/assembly"; export function _start(): void { // 從 stdin 讀取輸入 const input = Console.readAll(); // 解析 JSON const parsed = JSON.parse(input); if (!parsed.isObj) { Console.log('{"success":false,"error":"invalid input"}'); return; } const obj = parsed as JSON.Obj; const textVal = obj.getString("text"); if (textVal == null) { Console.log('{"success":false,"error":"text is required"}'); return; } const text = textVal.valueOf(); // 你的邏輯 const result = "[transformed] " + text; Console.log('{"success":true,"result":"' + result + '"}'); } ``` **專案依賴(package.json):** ```json { "dependencies": { "as-wasi": "^0.4.7", "assemblyscript-json": "^1.1.0" } } ``` ### 編譯 ```bash cd registry/components/my_component npm install npx asc assembly/index.ts \ --target release \ --outFile my_component.wasm \ --exportRuntime \ --use abort=~lib/wasi_abort ``` ### 本機測試 ```bash echo '{"text":"hello world"}' | wasmtime run my_component.wasm # 預期:{"success":true,"result":"[transformed] hello world"} ``` --- ## Rust 零件開發 Rust 零件支援已就緒,但文件尚在完善中。如果你熟悉 Rust + WASM,歡迎參考 [wasm-wasi 官方文件](https://doc.rust-lang.org/stable/reference/linkage.html),核心要求與其他語言相同:WASI preview1,stdin/stdout JSON I/O。 基本設定: ```bash rustup target add wasm32-wasip1 cargo build --target wasm32-wasip1 --release ``` --- ## 提交至公眾 Registry ```bash # 確保 .wasm 已編譯 ls my_component.wasm # 提交(需要 arcrun.dev API Key) acr parts publish ./registry/components/my_component/ ``` 提交後流程: | 狀態 | 說明 | |------|------| | `sandbox_pending` | 沙盒驗收執行中 | | `author_only` | 驗收通過,你自己可用 | | `public` | 人工審核通過,所有人可用,開始累積統計 | 查詢審核進度: ```bash acr parts publish --status ``` --- ## 常見問題 ### `no_network_syscall` 設定錯誤 - **功能類**(category: logic / data / ai):`no_network_syscall: true`。這類零件應完全沙盒化。 - **整合類**(category: api):`no_network_syscall: false`,因為要呼叫外部 API。 兩者都需要宣告在 `constraints` 下,設錯會在 syscall 掃描步驟被沙盒拒絕。 ### `gherkin_tests` 必須包含 happy path 和 error path 至少兩個測試場景:一個輸入正確的 happy path、一個缺少必填欄位或輸入非法的 error path。 ### 體積超過上限 - TinyGo:確認使用 `-target wasi`(而非 `-target wasm`),前者體積更小 - AssemblyScript:加上 `--optimize` 或 `--target release` - Rust:使用 `--release` 並加入 `opt-level = "z"` 到 `Cargo.toml` --- ## 問題回報 開 Issue:[github.com/richblack/arcrun/issues](https://github.com/richblack/arcrun/issues)