Docs SDKs Reference

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/sdk

register

gr.register(spec: ServiceSpec) → Promise<Service>

Register a service with the daemon. Idempotent — calling register for 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])