docs: multi-language component guide — TinyGo, AssemblyScript, Rust
README: add language comparison table in contributing section, explain AI writing quality differences and why TinyGo is recommended for official components. CONTRIBUTING: full rewrite with separate TinyGo + AssemblyScript sections, each with AI prompt templates, code templates, build commands, and test commands. Rust documented as supported with basic setup reference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+252
-97
@@ -2,51 +2,64 @@
|
|||||||
|
|
||||||
感謝你考慮貢獻 arcrun!本文件說明如何新增零件(WASM component)並提交至公眾零件庫。
|
感謝你考慮貢獻 arcrun!本文件說明如何新增零件(WASM component)並提交至公眾零件庫。
|
||||||
|
|
||||||
---
|
arcrun 的零件**主要由 AI 撰寫**。你不需要是 TinyGo 或 AssemblyScript 專家,只需要把這份文件和你的 API 文件或需求貼給 AI,讓它生成源碼,你負責編譯、測試、提交。
|
||||||
|
|
||||||
## 開發環境
|
|
||||||
|
|
||||||
### 必要工具
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# TinyGo(編譯 Go → WASM)
|
|
||||||
brew install tinygo # macOS
|
|
||||||
# 或參考 https://tinygo.org/getting-started/
|
|
||||||
|
|
||||||
# wasmtime(本機測試 WASM)
|
|
||||||
brew install wasmtime # macOS
|
|
||||||
# 或參考 https://wasmtime.dev
|
|
||||||
|
|
||||||
# Wrangler CLI(部署 Cloudflare Workers)
|
|
||||||
npm i -g wrangler
|
|
||||||
|
|
||||||
# arcrun CLI
|
|
||||||
npm i -g arcrun
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 新增零件步驟
|
## 選擇開發語言
|
||||||
|
|
||||||
### 1. 建立目錄結構
|
零件只需要輸出符合 **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/
|
registry/components/my_component/
|
||||||
├── component.contract.yaml # 零件規格宣告
|
├── component.contract.yaml # 零件規格宣告(必填)
|
||||||
├── main.go # 零件邏輯(TinyGo)
|
├── main.go # TinyGo 源碼(TinyGo 零件)
|
||||||
|
├── assembly/index.ts # AssemblyScript 源碼(AS 零件)
|
||||||
|
├── src/lib.rs # Rust 源碼(Rust 零件)
|
||||||
└── my_component.wasm # 編譯產出(不提交至 git,CI 自動產生)
|
└── my_component.wasm # 編譯產出(不提交至 git,CI 自動產生)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 撰寫 component.contract.yaml
|
---
|
||||||
|
|
||||||
|
## component.contract.yaml
|
||||||
|
|
||||||
|
所有語言共用相同的合約格式:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
canonical_id: "my_component"
|
canonical_id: "my_component" # 小寫底線,全庫唯一
|
||||||
display_name: "我的零件"
|
display_name: "我的零件"
|
||||||
category: "data" # api / logic / data / ai
|
category: "data" # api / logic / data / ai / style / anim / ui
|
||||||
version: "v1"
|
version: "v1"
|
||||||
author: "@your-github-username"
|
author: "@your-github-username"
|
||||||
wasi_target: "preview1"
|
wasi_target: "preview1"
|
||||||
stability: "floating"
|
stability: "floating" # floating / stable / pinned
|
||||||
runtime_compat:
|
runtime_compat:
|
||||||
- "cf-workers"
|
- "cf-workers"
|
||||||
- "workerd"
|
- "workerd"
|
||||||
@@ -54,14 +67,13 @@ runtime_compat:
|
|||||||
constraints:
|
constraints:
|
||||||
max_size_kb: 2048
|
max_size_kb: 2048
|
||||||
max_cold_start_ms: 50
|
max_cold_start_ms: 50
|
||||||
no_network_syscall: true # 功能類零件不允許網路請求
|
no_network_syscall: true # 功能類 true,整合類 false
|
||||||
no_filesystem_syscall: true
|
|
||||||
io_model: "stdin_stdout_json"
|
io_model: "stdin_stdout_json"
|
||||||
input_schema:
|
input_schema:
|
||||||
type: object
|
type: object
|
||||||
required: [input]
|
required: [text]
|
||||||
properties:
|
properties:
|
||||||
input:
|
text:
|
||||||
type: string
|
type: string
|
||||||
description: 輸入文字
|
description: 輸入文字
|
||||||
output_schema:
|
output_schema:
|
||||||
@@ -71,15 +83,18 @@ output_schema:
|
|||||||
type: string
|
type: string
|
||||||
gherkin_tests:
|
gherkin_tests:
|
||||||
- scenario: "基本轉換"
|
- scenario: "基本轉換"
|
||||||
given: '{"input":"hello"}'
|
given: '{"text":"hello"}'
|
||||||
then_contains: '"result"'
|
then_contains: '"result"'
|
||||||
|
- scenario: "缺少必填欄位"
|
||||||
|
given: '{}'
|
||||||
|
then_contains: '"success":false'
|
||||||
config_example: |
|
config_example: |
|
||||||
transform: # 節點名稱(可自訂)
|
transform:
|
||||||
input: "{{input.text}}" # 輸入欄位(必填)
|
text: "{{input.text}}"
|
||||||
description: "我的零件功能說明。"
|
description: "我的零件功能說明。"
|
||||||
```
|
```
|
||||||
|
|
||||||
**需要 Credential 的整合類零件需額外加入:**
|
整合類零件額外加入:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
credentials_required:
|
credentials_required:
|
||||||
@@ -89,91 +104,236 @@ credentials_required:
|
|||||||
inject_as: api_token
|
inject_as: api_token
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 撰寫 main.go
|
---
|
||||||
|
|
||||||
WASM 零件使用 stdin/stdout JSON I/O 模型(WASI preview1):
|
## 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
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Input struct {
|
type Input struct {
|
||||||
Input string `json:"input"`
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Output struct {
|
type Output struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Result string `json:"result,omitempty"`
|
Result string `json:"result,omitempty"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var input Input
|
var input Input
|
||||||
decoder := json.NewDecoder(os.Stdin)
|
if err := json.NewDecoder(os.Stdin).Decode(&input); err != nil {
|
||||||
if err := decoder.Decode(&input); err != nil {
|
writeError("invalid input: " + err.Error())
|
||||||
out, _ := json.Marshal(Output{Success: false, Error: "invalid input: " + err.Error()})
|
return
|
||||||
fmt.Println(string(out))
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 你的邏輯
|
if input.Text == "" {
|
||||||
result := doTransform(input.Input)
|
writeError("text is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
out, _ := json.Marshal(Output{Success: true, Result: result})
|
// 你的邏輯
|
||||||
fmt.Println(string(out))
|
result := "[transformed] " + input.Text
|
||||||
|
|
||||||
|
out, _ := json.Marshal(Output{Success: true, Result: result})
|
||||||
|
fmt.Println(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTransform(s string) string {
|
func writeError(msg string) {
|
||||||
return "[transformed] " + s
|
out, _ := json.Marshal(Output{Success: false, Error: msg})
|
||||||
|
fmt.Println(string(out))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 編譯 WASM
|
### 編譯
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd registry/components/my_component
|
cd registry/components/my_component
|
||||||
tinygo build -o my_component.wasm -target wasi .
|
tinygo build -o my_component.wasm -target wasi .
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. 本機測試
|
### 本機測試
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 直接測試 WASM
|
echo '{"text":"hello world"}' | wasmtime run my_component.wasm
|
||||||
echo '{"input":"hello world"}' | wasmtime run my_component.wasm
|
# 預期:{"success":true,"result":"[transformed] hello world"}
|
||||||
|
|
||||||
# 預期輸出:{"success":true,"result":"[transformed] hello world"}
|
echo '{}' | wasmtime run my_component.wasm
|
||||||
```
|
# 預期:{"success":false,"error":"text is required"}
|
||||||
|
|
||||||
### 6. 驗證 Gherkin 測試
|
|
||||||
|
|
||||||
確認 contract.yaml 的 `gherkin_tests` 所有場景都通過:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 每個 scenario:given 輸入 → 輸出包含 then_contains
|
|
||||||
echo '<given-json>' | wasmtime run my_component.wasm | grep '<then_contains>'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 提交零件至公眾 Registry
|
## AssemblyScript 零件開發
|
||||||
|
|
||||||
|
### 環境安裝
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 確保已編譯 .wasm
|
# Node.js >= 18
|
||||||
tinygo build -o my_component.wasm -target wasi .
|
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)
|
# 提交(需要 arcrun.dev API Key)
|
||||||
acr parts publish ./registry/components/my_component/
|
acr parts publish ./registry/components/my_component/
|
||||||
```
|
```
|
||||||
|
|
||||||
提交後:
|
提交後流程:
|
||||||
- **功能類**零件(無網路請求):體積 + syscall + Gherkin 測試通過後,立即 `author_only`(你自己可用)
|
|
||||||
- **整合類**零件(有外部 API 呼叫):體積 + syscall 掃描通過後,`author_only`
|
| 狀態 | 說明 |
|
||||||
- 等人工審核通過 → `public`(所有人可用,開始累積執行統計)
|
|------|------|
|
||||||
|
| `sandbox_pending` | 沙盒驗收執行中 |
|
||||||
|
| `author_only` | 驗收通過,你自己可用 |
|
||||||
|
| `public` | 人工審核通過,所有人可用,開始累積統計 |
|
||||||
|
|
||||||
查詢審核進度:
|
查詢審核進度:
|
||||||
|
|
||||||
@@ -183,32 +343,27 @@ acr parts publish --status <submission_id>
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 零件類型指引
|
## 常見問題
|
||||||
|
|
||||||
### 功能類(category: logic / data / ai)
|
### `no_network_syscall` 設定錯誤
|
||||||
|
|
||||||
- 不允許網路請求(`no_network_syscall: true`)
|
- **功能類**(category: logic / data / ai):`no_network_syscall: true`。這類零件應完全沙盒化。
|
||||||
- 不允許檔案系統存取(`no_filesystem_syscall: true`)
|
- **整合類**(category: api):`no_network_syscall: false`,因為要呼叫外部 API。
|
||||||
- 需通過所有 Gherkin 測試
|
|
||||||
- 冷啟動 < 50ms,體積 < 2048KB
|
|
||||||
|
|
||||||
### 整合類(category: api)
|
兩者都需要宣告在 `constraints` 下,設錯會在 syscall 掃描步驟被沙盒拒絕。
|
||||||
|
|
||||||
- 允許網路請求(設 `no_network_syscall: false`)
|
### `gherkin_tests` 必須包含 happy path 和 error path
|
||||||
- 必須宣告 `credentials_required`
|
|
||||||
- 需通過體積和 syscall 掃描
|
|
||||||
|
|
||||||
---
|
至少兩個測試場景:一個輸入正確的 happy path、一個缺少必填欄位或輸入非法的 error path。
|
||||||
|
|
||||||
## 程式碼風格
|
### 體積超過上限
|
||||||
|
|
||||||
- 零件必須回傳包含 `success: bool` 的 JSON
|
- TinyGo:確認使用 `-target wasi`(而非 `-target wasm`),前者體積更小
|
||||||
- 錯誤時回傳 `{"success":false,"error":"..."}`,不 panic
|
- AssemblyScript:加上 `--optimize` 或 `--target release`
|
||||||
- 所有 `required` 欄位缺少時應回傳 `success:false` 而非 crash
|
- Rust:使用 `--release` 並加入 `opt-level = "z"` 到 `Cargo.toml`
|
||||||
- contract 的 `input_schema.required[]` 必須與 main.go 的驗證邏輯一致
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 問題回報
|
## 問題回報
|
||||||
|
|
||||||
開 Issue:[github.com/arcrun/arcrun/issues](https://github.com/arcrun/arcrun/issues)
|
開 Issue:[github.com/richblack/arcrun/issues](https://github.com/richblack/arcrun/issues)
|
||||||
|
|||||||
@@ -240,9 +240,20 @@ acr parts scaffold gmail
|
|||||||
|
|
||||||
### 貢獻零件
|
### 貢獻零件
|
||||||
|
|
||||||
零件是 TinyGo 編譯的 `.wasm` 檔案,stdin 進 JSON、stdout 出 JSON,就這樣。
|
零件是 `.wasm` 檔案,stdin 進 JSON、stdout 出 JSON。用什麼語言編譯不重要,只要輸出符合 WASI preview1 的 `.wasm` 即可。
|
||||||
|
|
||||||
**AI 可以直接幫你寫零件。** 把 API 文件和 [CONTRIBUTING.md](CONTRIBUTING.md) 一起貼給它,它生成 `main.go` 和 `component.contract.yaml`,你編譯、測試、提交:
|
**arcrun 的零件主要由 AI 撰寫。** 這個設計決定影響了語言選擇:
|
||||||
|
|
||||||
|
| 語言 | 輸出大小 | AI 撰寫品質 | 學習曲線 | 備註 |
|
||||||
|
|------|---------|------------|---------|------|
|
||||||
|
| **TinyGo** | 極小(10–80KB) | 優秀 | 低(Go 語法簡單) | 官方零件首選;語法與 TS 差異夠大,AI 不易混淆 |
|
||||||
|
| **AssemblyScript** | 小(20–150KB) | 良好 | 低(TypeScript 語法) | 社群貢獻首選;TS 開發者上手最快 |
|
||||||
|
| **Rust** | 小–中(30–300KB) | 良好 | 高(Rust 所有權) | 效能最強;適合複雜演算法零件 |
|
||||||
|
| C / C++ | 小 | 尚可 | 高 | 不建議,現代語言更好 |
|
||||||
|
|
||||||
|
**注意:** AssemblyScript 與 TypeScript 語法高度相似,AI 有時會把純 TS 邏輯直接搬過來造成編譯錯誤。TinyGo 語法差異夠大,AI 出錯率較低。初期如果不確定選哪個,TinyGo 是最穩的選擇。
|
||||||
|
|
||||||
|
**AI 可以直接幫你寫零件。** 把 API 文件和 [CONTRIBUTING.md](CONTRIBUTING.md) 一起貼給它,指定語言(TinyGo 或 AssemblyScript),它生成源碼和 `component.contract.yaml`,你編譯、測試、提交:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acr parts publish ./my-component/
|
acr parts publish ./my-component/
|
||||||
|
|||||||
Reference in New Issue
Block a user