@go-labs-sg/gosh
v0.8.3
Published
GOSH CLI for organizers and AI agents — manage organizations, events, teams, stations, missions, templates, and live operations through the GOSH API.
Readme
GOSH CLI
@go-labs-sg/gosh provides the gosh binary for permission-aware GOSH organizer operations and agent automation. It calls the same tRPC procedures as the web app using the authorizing user's global, organization, and event permissions.
Runtime and installation
The CLI requires Bun 1.4.0 or newer. npm remains the package distribution channel, while the installed gosh command runs under Bun.
Check whether a compatible Bun version is already available:
bun --versionIf Bun is missing, install it for the current operating system:
macOS (Homebrew)
brew install oven-sh/bun/bunWindows (PowerShell)
powershell -c "irm bun.sh/install.ps1|iex"Linux
curl -fsSL https://bun.com/install | bashIf Bun is installed but older than 1.4.0, run bun upgrade. Open a new terminal if the installer changes PATH, then verify bun --version again. Installation agents should skip the platform installer when Bun 1.4.0 or newer is already available.
Install and verify the CLI:
bun add --global @go-labs-sg/gosh
gosh versionAuthentication
gosh auth login
gosh auth status
gosh auth whoamigosh auth login opens GOSH in your browser. Sign in through the normal web flow if needed, confirm that the displayed device and code match your terminal, and click Authorize CLI. The CLI polls automatically, then stores the product-issued credentials exclusively through Bun.secrets in the operating system credential store. No Google credentials are stored by the CLI.
GOSH CLI access is limited to global administrators. The server rechecks the account's current global role and ban state when issuing, refreshing, and authenticating CLI credentials, so removing the admin role immediately blocks further CLI access.
Use gosh auth logout to revoke the remote CLI session and remove the local credential. Use GOSH_API_URL or --api-url to target another HTTPS deployment; plain HTTP is accepted only for localhost development. Pass --no-browser when working over SSH or in a container and open the printed URL on another device.
Commands and JSON input
Discover the current command surface instead of guessing payloads:
gosh commands
gosh help event list
gosh health status
gosh organization list
gosh event list --input '{"organizationId":"acme"}'
gosh event list-page --input '{"organizationId":"acme","page":1,"pageSize":25,"search":"race","status":"LIVE"}'
gosh event get --input '{"organizationId":"acme","eventId":"event-id"}'Commands use --input '<json>' for the exact validated tRPC input object. This preserves the GOSH API's typed validation without maintaining a second set of lossy CLI flags. See command-reference.md or import @go-labs-sg/gosh/command-manifest for the generated catalogue.
gosh event list-page returns { items, page, pageSize, pageCount, totalCount }. It defaults to page 1 with 25 results, accepts page sizes from 1 to 100, and supports server-side search and status filters. Archived events are excluded unless an explicit status is supplied. Request subsequent pages explicitly; the CLI does not automatically fetch every page. The existing gosh event list command and its array response remain unchanged.
The command registry covers every callable GOSH tRPC procedure: organizer and global-admin operations, organization invitations and membership, player workflows, public results/login procedures, uploads, and realtime subscriptions. The compile-time registry test fails whenever a new API procedure is added without a CLI command.
Templates and organization scope
Template commands use the selected organization as their scope. Provide its ID in the input when listing or retrieving a template:
gosh template list --input '{"organizationId":"acme"}'
gosh template get --input '{"organizationId":"acme","templateId":"template-id"}'Organization members can use only templates from that organization. Global admins
can inspect templates from all organizations with allOrganizations: true, then
use any returned template when creating an event for a chosen organization:
gosh template list --input '{"allOrganizations":true}'
gosh event create-from-template \
--input '{"name":"Team challenge","organizationId":"acme","templateId":"template-id"}' \
--target template-id \
--allow-state-changeOrganization provisioning
Only global administrators can provision an organization. gosh organization create
requires name, slug, and ownerEmail; it creates the organization with a
pending first-owner invitation and emails that address. The provisioner does not
become a member or owner. The invited person must sign in with that email and
accept the invitation before they can manage the organization.
gosh organization create \
--input '{"name":"Acme Events","slug":"acme-events","ownerEmail":"[email protected]"}' \
--target acme-events \
--allow-state-change \
--allow-email \
--allow-external-writeThe response includes the pending owner invitation and its delivery status; do not treat the provisioner as the customer owner.
Shared stations and team routes
Create each station once for an event. In a team event, link the same station to
each team's route with its own order. team set-station-routes replaces one
team's complete route, so provide every station that team should see in order:
gosh team station-routes --input '{"eventId":"event-id"}'
gosh team set-station-routes \
--input '{"eventId":"event-id","teamId":"team-red","stationIds":["station-2","station-1"]}' \
--target team-red \
--allow-state-changeTo update an existing route's ordering without changing its station links, use
gosh station reorder with the team as scopeTeamId.
gosh station clone preserves the source station's team links when
targetTeamId is omitted, appending the clone to each linked team's route.
Provide a team ID to target only that team, or null to add the clone to the
event catalog without explicit team links.
When creating or replacing a template, each station can include teamOrders to
preserve its route order for specific teams. teamOrder identifies the matching
value in that template team's order field:
{
"stations": [
{
"name": "Welcome station",
"teamOrders": [{ "teamOrder": 0, "order": 2 }]
}
]
}Local file uploads
Upload commands add the local file metadata to the supplied API input, request a permission-scoped signed URL, and upload the bytes directly to storage without sending the GOSH CLI access token to storage:
gosh upload branding \
--file ./logo.png \
--input '{"eventId":"event-id","assetType":"logo"}' \
--target event-id \
--allow-state-change \
--allow-external-write
gosh player mission upload \
--file ./proof.mp4 \
--input '{"eventId":"event-id","missionId":"mission-id"}' \
--target mission-id \
--allow-state-change \
--allow-external-writeMission videos use GOSH's multipart upload protocol automatically. Failed multipart uploads are aborted best-effort, and signed URLs are never included in the final JSON envelope or debug output.
Submission media exports
Queue all original photo and video submissions as a ZIP organized by event and then by team or individual participant. GOSH emails the requester when the archive is ready and keeps it available for 30 days.
gosh event submissions request-image-export \
--input '{"organizationId":"acme","eventId":"event-id"}' \
--target event-id \
--allow-state-change \
--allow-email \
--allow-external-write
gosh event submissions image-exports \
--input '{"organizationId":"acme","eventId":"event-id"}'Realtime watches
gosh player watch and gosh submission watch keep an authenticated SSE connection open and emit one JSON envelope per line. Press Ctrl-C to close the stream cleanly:
gosh submission watch --input '{"organizationId":"acme","eventId":"event-id"}'Global administration
Global admins can list users, change global roles, and ban or unban accounts under gosh admin users …. Banning an account atomically revokes its active browser and CLI sessions.
Mutation safety
Mutations require --target to exactly match an identifier in --input (such as an eventId, stationId, slug, or name), binding the confirmation to the payload. In non-interactive automation, every applicable effect flag is also required:
--allow-state-change--allow-email--allow-external-write--allow-delete
Example after reviewing the event and obtaining explicit approval:
gosh event update-status \
--input '{"organizationId":"acme","eventId":"event-id","status":"LIVE"}' \
--target 'event-id' \
--allow-state-changeWithout the allow flags, an interactive terminal prints a workflow preview and requires the exact response CONFIRM. Read-only commands never prompt.
Output
One-shot commands write one JSON envelope to stdout. Watch commands write newline-delimited JSON envelopes. Failures and opt-in --debug diagnostics go to stderr, and known secrets, payloads, join codes, share tokens, and signed URLs are redacted. --quiet or -q suppresses diagnostics.
Release verification
The gosh-cli-v* release workflow runs
bun run packages/scripts/monorepo/verify-cli-release-baseline.ts gosh to check
and typecheck the shared credential store, typecheck the retained test setup,
and check, typecheck, and build the GOSH CLI. It then validates the packed
artifact and runs both the installed entry point and gosh version before
publishing.
Behavior changes are verified with focused temporary tests before the PR,
following the repository's temporary-test workflow in AGENTS.md. Those tests
are removed before delivery, so the release workflow does not invoke the retired
credential-store, architecture, or product regression suites.
