Files
Arcrun/registry/components/gmail/main.go
T
Claude 2707fca32b feat(arcrun): implement arcrun MVP — open-source AI workflow engine
Phase 1-5 complete per .agents/specs/u6u-core-mvp/:

**Phase 1 — Cherry-pick & cleanup**
- Create arcrun/ from cypher-executor, credentials, builtins, registry
- Remove 9 InkStone Service Bindings (KBDB, REGISTRY, CLINIC_*, AICEO, MINI_ME)
- Rewrite component-loader: 3-layer (builtin → WASM_BUCKET R2 → error)
- Remove autoPublishMissing.ts, proxy.ts (AICEO), execution-logger.ts (KBDB)
- Clean all KV namespace IDs and InkStone internal URLs from config files

**Phase 2 — contract.yaml completeness**
- Add credentials_required to gmail, google_sheets, telegram, line_notify
- Add config_example to all 21 components with annotated field descriptions

**Phase 3 — Credential injection**
- Add credential-injector.ts: AES-GCM decrypt from CREDENTIALS_KV
- Integrate into GraphExecutor before WASM execution
- Structured errors with repair instructions when credential missing

**Phase 4 — CLI (acr)**
- cli/package.json: arcrun package, bin: acr, deps: commander/js-yaml/chalk/ora
- 8 commands: init, creds push, push, run, validate, parts, list, logs
- Standard mode: writes directly to user's CF KV via CF REST API
- acr init: interactive setup with arcrun.dev API Key registration

**Phase 5 — Open source release prep**
- README.md: 5-minute quickstart, component table, workflow YAML syntax
- CONTRIBUTING.md: TinyGo dev env, component scaffolding, submission flow
- Security audit: no InkStone internal URLs/IDs in committed files
- .gitignore: exclude credentials.yaml, .wrangler, *.wasm

https://claude.ai/code/session_01BnCdSLVH8tUed9VrrPavgT
2026-04-16 04:06:25 +00:00

140 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// gmail — 透過 Gmail API 發送 Email
// 透過 host function 呼叫 Gmail API
//
//go:build tinygo
package main
import (
"encoding/json"
"io"
"os"
"strings"
"unsafe"
)
//go:wasmimport u6u http_request
func hostHttpRequest(
urlPtr uintptr, urlLen uint32,
methodPtr uintptr, methodLen uint32,
headersPtr uintptr, headersLen uint32,
bodyPtr uintptr, bodyLen uint32,
outPtr uintptr, outLenPtr uintptr,
) uint32
type Input struct {
To string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
AccessToken string `json:"access_token"`
}
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.To == "" {
writeError("to 必填")
return
}
if input.Subject == "" {
writeError("subject 必填")
return
}
if input.AccessToken == "" {
writeError("access_token 必填")
return
}
// 建立 RFC 2822 格式的 emailbase64url 編碼
emailLines := []string{
"To: " + input.To,
"Subject: " + input.Subject,
"Content-Type: text/plain; charset=UTF-8",
"",
input.Body,
}
emailRaw := strings.Join(emailLines, "\r\n")
encoded := base64URLEncode([]byte(emailRaw))
bodyData, _ := json.Marshal(map[string]string{"raw": encoded})
apiURL := "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
method := "POST"
headers := map[string]string{
"Authorization": "Bearer " + input.AccessToken,
"Content-Type": "application/json",
}
headersJSON, _ := json.Marshal(headers)
urlBytes := []byte(apiURL)
methodBytes := []byte(method)
bodyBytes := bodyData
outBuf := make([]byte, 65536)
var outLen uint32
result := hostHttpRequest(
uintptr(unsafe.Pointer(&urlBytes[0])), uint32(len(urlBytes)),
uintptr(unsafe.Pointer(&methodBytes[0])), uint32(len(methodBytes)),
uintptr(unsafe.Pointer(&headersJSON[0])), uint32(len(headersJSON)),
uintptr(unsafe.Pointer(&bodyBytes[0])), uint32(len(bodyBytes)),
uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)),
)
if result != 0 {
writeError("Gmail API 呼叫失敗")
return
}
responseStr := string(outBuf[:outLen])
var responseData map[string]interface{}
if err := json.Unmarshal([]byte(responseStr), &responseData); err != nil {
responseData = map[string]interface{}{"raw": responseStr}
}
messageID, _ := responseData["id"].(string)
out, _ := json.Marshal(map[string]interface{}{
"success": true,
"data": map[string]interface{}{"message_id": messageID},
})
os.Stdout.Write(out)
}
// base64URLEncode — 不依賴 encoding/base64TinyGo 相容)
func base64URLEncode(data []byte) string {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
var sb strings.Builder
for i := 0; i < len(data); i += 3 {
b0 := data[i]
var b1, b2 byte
if i+1 < len(data) {
b1 = data[i+1]
}
if i+2 < len(data) {
b2 = data[i+2]
}
sb.WriteByte(chars[b0>>2])
sb.WriteByte(chars[((b0&0x3)<<4)|(b1>>4)])
if i+1 < len(data) {
sb.WriteByte(chars[((b1&0xf)<<2)|(b2>>6)])
}
if i+2 < len(data) {
sb.WriteByte(chars[b2&0x3f])
}
}
return sb.String()
}
func writeError(msg string) {
out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg})
os.Stdout.Write(out)
}