hermes-client 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,394 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hermes_agent/client/entity"
4
+
5
+ module HermesAgent
6
+ class Client
7
+ module Entities
8
+ ##
9
+ # The repeat policy of a {Job} ({Job#repeat}): how many times the job runs
10
+ # before the server deletes it, and how many runs it has completed so far.
11
+ #
12
+ class JobRepeat < Entity
13
+ ##
14
+ # The maximum number of runs, or `nil` for an unbounded (uncapped)
15
+ # recurring job. Once a capped job reaches this many runs the server
16
+ # deletes it.
17
+ # @return [Integer, nil]
18
+ #
19
+ def times
20
+ self["times"]
21
+ end
22
+
23
+ ##
24
+ # The number of runs completed so far (incremented per executed run).
25
+ # @return [Integer, nil]
26
+ #
27
+ def completed
28
+ self["completed"]
29
+ end
30
+ end
31
+
32
+ ##
33
+ # The schedule of a {Job} ({Job#schedule}): a tagged union discriminated by
34
+ # {#kind}. The create/update `schedule` string is parsed server-side into
35
+ # one of three kinds, each carrying its own payload:
36
+ #
37
+ # - `"once"` — a one-shot run at {#run_at} (an ISO-8601 string).
38
+ # - `"interval"` — a recurring run every {#minutes} minutes.
39
+ # - `"cron"` — a recurring run on the cron expression {#expr}.
40
+ #
41
+ # Use the {#once?} / {#interval?} / {#cron?} predicates to discriminate.
42
+ # The payload readers ({#run_at} / {#minutes} / {#expr}) return `nil` when
43
+ # the schedule is not of their kind. {#display} is a human-readable form of
44
+ # the schedule for any kind. Field readers are best-effort; {#to_h} remains
45
+ # the source of truth.
46
+ #
47
+ class JobSchedule < Entity
48
+ ##
49
+ # The schedule kind: `"once"`, `"interval"`, or `"cron"`.
50
+ # @return [String, nil]
51
+ #
52
+ def kind
53
+ self["kind"]
54
+ end
55
+
56
+ ##
57
+ # Whether this is a one-shot (`"once"`) schedule.
58
+ # @return [boolean]
59
+ #
60
+ def once?
61
+ kind == "once"
62
+ end
63
+
64
+ ##
65
+ # Whether this is a recurring interval (`"interval"`) schedule.
66
+ # @return [boolean]
67
+ #
68
+ def interval?
69
+ kind == "interval"
70
+ end
71
+
72
+ ##
73
+ # Whether this is a recurring cron (`"cron"`) schedule.
74
+ # @return [boolean]
75
+ #
76
+ def cron?
77
+ kind == "cron"
78
+ end
79
+
80
+ ##
81
+ # A human-readable rendering of the schedule (e.g. `"every 120m"`,
82
+ # `"0 9 * * *"`, or `"once at 2027-02-03 14:00"`).
83
+ # @return [String, nil]
84
+ #
85
+ def display
86
+ self["display"]
87
+ end
88
+
89
+ ##
90
+ # The scheduled run time of a `"once"` schedule, as an ISO-8601 string.
91
+ # Returns `nil` when the schedule is not of kind `"once"`.
92
+ # @return [String, nil]
93
+ #
94
+ def run_at
95
+ self["run_at"]
96
+ end
97
+
98
+ ##
99
+ # The interval in minutes of an `"interval"` schedule. Returns `nil` when
100
+ # the schedule is not of kind `"interval"`.
101
+ # @return [Integer, nil]
102
+ #
103
+ def minutes
104
+ self["minutes"]
105
+ end
106
+
107
+ ##
108
+ # The cron expression of a `"cron"` schedule. Returns `nil` when the
109
+ # schedule is not of kind `"cron"`.
110
+ # @return [String, nil]
111
+ #
112
+ def expr
113
+ self["expr"]
114
+ end
115
+ end
116
+
117
+ ##
118
+ # A scheduled background job from the Jobs API (`/api/jobs`): a cron-like
119
+ # recurring task, a one-shot deferred task, or a watchdog script. The same
120
+ # object is returned by create, get, list, update, pause, resume, and
121
+ # trigger.
122
+ #
123
+ # Note the timestamp fields ({#created_at} / {#next_run_at} /
124
+ # {#last_run_at} / {#paused_at}) are **ISO-8601 strings with an offset**,
125
+ # not the epoch floats the runs and responses APIs use; they are exposed
126
+ # verbatim with no Time parsing. The override slots ({#model} /
127
+ # {#provider} / {#base_url} / {#workdir} / {#profile} / {#context_from}) are
128
+ # **read-only** here — the HTTP API ignores them on create/update, so they
129
+ # are surfaced as readers but are not create/update parameters.
130
+ #
131
+ # Field readers are best-effort; {#to_h} remains the source of truth.
132
+ #
133
+ class Job < Entity
134
+ ##
135
+ # The job id: 12 lowercase hex characters (no `run_`-style prefix).
136
+ # @return [String, nil]
137
+ #
138
+ def id
139
+ self["id"]
140
+ end
141
+
142
+ ##
143
+ # The human-friendly job name (required on create).
144
+ # @return [String, nil]
145
+ #
146
+ def name
147
+ self["name"]
148
+ end
149
+
150
+ ##
151
+ # The task instruction given to the agent each run. `nil` for a
152
+ # `no_agent` script job, which needs none.
153
+ # @return [String, nil]
154
+ #
155
+ def prompt
156
+ self["prompt"]
157
+ end
158
+
159
+ ##
160
+ # The attached skill names. The singular {#skill} is a separate scalar
161
+ # slot.
162
+ # @return [Array<String>, nil]
163
+ #
164
+ def skills
165
+ self["skills"]
166
+ end
167
+
168
+ ##
169
+ # The single attached skill (a scalar slot separate from {#skills});
170
+ # `nil` when unused.
171
+ # @return [String, nil]
172
+ #
173
+ def skill
174
+ self["skill"]
175
+ end
176
+
177
+ ##
178
+ # The per-job model override. **Read-only via this API** (ignored on
179
+ # create/update); `nil` means the profile's configured model is used.
180
+ # @return [String, nil]
181
+ #
182
+ def model
183
+ self["model"]
184
+ end
185
+
186
+ ##
187
+ # The per-job provider override. **Read-only via this API**; `nil` =
188
+ # the profile's configured provider.
189
+ # @return [String, nil]
190
+ #
191
+ def provider
192
+ self["provider"]
193
+ end
194
+
195
+ ##
196
+ # The per-job base URL override. **Read-only via this API**; `nil` =
197
+ # the profile's configured base URL.
198
+ # @return [String, nil]
199
+ #
200
+ def base_url
201
+ self["base_url"]
202
+ end
203
+
204
+ ##
205
+ # The path of a script (under `~/.hermes/scripts/`) run each execution;
206
+ # by default its stdout is injected into the agent prompt.
207
+ # @return [String, nil]
208
+ #
209
+ def script
210
+ self["script"]
211
+ end
212
+
213
+ ##
214
+ # Whether the LLM is skipped entirely: the {#script} runs and its stdout
215
+ # is delivered verbatim (the watchdog pattern). Defaults to `false` (the
216
+ # agent runs) when the field is absent.
217
+ # @return [boolean]
218
+ #
219
+ def no_agent?
220
+ !!self["no_agent"]
221
+ end
222
+
223
+ ##
224
+ # The source to pull run context from. **Read-only via this API**.
225
+ # @return [String, nil]
226
+ #
227
+ def context_from
228
+ self["context_from"]
229
+ end
230
+
231
+ ##
232
+ # The schedule, wrapped in a {JobSchedule}. Returns `nil` when absent.
233
+ # @return [JobSchedule, nil]
234
+ #
235
+ def schedule
236
+ raw = self["schedule"]
237
+ raw.is_a?(::Hash) ? JobSchedule.new(raw) : nil
238
+ end
239
+
240
+ ##
241
+ # A human-readable rendering of the schedule (mirrors
242
+ # {JobSchedule#display}).
243
+ # @return [String, nil]
244
+ #
245
+ def schedule_display
246
+ self["schedule_display"]
247
+ end
248
+
249
+ ##
250
+ # The repeat policy, wrapped in a {JobRepeat}. Returns `nil` when absent.
251
+ # @return [JobRepeat, nil]
252
+ #
253
+ def repeat
254
+ raw = self["repeat"]
255
+ raw.is_a?(::Hash) ? JobRepeat.new(raw) : nil
256
+ end
257
+
258
+ ##
259
+ # Whether the job is enabled (`false` while paused).
260
+ # @return [boolean, nil]
261
+ #
262
+ def enabled?
263
+ self["enabled"]
264
+ end
265
+
266
+ ##
267
+ # The lifecycle state, e.g. `"scheduled"` or `"paused"`. (There is no
268
+ # terminal state — an exhausted one-shot or capped job is deleted by the
269
+ # server.)
270
+ # @return [String, nil]
271
+ #
272
+ def state
273
+ self["state"]
274
+ end
275
+
276
+ ##
277
+ # When the job was paused, as an ISO-8601 string; `nil` when not paused.
278
+ # @return [String, nil]
279
+ #
280
+ def paused_at
281
+ self["paused_at"]
282
+ end
283
+
284
+ ##
285
+ # The optional reason the job was paused; `nil` for a manual pause.
286
+ # @return [String, nil]
287
+ #
288
+ def paused_reason
289
+ self["paused_reason"]
290
+ end
291
+
292
+ ##
293
+ # When the job was created, as an ISO-8601 string with offset.
294
+ # @return [String, nil]
295
+ #
296
+ def created_at
297
+ self["created_at"]
298
+ end
299
+
300
+ ##
301
+ # When the job is next scheduled to run, as an ISO-8601 string with
302
+ # offset.
303
+ # @return [String, nil]
304
+ #
305
+ def next_run_at
306
+ self["next_run_at"]
307
+ end
308
+
309
+ ##
310
+ # When the job last ran, as an ISO-8601 string with offset; `nil` until
311
+ # the first execution.
312
+ # @return [String, nil]
313
+ #
314
+ def last_run_at
315
+ self["last_run_at"]
316
+ end
317
+
318
+ ##
319
+ # The outcome of the most recent run: `"ok"` on success or `"error"`
320
+ # when the run failed; `nil` before the first run.
321
+ # @return [String, nil]
322
+ #
323
+ def last_status
324
+ self["last_status"]
325
+ end
326
+
327
+ ##
328
+ # The error detail from the last execution when {#last_status} is
329
+ # `"error"`, or `nil` on success. The message is prefixed with the
330
+ # failing exception class, e.g. `"RuntimeError: Gemini HTTP 400 …"`.
331
+ # @return [String, nil]
332
+ #
333
+ def last_error
334
+ self["last_error"]
335
+ end
336
+
337
+ ##
338
+ # The error detail from the last delivery attempt, or `nil` on success.
339
+ # @return [String, nil]
340
+ #
341
+ def last_delivery_error
342
+ self["last_delivery_error"]
343
+ end
344
+
345
+ ##
346
+ # The delivery target, e.g. `"local"`, `"origin"`, `"telegram"`, or
347
+ # `"platform:chat_id"`. Defaults to `"local"`.
348
+ # @return [String, nil]
349
+ #
350
+ def deliver
351
+ self["deliver"]
352
+ end
353
+
354
+ ##
355
+ # The originating channel info (for `deliver: "origin"`), passed through
356
+ # raw; `nil` for locally created jobs. Its populated shape was never
357
+ # observed, so it is not wrapped — use {#to_h} / {#[]} if a shape appears.
358
+ # @return [Object, nil]
359
+ #
360
+ def origin
361
+ self["origin"]
362
+ end
363
+
364
+ ##
365
+ # The toolset allowlist, passed through raw; `nil` = the default set.
366
+ # Expected to be a plain array of names if populated (never observed), so
367
+ # it is not wrapped.
368
+ # @return [Object, nil]
369
+ #
370
+ def enabled_toolsets
371
+ self["enabled_toolsets"]
372
+ end
373
+
374
+ ##
375
+ # The absolute working directory for the run. **Read-only via this API**;
376
+ # `nil` = no project context.
377
+ # @return [String, nil]
378
+ #
379
+ def workdir
380
+ self["workdir"]
381
+ end
382
+
383
+ ##
384
+ # The Hermes profile the job runs under. **Read-only via this API**;
385
+ # `nil` = the scheduler's existing profile.
386
+ # @return [String, nil]
387
+ #
388
+ def profile
389
+ self["profile"]
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hermes_agent/client/entity"
4
+
5
+ module HermesAgent
6
+ class Client
7
+ module Entities
8
+ ##
9
+ # A model advertised by the server (one entry of the `GET /v1/models`
10
+ # list). Field readers are best-effort; {#to_h} remains the source of
11
+ # truth.
12
+ #
13
+ # The `permission` field is intentionally not exposed as a reader: it was
14
+ # observed empty, its element type is unknown (and likely a nested
15
+ # object), so callers should reach it via {#[]} / {#to_h} for now.
16
+ #
17
+ class Model < Entity
18
+ ##
19
+ # The model identifier, e.g. `"hermes-test"`.
20
+ # @return [String, nil]
21
+ #
22
+ def id
23
+ self["id"]
24
+ end
25
+
26
+ ##
27
+ # The object type, `"model"`.
28
+ # @return [String, nil]
29
+ #
30
+ def object
31
+ self["object"]
32
+ end
33
+
34
+ ##
35
+ # When the model was created, as a Unix timestamp (seconds).
36
+ # @return [Integer, nil]
37
+ #
38
+ def created
39
+ self["created"]
40
+ end
41
+
42
+ ##
43
+ # The organization that owns the model, e.g. `"hermes"`.
44
+ # @return [String, nil]
45
+ #
46
+ def owned_by
47
+ self["owned_by"]
48
+ end
49
+
50
+ ##
51
+ # The root model id this model derives from.
52
+ # @return [String, nil]
53
+ #
54
+ def root
55
+ self["root"]
56
+ end
57
+
58
+ ##
59
+ # The parent model id, or `nil` when there is no parent.
60
+ # @return [String, nil]
61
+ #
62
+ def parent
63
+ self["parent"]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end