SDK reference
Same primitives in every language: register, resolve, start,
stop, waitFor, and events.
Each SDK is a thin client over the local daemon at http://127.0.0.1:11611 — there are no remote
services, no API keys.
Common shape
Every SDK exposes a single top-level gr object (or module) with the same surface:
// pseudo-typescript interface Gretl { register(svc: ServiceSpec): Promise<Service> resolve(name: string): Promise<{ url: string, port: number }> url(name: string): string // sync, throws if unknown start(name: string): Promise<Service> stop(name: string): Promise<void> ensure(name: string): Promise<Service> // start if not running waitFor(name: string, opts?): Promise<Service> status(name?): Promise<Service[]> }Node — @gretl/sdk
# install npm i @gretl/sdkregister
gr.register(spec: ServiceSpec) → Promise<Service>Register a service with the daemon. Idempotent — calling
registerfor an existing name updates its spec.
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | yes | Globally unique within this machine. |
| port | number | yes | Listening port. Gretl enforces uniqueness. |
| cmd | string | no | Shell command to launch the service. |
| cwd | string | no | Working directory for cmd. Defaults to process.cwd(). |
| env | Record<string,string> | no | Extra env vars merged into the child process. |
| group | string | no | Group name (e.g. "@jobs") for batch ops. |
| ready | string | (() ⇒ bool) | no | Health check URL or predicate. Used by waitFor. |
| dependsOn | string[] | no | Service names that must be running first. |
import { gr } from '@gretl/sdk'; await gr.register({ name: 'jobs-server', port: 7401, cmd: 'npm run dev:server', group: '@jobs', ready: 'http://localhost:7401/health', });
resolve / url
gr.resolve(name: string) → Promise<{ url, port }>
gr.url(name: string) → string // sync
Look up a service's URL. Use resolve when the daemon may need to start it; use url in
hot paths where you've already ensured it.
const { url } = await gr.resolve('jobs-server'); const res = await fetch(`${url}/health`);
ensure / waitFor
gr.ensure(name: string) → Promise<Service>
gr.waitFor(name: string, opts?: { timeout }) → Promise<Service>
ensure is the workhorse — it starts the service if needed, waits for the ready-check, and returns
once it's healthy. waitFor only waits; it doesn't start.
// in your test setup beforeAll(async () => { await gr.ensure('postgres'); await gr.ensure('jobs-server'); });
events
gr.events() → AsyncIterable<Event>
Subscribe to lifecycle events. Backed by the SSE stream at /v1/events.
for await (const ev of gr.events()) { if (ev.type === 'service.crashed') { console.error(`${ev.name} died:`, ev.exitCode); } }
Kubernetes — Node
K8s methods talk to the Gretl cloud API (api.gretl.dev). Set GR_TOKEN env var or pass it as a constructor option.
import { gr } from '@gretl/sdk'; // List clusters and workloads const clusters = await gr.k8s.clusters(); const workloads = await gr.k8s.workloads({ clusterId: clusters[0].id }); // Sleep / wake a deployment await gr.k8s.sleep(workloads[0].id); await gr.k8s.wake(workloads[0].id); // Open a port-forward tether (returns localhost:PORT) const tether = await gr.k8s.tether(workloads[0].id, { containerPort: 8000 }); console.log(`Access at localhost:${tether.local_port}`); // Sync workloads from cluster await gr.k8s.sync(clusters[0].id);
Python — gretl
# install pip install gretl
from gretl import gr # Register a port (creates or updates) gr.add(9001, name="Worker", cmd="node worker.js", group="Backend") # Start / stop / list gr.start("Worker") gr.stop("Worker") ports = gr.ports() # Wait for a port to come up (e.g. in tests) gr.wait_for_active("Worker", timeout=30)
Kubernetes — Python
from gretl import gr # List clusters and workloads clusters = gr.k8s.clusters() workloads = gr.k8s.workloads(cluster_id=clusters[0]["id"]) # Sleep / wake gr.k8s.sleep(workloads[0]["id"]) gr.k8s.wake(workloads[0]["id"]) # Open a port-forward tether tether = gr.k8s.tether(workloads[0]["id"], container_port=8000) print(f"Access at localhost:{tether['local_port']}") # Sync workloads from cluster gr.k8s.sync(clusters[0]["id"])
Go — gretl-go
// install go get github.com/slowdutch/gretl-sdks/go
import "github.com/slowdutch/gretl-sdks/go" client := gretl.New() // Register a port (creates or updates) client.Add(9001, gretl.AddOptions{ Name: "Worker", Cmd: "node worker.js", Group: "Backend", }) // Start / stop / list client.Start("Worker") client.Stop("Worker") ports, _ := client.Ports()
Kubernetes — Go
import gretl "github.com/slowdutch/gretl-sdks/go" client := gretl.New(gretl.WithToken(os.Getenv("GR_TOKEN"))) // List clusters and workloads clusters, _ := client.K8s.Clusters() workloads, _ := client.K8s.Workloads(gretl.WorkloadOpts{ClusterID: clusters[0].ID}) // Sleep / wake client.K8s.Sleep(workloads[0].ID) client.K8s.Wake(workloads[0].ID) // Open a port-forward tether tether, _ := client.K8s.Tether(workloads[0].ID, 8000) fmt.Printf("Access at localhost:%d\n", tether.LocalPort) // Sync workloads client.K8s.Sync(clusters[0].ID)
Ruby — gretl
# install gem install gretl
require "gretl" # Register a port (creates or updates) Gretl::PA.add(9001, name: "Worker", cmd: "node worker.js", group: "Backend") # Start / stop / list Gretl::PA.start("Worker") Gretl::PA.stop("Worker") ports = Gretl::PA.ports
Kubernetes — Ruby
require "gretl" client = Gretl::Client.new(token: ENV["GR_TOKEN"]) # List clusters and workloads clusters = client.k8s.clusters workloads = client.k8s.workloads(cluster_id: clusters.first[:id]) # Sleep / wake client.k8s.sleep(workloads.first[:id]) client.k8s.wake(workloads.first[:id]) # Open a port-forward tether tether = client.k8s.tether(workloads.first[:id], container_port: 8000) puts "Access at localhost:#{tether[:local_port]}" # Sync workloads from cluster client.k8s.sync(clusters.first[:id])