diff --git a/.gitignore b/.gitignore index c84ac1b1e..055047bb9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ registry/native/target/ registry/native/vendor/ registry/native/c/build/ registry/native/c/vendor/ +registry/native/go/vendor/ registry/native/c/libs/ registry/native/c/.cache/ registry/native/c/sysroot/ diff --git a/packages/core/package.json b/packages/core/package.json index 714a7bd42..06a241576 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -67,6 +67,7 @@ "@rivet-dev/agent-os-ripgrep": "link:../../registry/software/ripgrep", "@rivet-dev/agent-os-sed": "link:../../registry/software/sed", "@rivet-dev/agent-os-tar": "link:../../registry/software/tar", + "@rivet-dev/agent-os-slack-cli": "link:../../registry/software/slack-cli", "@rivet-dev/agent-os-tree": "link:../../registry/software/tree", "@rivet-dev/agent-os-yq": "link:../../registry/software/yq", "@types/node": "^22.10.2", diff --git a/packages/core/tests/wasm-commands.test.ts b/packages/core/tests/wasm-commands.test.ts index aa5e19a03..d763ab410 100644 --- a/packages/core/tests/wasm-commands.test.ts +++ b/packages/core/tests/wasm-commands.test.ts @@ -3,6 +3,7 @@ import { existsSync } from "node:fs"; import { join } from "node:path"; import { AgentOs } from "../src/index.js"; import curlPackage from "@rivet-dev/agent-os-curl"; +import slackCliPackage from "@rivet-dev/agent-os-slack-cli"; import { REGISTRY_SOFTWARE, registrySkipReason, @@ -804,3 +805,33 @@ server.listen(0, "0.0.0.0", () => { }); }); }); + +// ── slack-cli (Go WASM, separate build) ────────────────────────────── + +const slackCliSkipReason = existsSync(slackCliPackage.commandDir) + ? false + : "slack-cli WASM binary not available (run: cd registry && make build-wasm-go copy-wasm)"; + +describe.skipIf(slackCliSkipReason)("slack-cli", () => { + let vm: AgentOs; + + beforeEach(async () => { + vm = await AgentOs.create({ software: [...REGISTRY_SOFTWARE, slackCliPackage] }); + }); + + afterEach(async () => { + await vm.dispose(); + }); + + test("slack version prints version info", async () => { + const r = await vm.exec("slack version"); + expect(r.exitCode).toBe(0); + expect(r.stdout).toMatch(/slack/i); + }); + + test("slack help prints usage", async () => { + const r = await vm.exec("slack help"); + expect(r.exitCode).toBe(0); + expect(r.stdout).toContain("slack"); + }); +}); diff --git a/packages/registry-types/src/index.ts b/packages/registry-types/src/index.ts index 10baaede1..b749ed068 100644 --- a/packages/registry-types/src/index.ts +++ b/packages/registry-types/src/index.ts @@ -32,8 +32,8 @@ export interface WasmCommandPackage { aptName: string; /** Human-readable description. */ description: string; - /** Build source: "rust" or "c". */ - source: "rust" | "c"; + /** Build source: "rust", "c", or "go". */ + source: "rust" | "c" | "go"; /** Commands provided by this package. */ commands: WasmCommandEntry[]; /** Absolute path to the directory containing WASM command binaries. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99a3cd084..70e03d4dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,6 +236,9 @@ importers: '@rivet-dev/agent-os-sed': specifier: link:../../registry/software/sed version: link:../../registry/software/sed + '@rivet-dev/agent-os-slack-cli': + specifier: link:../../registry/software/slack-cli + version: link:../../registry/software/slack-cli '@rivet-dev/agent-os-tar': specifier: link:../../registry/software/tar version: link:../../registry/software/tar @@ -900,6 +903,18 @@ importers: specifier: ^5.9.2 version: 5.9.3 + registry/software/slack-cli: + devDependencies: + '@rivet-dev/agent-os-registry-types': + specifier: link:../../../packages/registry-types + version: link:../../../packages/registry-types + '@types/node': + specifier: ^22.10.2 + version: 22.19.15 + typescript: + specifier: ^5.9.2 + version: 5.9.3 + registry/software/sqlite3: devDependencies: '@rivet-dev/agent-os-registry-types': diff --git a/registry/Makefile b/registry/Makefile index 2f90ee938..5fa72c096 100644 --- a/registry/Makefile +++ b/registry/Makefile @@ -8,13 +8,14 @@ COMMANDS_DIR := $(NATIVE_DIR)/target/wasm32-wasip1/release/commands MARKER_DIR := .build-markers RUST_MARKER := $(MARKER_DIR)/rust-wasm-built C_MARKER := $(MARKER_DIR)/c-wasm-built +GO_MARKER := $(MARKER_DIR)/go-wasm-built COPY_MARKER := $(MARKER_DIR)/wasm-copied META_MARKER := $(MARKER_DIR)/meta-generated TS_MARKER := $(MARKER_DIR)/ts-built # Command packages (excludes _types and meta-packages) CMD_PACKAGES := coreutils sed grep gawk findutils diffutils tar gzip \ - curl http-get wget zip unzip jq ripgrep fd tree file sqlite3 duckdb yq codex git + curl http-get wget zip unzip jq ripgrep fd tree file sqlite3 duckdb yq codex git slack-cli # Meta-packages META_PACKAGES := common build-essential everything @@ -22,7 +23,7 @@ META_PACKAGES := common build-essential everything # All publishable packages ALL_PACKAGES := $(CMD_PACKAGES) $(META_PACKAGES) -.PHONY: all build-wasm build-wasm-rust build-wasm-c copy-wasm generate-meta build test \ +.PHONY: all build-wasm build-wasm-rust build-wasm-c build-wasm-go copy-wasm generate-meta build test \ publish publish-dry publish-force publish-clean clean rebuild all: build @@ -36,7 +37,7 @@ $(MARKER_DIR): # --------------------------------------------------------------------------- # Build WASM binaries from source (in native/) # --------------------------------------------------------------------------- -build-wasm: build-wasm-rust build-wasm-c +build-wasm: build-wasm-rust build-wasm-c build-wasm-go build-wasm-rust: $(RUST_MARKER) $(RUST_MARKER): $(MARKER_DIR) @@ -51,11 +52,17 @@ $(C_MARKER): $(MARKER_DIR) cd $(NATIVE_DIR)/c && make install COMMANDS_DIR=../target/wasm32-wasip1/release/commands @touch $(C_MARKER) +build-wasm-go: $(GO_MARKER) +$(GO_MARKER): $(MARKER_DIR) + @echo "=== Building Go WASM commands ===" + cd $(NATIVE_DIR)/go && make wasm + @touch $(GO_MARKER) + # --------------------------------------------------------------------------- # Copy WASM binaries from native build output into per-package wasm/ dirs # --------------------------------------------------------------------------- copy-wasm: $(COPY_MARKER) -$(COPY_MARKER): $(RUST_MARKER) $(C_MARKER) +$(COPY_MARKER): $(RUST_MARKER) $(C_MARKER) $(GO_MARKER) @echo "=== Copying WASM binaries to packages ===" @# --- coreutils --- @@ -186,6 +193,10 @@ $(COPY_MARKER): $(RUST_MARKER) $(C_MARKER) @mkdir -p software/git/wasm @[ -f "$(COMMANDS_DIR)/git" ] && cp -f "$(COMMANDS_DIR)/git" software/git/wasm/ || echo " WARN: git not found" + @# --- slack-cli (Go build, GOOS=wasip1) --- + @mkdir -p software/slack-cli/wasm + @[ -f "$(COMMANDS_DIR)/slack" ] && cp -f "$(COMMANDS_DIR)/slack" software/slack-cli/wasm/ || echo " WARN: slack not found (Go WASI build)" + @echo "=== Copy complete ===" @touch $(COPY_MARKER) @@ -321,6 +332,7 @@ clean-native: @rm -rf $(NATIVE_DIR)/target @rm -rf $(NATIVE_DIR)/vendor @rm -rf $(NATIVE_DIR)/c/build $(NATIVE_DIR)/c/vendor $(NATIVE_DIR)/c/libs $(NATIVE_DIR)/c/.cache + @rm -rf $(NATIVE_DIR)/go/vendor @rm -rf $(MARKER_DIR) @echo "Cleaned native build artifacts" diff --git a/registry/native/go/Makefile b/registry/native/go/Makefile new file mode 100644 index 000000000..4e9f2e82f --- /dev/null +++ b/registry/native/go/Makefile @@ -0,0 +1,66 @@ +SHELL := /bin/bash + +# Output directory (shared with Rust/C builds) +COMMANDS_DIR ?= ../target/wasm32-wasip1/release/commands + +# Vendor directory for upstream Go sources +VENDOR_DIR := vendor + +# --- slack-cli --- +SLACK_CLI_REPO := https://github.com/slackapi/slack-cli.git +SLACK_CLI_COMMIT := 24c1d2c50b7f438700596c4d1902e0838a42fab0 +SLACK_CLI_DIR := $(VENDOR_DIR)/slack-cli +SLACK_CLI_PATCHES := $(sort $(wildcard patches/slack-cli/*.patch)) + +.PHONY: wasm clean install + +# --------------------------------------------------------------------------- +# Build all Go WASM commands +# --------------------------------------------------------------------------- +wasm: $(COMMANDS_DIR)/slack + +# --------------------------------------------------------------------------- +# slack-cli +# --------------------------------------------------------------------------- + +# Fetch upstream source at pinned commit +$(SLACK_CLI_DIR)/go.mod: + @echo "=== Fetching slack-cli ===" + @mkdir -p $(VENDOR_DIR) + git clone $(SLACK_CLI_REPO) $(SLACK_CLI_DIR) + git -C $(SLACK_CLI_DIR) checkout $(SLACK_CLI_COMMIT) + +# Vendor Go dependencies +$(SLACK_CLI_DIR)/vendor/modules.txt: $(SLACK_CLI_DIR)/go.mod + @echo "=== Vendoring slack-cli dependencies ===" + cd $(SLACK_CLI_DIR) && go mod vendor + +# Apply WASI patches to vendored dependencies +.PHONY: patch-slack-cli +patch-slack-cli: $(SLACK_CLI_DIR)/vendor/modules.txt + @echo "=== Applying WASI patches to slack-cli ===" + @for patch in $(SLACK_CLI_PATCHES); do \ + echo " PATCH: $$patch"; \ + patch --forward -p1 -d $(SLACK_CLI_DIR) < "$$patch" || \ + echo " (already applied)"; \ + done + +# Build WASM binary +$(COMMANDS_DIR)/slack: $(SLACK_CLI_DIR)/vendor/modules.txt $(SLACK_CLI_PATCHES) | patch-slack-cli + @echo "=== Building slack-cli WASM ===" + @mkdir -p $(COMMANDS_DIR) + cd $(SLACK_CLI_DIR) && GOOS=wasip1 GOARCH=wasm go build -mod=vendor -o $(abspath $@) . + @echo " Built: $@ ($$(du -h $@ | cut -f1))" + +# --------------------------------------------------------------------------- +# Install (copy to COMMANDS_DIR, for parity with C Makefile) +# --------------------------------------------------------------------------- +install: + @echo "Go binaries already output directly to COMMANDS_DIR" + +# --------------------------------------------------------------------------- +# Clean +# --------------------------------------------------------------------------- +clean: + rm -rf $(VENDOR_DIR) + rm -f $(COMMANDS_DIR)/slack diff --git a/registry/native/go/patches/slack-cli/0001-clipboard-wasip1-stub.patch b/registry/native/go/patches/slack-cli/0001-clipboard-wasip1-stub.patch new file mode 100644 index 000000000..cf26a0b75 --- /dev/null +++ b/registry/native/go/patches/slack-cli/0001-clipboard-wasip1-stub.patch @@ -0,0 +1,16 @@ +--- /dev/null ++++ b/vendor/github.com/atotto/clipboard/clipboard_wasip1.go +@@ -0,0 +1,13 @@ ++//go:build wasip1 ++ ++package clipboard ++ ++import "errors" ++ ++func readAll() (string, error) { ++ return "", errors.New("clipboard not supported in WASM") ++} ++ ++func writeAll(text string) error { ++ return errors.New("clipboard not supported in WASM") ++} diff --git a/registry/native/go/patches/slack-cli/0002-bubbletea-wasip1-stubs.patch b/registry/native/go/patches/slack-cli/0002-bubbletea-wasip1-stubs.patch new file mode 100644 index 000000000..4f1673b72 --- /dev/null +++ b/registry/native/go/patches/slack-cli/0002-bubbletea-wasip1-stubs.patch @@ -0,0 +1,24 @@ +--- /dev/null ++++ b/vendor/charm.land/bubbletea/v2/signals_wasip1.go +@@ -0,0 +1,7 @@ ++//go:build wasip1 ++ ++package tea ++ ++func (p *Program) listenForResize(done chan struct{}) { ++ close(done) ++} +--- /dev/null ++++ b/vendor/charm.land/bubbletea/v2/tty_wasip1.go +@@ -0,0 +1,11 @@ ++//go:build wasip1 ++ ++package tea ++ ++const suspendSupported = false ++ ++func suspendProcess() {} ++ ++func (p *Program) initInput() error { ++ return nil ++} diff --git a/registry/native/go/patches/slack-cli/0003-go-git-wasip1-stub.patch b/registry/native/go/patches/slack-cli/0003-go-git-wasip1-stub.patch new file mode 100644 index 000000000..9a44a7cde --- /dev/null +++ b/registry/native/go/patches/slack-cli/0003-go-git-wasip1-stub.patch @@ -0,0 +1,10 @@ +--- /dev/null ++++ b/vendor/github.com/go-git/go-git/v5/worktree_wasip1.go +@@ -0,0 +1,7 @@ ++//go:build wasip1 ++ ++package git ++ ++func isSymlinkWindowsNonAdmin(err error) bool { ++ return false ++} diff --git a/registry/software/slack-cli/agent-os-package.json b/registry/software/slack-cli/agent-os-package.json new file mode 100644 index 000000000..b88ffee4b --- /dev/null +++ b/registry/software/slack-cli/agent-os-package.json @@ -0,0 +1,7 @@ +{ + "name": "@rivet-dev/agent-os-slack-cli", + "type": "wasm", + "description": "Slack CLI for building apps on the Slack Platform", + "aptName": "slack-cli", + "source": "go" +} diff --git a/registry/software/slack-cli/package.json b/registry/software/slack-cli/package.json new file mode 100644 index 000000000..48cbecc53 --- /dev/null +++ b/registry/software/slack-cli/package.json @@ -0,0 +1,28 @@ +{ + "name": "@rivet-dev/agent-os-slack-cli", + "version": "0.0.0", + "type": "module", + "license": "Apache-2.0", + "description": "Slack CLI for building apps on the Slack Platform for agentOS", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "wasm" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc", + "check-types": "tsc --noEmit" + }, + "devDependencies": { + "@rivet-dev/agent-os-registry-types": "link:../../../packages/registry-types", + "@types/node": "^22.10.2", + "typescript": "^5.9.2" + } +} diff --git a/registry/software/slack-cli/src/index.ts b/registry/software/slack-cli/src/index.ts new file mode 100644 index 000000000..c32b9e2a5 --- /dev/null +++ b/registry/software/slack-cli/src/index.ts @@ -0,0 +1,18 @@ +import type { WasmCommandPackage } from "@rivet-dev/agent-os-registry-types"; +import { resolve, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const pkg = { + name: "slack-cli", + aptName: "slack-cli", + description: "Slack CLI for building apps on the Slack Platform", + source: "go" as const, + commands: [{ name: "slack", permissionTier: "full" as const }], + get commandDir() { + return resolve(__dirname, "..", "wasm"); + }, +} satisfies WasmCommandPackage; + +export default pkg; diff --git a/registry/software/slack-cli/tsconfig.json b/registry/software/slack-cli/tsconfig.json new file mode 100644 index 000000000..8f24167af --- /dev/null +++ b/registry/software/slack-cli/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"] +}