diff --git a/assets/agents/.sources.json b/assets/agents/.sources.json index 100af3a2..c21d054e 100644 --- a/assets/agents/.sources.json +++ b/assets/agents/.sources.json @@ -34,5 +34,9 @@ "pi": { "url": "custom:shittycodingagent.ai/logo.svg (official Pi logo, converted to PNG with dark background)", "ext": "png" + }, + "t3code": { + "url": "https://avatars.githubusercontent.com/u/59054278?s=200&v=4", + "ext": "png" } } diff --git a/assets/agents/t3code.png b/assets/agents/t3code.png new file mode 100644 index 00000000..bbfd2818 Binary files /dev/null and b/assets/agents/t3code.png differ diff --git a/manifest.json b/manifest.json index 441a6d5c..b58bb845 100644 --- a/manifest.json +++ b/manifest.json @@ -340,6 +340,43 @@ "agentic", "cursor" ] + }, + "t3code": { + "name": "T3 Code", + "description": "Minimal web GUI for coding agents by Ping.gg — wraps Claude Code and Codex with a browser-based interface", + "url": "https://github.com/pingdotgg/t3code", + "install": "npm install -g t3", + "launch": "t3", + "env": { + "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}", + "ANTHROPIC_BASE_URL": "https://openrouter.ai/api", + "ANTHROPIC_API_KEY": "${OPENROUTER_API_KEY}", + "OPENAI_API_KEY": "${OPENROUTER_API_KEY}", + "OPENAI_BASE_URL": "https://openrouter.ai/api/v1" + }, + "notes": "Web GUI that spawns Claude Code and Codex as subprocesses via node-pty. OpenRouter integration works through inherited env vars on the child agent processes. Requires Node.js 22+. Default port 3773.", + "icon": "https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/assets/agents/t3code.png", + "featured_cloud": [ + "digitalocean", + "sprite" + ], + "creator": "Ping.gg", + "repo": "pingdotgg/t3code", + "license": "MIT", + "created": "2025-06", + "added": "2026-04", + "github_stars": 9500, + "stars_updated": "2026-04-18", + "language": "TypeScript", + "runtime": "node", + "category": "gui", + "tagline": "Minimal web GUI for coding agents — browse, code, and ship via Claude or Codex", + "tags": [ + "coding", + "web-gui", + "wrapper", + "pinggg" + ] } }, "clouds": { @@ -518,6 +555,13 @@ "digitalocean/cursor": "implemented", "gcp/cursor": "implemented", "daytona/cursor": "implemented", - "sprite/cursor": "implemented" + "sprite/cursor": "implemented", + "local/t3code": "implemented", + "hetzner/t3code": "implemented", + "aws/t3code": "implemented", + "digitalocean/t3code": "implemented", + "gcp/t3code": "implemented", + "daytona/t3code": "implemented", + "sprite/t3code": "implemented" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 9d9978a7..8fb6035a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "1.0.16", + "version": "1.0.17", "type": "module", "bin": { "spawn": "cli.js" diff --git a/packages/cli/src/__tests__/manifest-type-contracts.test.ts b/packages/cli/src/__tests__/manifest-type-contracts.test.ts index b0b0a96c..6011bf13 100644 --- a/packages/cli/src/__tests__/manifest-type-contracts.test.ts +++ b/packages/cli/src/__tests__/manifest-type-contracts.test.ts @@ -361,6 +361,7 @@ describe("Agent metadata field types", () => { [ "cli", "tui", + "gui", "ide-extension", ], `agent "${key}" category value`, diff --git a/packages/cli/src/shared/agent-setup.ts b/packages/cli/src/shared/agent-setup.ts index f2ebff21..50eb8d8d 100644 --- a/packages/cli/src/shared/agent-setup.ts +++ b/packages/cli/src/shared/agent-setup.ts @@ -1399,6 +1399,34 @@ function createAgents(runner: CloudRunner): Record { updateCmd: `${NPM_AUTO_UPDATE_SETUP} && ` + "npm install -g $_NPM_G_FLAGS @mariozechner/pi-coding-agent@latest", }, + t3code: { + name: "T3 Code", + cloudInitTier: "node" satisfies AgentConfig["cloudInitTier"], + preProvision: detectGithubAuth, + install: () => + installAgent( + runner, + "T3 Code", + `${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} t3 && ${NPM_GLOBAL_PATH_PERSIST}`, + ), + envVars: (apiKey) => [ + `OPENROUTER_API_KEY=${apiKey}`, + `ANTHROPIC_API_KEY=${apiKey}`, + "ANTHROPIC_BASE_URL=https://openrouter.ai/api", + `OPENAI_API_KEY=${apiKey}`, + "OPENAI_BASE_URL=https://openrouter.ai/api/v1", + ], + preLaunchMsg: "T3 Code web GUI will open automatically — use it to interact with Claude Code and Codex agents.", + launchCmd: () => + "source ~/.spawnrc 2>/dev/null; source ~/.zshrc 2>/dev/null; t3 --port 3773 --host 0.0.0.0 --no-browser", + tunnel: { + remotePort: 3773, + browserUrl: (localPort: number) => `http://localhost:${localPort}`, + }, + updateCmd: + 'export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$PATH"; ' + "npm install -g ${_NPM_G_FLAGS:-} t3@latest", + }, + cursor: { name: "Cursor CLI", cloudInitTier: "bun", diff --git a/sh/aws/README.md b/sh/aws/README.md index 357d2150..f9052d88 100644 --- a/sh/aws/README.md +++ b/sh/aws/README.md @@ -66,6 +66,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/aws/t3code.sh) +``` + ## Non-Interactive Mode ```bash diff --git a/sh/aws/t3code.sh b/sh/aws/t3code.sh new file mode 100644 index 00000000..7b7555e9 --- /dev/null +++ b/sh/aws/t3code.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled aws.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/aws/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/aws/main.ts" t3code "$@" +fi + +# Remote — download and run compiled TypeScript bundle +AWS_JS=$(mktemp) +trap 'rm -f "$AWS_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/aws-latest/aws.js" -o "$AWS_JS" \ + || { printf '\033[0;31mFailed to download aws.js\033[0m\n' >&2; exit 1; } +exec bun run "$AWS_JS" t3code "$@" diff --git a/sh/daytona/README.md b/sh/daytona/README.md index 59496345..1fef04c9 100644 --- a/sh/daytona/README.md +++ b/sh/daytona/README.md @@ -60,6 +60,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/daytona/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/daytona/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/daytona/t3code.sh) +``` + ## Non-Interactive Mode ```bash diff --git a/sh/daytona/t3code.sh b/sh/daytona/t3code.sh new file mode 100644 index 00000000..1c635a91 --- /dev/null +++ b/sh/daytona/t3code.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled daytona.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/daytona/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/daytona/main.ts" t3code "$@" +fi + +# Remote — download bundled daytona.js from GitHub release +DAYTONA_JS=$(mktemp) +trap 'rm -f "$DAYTONA_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/daytona-latest/daytona.js" -o "$DAYTONA_JS" \ + || { printf '\033[0;31mFailed to download daytona.js\033[0m\n' >&2; exit 1; } + +exec bun run "$DAYTONA_JS" t3code "$@" diff --git a/sh/digitalocean/README.md b/sh/digitalocean/README.md index 6a25aa35..12197b44 100644 --- a/sh/digitalocean/README.md +++ b/sh/digitalocean/README.md @@ -58,6 +58,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/digitalocean/t3code.sh) +``` + ## Environment Variables | Variable | Description | Default | diff --git a/sh/digitalocean/t3code.sh b/sh/digitalocean/t3code.sh new file mode 100644 index 00000000..98daa064 --- /dev/null +++ b/sh/digitalocean/t3code.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled digitalocean.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/digitalocean/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/digitalocean/main.ts" t3code "$@" +fi + +# Remote — download and run compiled TypeScript bundle +DO_JS=$(mktemp) +trap 'rm -f "$DO_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \ + || { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; } +exec bun run "$DO_JS" t3code "$@" diff --git a/sh/gcp/README.md b/sh/gcp/README.md index 81276cbe..0f9adb33 100644 --- a/sh/gcp/README.md +++ b/sh/gcp/README.md @@ -60,6 +60,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/gcp/t3code.sh) +``` + ## Non-Interactive Mode ```bash diff --git a/sh/gcp/t3code.sh b/sh/gcp/t3code.sh new file mode 100644 index 00000000..2ace3cea --- /dev/null +++ b/sh/gcp/t3code.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled gcp.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/gcp/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/gcp/main.ts" t3code "$@" +fi + +# Remote — download and run compiled TypeScript bundle +GCP_JS=$(mktemp) +trap 'rm -f "$GCP_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/gcp-latest/gcp.js" -o "$GCP_JS" \ + || { printf '\033[0;31mFailed to download gcp.js\033[0m\n' >&2; exit 1; } +exec bun run "$GCP_JS" t3code "$@" diff --git a/sh/hetzner/README.md b/sh/hetzner/README.md index 6b125c30..7241aeec 100644 --- a/sh/hetzner/README.md +++ b/sh/hetzner/README.md @@ -58,6 +58,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/hetzner/t3code.sh) +``` + ## Non-Interactive Mode ```bash diff --git a/sh/hetzner/t3code.sh b/sh/hetzner/t3code.sh new file mode 100644 index 00000000..8642f958 --- /dev/null +++ b/sh/hetzner/t3code.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled hetzner.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/hetzner/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/hetzner/main.ts" t3code "$@" +fi + +# Remote — download and run compiled TypeScript bundle +HETZNER_JS=$(mktemp) +trap 'rm -f "$HETZNER_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/hetzner-latest/hetzner.js" -o "$HETZNER_JS" \ + || { printf '\033[0;31mFailed to download hetzner.js\033[0m\n' >&2; exit 1; } +exec bun run "$HETZNER_JS" t3code "$@" diff --git a/sh/local/README.md b/sh/local/README.md index 007f13d3..e731ffab 100644 --- a/sh/local/README.md +++ b/sh/local/README.md @@ -18,6 +18,7 @@ spawn hermes local spawn junie local spawn cursor local spawn pi local +spawn t3code local ``` Or run directly without the CLI: @@ -32,6 +33,7 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/hermes.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/junie.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/pi.sh) +bash <(curl -fsSL https://openrouter.ai/labs/spawn/local/t3code.sh) ``` ## Non-Interactive Mode diff --git a/sh/local/t3code.sh b/sh/local/t3code.sh new file mode 100644 index 00000000..f6e55464 --- /dev/null +++ b/sh/local/t3code.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled local.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/local/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/local/main.ts" t3code "$@" +fi + +# Remote — download bundled local.js from GitHub release +LOCAL_JS=$(mktemp) +trap 'rm -f "$LOCAL_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/local-latest/local.js" -o "$LOCAL_JS" \ + || { printf '\033[0;31mFailed to download local.js\033[0m\n' >&2; exit 1; } + +exec bun run "$LOCAL_JS" t3code "$@" diff --git a/sh/sprite/README.md b/sh/sprite/README.md index eaa4a837..9512eae1 100644 --- a/sh/sprite/README.md +++ b/sh/sprite/README.md @@ -58,6 +58,12 @@ bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/cursor.sh) bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/pi.sh) ``` +#### T3 Code + +```bash +bash <(curl -fsSL https://openrouter.ai/labs/spawn/sprite/t3code.sh) +``` + ## Non-Interactive Mode ```bash diff --git a/sh/sprite/t3code.sh b/sh/sprite/t3code.sh new file mode 100644 index 00000000..21b69a99 --- /dev/null +++ b/sh/sprite/t3code.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eo pipefail + +# Thin shim: ensures bun is available, runs bundled sprite.js (local or from GitHub release) + +_ensure_bun() { + if command -v bun &>/dev/null; then return 0; fi + printf '\033[0;36mInstalling bun...\033[0m\n' >&2 + curl -fsSL --proto '=https' --show-error https://bun.sh/install?version=1.3.9 | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; } + export PATH="$HOME/.bun/bin:$PATH" + command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; } +} + +_ensure_bun + +# SPAWN_CLI_DIR override — force local source (used by e2e tests) +if [[ -n "${SPAWN_CLI_DIR:-}" && -f "$SPAWN_CLI_DIR/packages/cli/src/sprite/main.ts" ]]; then + exec bun run "$SPAWN_CLI_DIR/packages/cli/src/sprite/main.ts" t3code "$@" +fi + +# Remote — download and run compiled TypeScript bundle +SPRITE_JS=$(mktemp) +trap 'rm -f "$SPRITE_JS"' EXIT +curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/sprite-latest/sprite.js" -o "$SPRITE_JS" \ + || { printf '\033[0;31mFailed to download sprite.js\033[0m\n' >&2; exit 1; } +exec bun run "$SPRITE_JS" t3code "$@"