Class: Space::Architect::JobsClient

Inherits:
Object
  • Object
show all
Defined in:
lib/space_architect/jobs_client.rb

Overview

HTTP client for the space-server jobs API, modeled on RunCreator: Bearer auth, injectable async-http client, Space::Core::Error on any failure.

Constant Summary collapse

POLL_INTERVAL_SECONDS =

Poll bound for #wait_for_run_id: 2s * 30 attempts = 60s. Both are injectable kwargs so tests need not sleep real seconds.

2
POLL_MAX_ATTEMPTS =
30

Instance Method Summary collapse

Constructor Details

#initialize(host, token, client: nil) ⇒ JobsClient

Returns a new instance of JobsClient.



16
17
18
19
20
# File 'lib/space_architect/jobs_client.rb', line 16

def initialize(host, token, client: nil)
  @host   = host.chomp("/")
  @token  = token
  @client = client
end

Instance Method Details

#cancel(id) ⇒ Object

POST /jobs/:id/cancel — returns "status"=>"canceled".



50
51
52
53
54
55
56
57
# File 'lib/space_architect/jobs_client.rb', line 50

def cancel(id)
  Sync do
    with_client do |c|
      response = c.post("/jobs/#{id}/cancel", headers: headers, body: nil)
      parse_json(response, "POST /jobs/#{id}/cancel")
    end
  end
end

#create(spec) ⇒ Object

POST /jobs — submits a job spec (harness/prompt/environment) and returns the created job's integer id. Raises Space::Core::Error on any failure, modeled on RunCreator#create (same 20x-status + integer-id checks).



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/space_architect/jobs_client.rb', line 35

def create(spec)
  Sync do
    with_client do |c|
      response = c.post("/jobs", headers: headers, body: JSON.generate(spec))
      parsed   = parse_json(response, "POST /jobs", expected_status: 201)
      id       = parsed["id"]
      raise Space::Core::Error, "POST /jobs: missing or non-integer id in response: #{parsed.inspect[0, 200]}" \
        unless id.is_a?(Integer)

      id
    end
  end
end

#listObject

GET /jobs — returns the "jobs" array (owner-scoped, newest-first).



23
24
25
# File 'lib/space_architect/jobs_client.rb', line 23

def list
  get_json("/jobs")["jobs"]
end

#show(id) ⇒ Object

GET /jobs/:id — returns the job JSON.



28
29
30
# File 'lib/space_architect/jobs_client.rb', line 28

def show(id)
  get_json("/jobs/#{id}")
end

#stream(run_id, &block) ⇒ Object

GET /runs/:id/stream — yields each SSE event's data: payload to the block as it arrives. Returns when the stream reports run_complete or closes cleanly. Raises Space::Core::Error on a non-200 response.



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/space_architect/jobs_client.rb', line 62

def stream(run_id, &block)
  Sync do
    with_client do |c|
      response = c.get("/runs/#{run_id}/stream", headers: headers)
      unless response.status == 200
        body = response.read || ""
        raise Space::Core::Error, "GET /runs/#{run_id}/stream failed (#{response.status}): #{body[0, 200]}"
      end

      read_sse(response, &block)
    end
  end
end

#wait_for_run_id(id, interval: POLL_INTERVAL_SECONDS, attempts: POLL_MAX_ATTEMPTS) ⇒ Object

Polls GET /jobs/:id until run_id is present, waiting interval seconds between attempts, up to attempts tries. Raises Space::Core::Error if the bound is exceeded.

Raises:



79
80
81
82
83
84
85
86
# File 'lib/space_architect/jobs_client.rb', line 79

def wait_for_run_id(id, interval: POLL_INTERVAL_SECONDS, attempts: POLL_MAX_ATTEMPTS)
  attempts.times do |i|
    run_id = show(id)["run_id"]
    return run_id if run_id
    sleep interval unless i == attempts - 1
  end
  raise Space::Core::Error, "job #{id}: no run_id after #{attempts} attempts (#{attempts * interval}s)"
end