- registry/aliases.yaml: scope-level synonym table for 21 built-in components covers api (gmail/google_sheets/telegram/line_notify/http_request), data (string/array/date/number/json), logic (if/foreach/switch/try_catch/wait), ai scopes; includes zh/en/abbrev variants - types.ts: add optional aliases[] field to ComponentContractSchema - CONTRIBUTING.md: explain aliases auto-merge from aliases.yaml vs manual contract aliases Note: manual maintenance for now; aliases.yaml becomes KBDB synonym graph seed data when KBDB is introduced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
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:falseJSON,不讓執行器收到空輸出 - 不使用網路 / 檔案系統(功能類零件):
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
所有語言共用相同的合約格式:
canonical_id: "string_reverse" # 見下方命名規範
display_name: "字串反轉" # 人類可讀,可中文,供 UI 顯示用
description: > # 語意搜尋用,見下方說明
將字串內容倒序排列,適合測試、資料清洗、回文判斷等場景。
不依賴外部服務,純本地運算。
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: "我的零件功能說明。"
整合類零件額外加入:
credentials_required:
- key: my_api_token
type: api_key
description: "My Service API token"
inject_as: api_token
canonical_id 命名規範
canonical_id 是零件的永久識別符,一旦上架不能更改(改名 = 新零件)。命名不統一會導致功能重複,請遵守以下規則:
| category | 格式 | 範例 |
|---|---|---|
api(整合類) |
{服務名} 或 {服務名}_{動作} |
gmail、gmail_send、google_sheets、google_sheets_append、telegram |
data(資料處理) |
{資料型別}_ops 或 {動詞}_{名詞} |
string_ops、array_ops、json_transform、csv_parse |
logic(控制流) |
{結構名}_control 或單詞動詞 |
if_control、foreach_control、try_catch、switch、wait |
ai(AI 類) |
ai_{動作} |
ai_transform_compile、ai_summarize、ai_classify |
規則:
- 全部小寫、底線分隔、最多 4 個單詞
- 禁止:中文、空格、大寫、連字號(
-)、版本號混入(用version欄位表達) display_name才是人類可讀名稱,可以是「宇宙無敵 gsheets 新增一列」,canonical_id不行
提交前自問: 如果有人想用 AI 搜尋「幫我找一個可以新增 Google Sheets 列的零件」,他搜到的名字應該是什麼?答案就是你的 canonical_id。
description 寫法(語意搜尋)
description 是語意搜尋的索引來源,用自然語言描述「能做什麼、適合什麼情境」,而不是重複零件名稱。
好的 description:
description: >
傳送 Gmail 電子郵件,適合 Workflow 完成後通知使用者、發送訂閱確認信、
錯誤警報通知等場景。支援自訂主旨、內文與收件人。需要 Gmail OAuth token。
不好的 description(等於沒有):
description: "Gmail 發信零件" # 只是名稱的同義詞,搜不到任何額外資訊
原則:把這個 description 給一個不知道這個零件存在的人看,他能判斷「這就是我要的東西」嗎?
aliases(搜尋同義詞)
arcrun 在 registry/aliases.yaml 維護一份 scope 級別的同義詞表。當你的零件 canonical_id 以已知 scope 為前綴,Registry 建立搜尋索引時會自動把對應的同義詞合併進去,不需要在 contract 裡手動填。
例如 canonical_id: google_sheets_append,Registry 會自動從 aliases.yaml 取得 google_sheets scope 的同義詞(gsheets、試算表、spreadsheet...),搜這些詞都能找到你的零件。
如果你的零件有額外的情境同義詞(超出 scope 範圍),可以在 contract 內手動補充:
canonical_id: "google_sheets_append"
aliases:
- "新增資料列" # 情境同義詞,超出 scope 範圍
- "insert row"
# google_sheets scope 的同義詞(gsheets / 試算表 / spreadsheet...)
# 由 registry/aliases.yaml 自動合併,不需要重複填寫
想新增新 scope 的同義詞(例如你要加一個 notion 零件):在 registry/aliases.yaml 的對應 category 下加一個新 key,開 PR,merge 後所有以 notion_ 開頭的零件都自動繼承。
這個機制目前是手工維護。未來接入 KBDB 後,
canonical_id將獲得系統派發的唯一 hash id,同義詞表將成為 KBDB synonym graph 的初始資料。
TinyGo 零件開發
環境安裝
# 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 範本
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))
}
編譯
cd registry/components/my_component
tinygo build -o my_component.wasm -target wasi .
本機測試
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 零件開發
環境安裝
# 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 範本
// 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):
{
"dependencies": {
"as-wasi": "^0.4.7",
"assemblyscript-json": "^1.1.0"
}
}
編譯
cd registry/components/my_component
npm install
npx asc assembly/index.ts \
--target release \
--outFile my_component.wasm \
--exportRuntime \
--use abort=~lib/wasi_abort
本機測試
echo '{"text":"hello world"}' | wasmtime run my_component.wasm
# 預期:{"success":true,"result":"[transformed] hello world"}
Rust 零件開發
Rust 零件支援已就緒,但文件尚在完善中。如果你熟悉 Rust + WASM,歡迎參考 wasm-wasi 官方文件,核心要求與其他語言相同:WASI preview1,stdin/stdout JSON I/O。
基本設定:
rustup target add wasm32-wasip1
cargo build --target wasm32-wasip1 --release
提交至公眾 Registry
# 確保 .wasm 已編譯
ls my_component.wasm
# 提交(需要 arcrun.dev API Key)
acr parts publish ./registry/components/my_component/
提交後流程:
| 狀態 | 說明 |
|---|---|
sandbox_pending |
沙盒驗收執行中 |
author_only |
驗收通過,你自己可用 |
public |
人工審核通過,所有人可用,開始累積統計 |
查詢審核進度:
acr parts publish --status <submission_id>
常見問題
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