@byollm/server 0.1.0-alpha.2 → 0.1.0-alpha.21

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.
@@ -1,255 +0,0 @@
1
- import { JobKind, JobPayload, Audience, JobState, Lease, JobOutcome, ResultProvenance, Capability } from '@byollm/protocol';
2
-
3
- /**
4
- * A job as the server stores it.
5
- *
6
- * Adapters map this shape onto their own storage; the field meanings are
7
- * normative because the conformance kit asserts behaviour that depends on
8
- * them (TTL clock start, dependency gating, refusal tracking).
9
- */
10
- interface JobRecord {
11
- readonly id: string;
12
- readonly kind: JobKind;
13
- readonly payload: JobPayload;
14
- readonly audience: Audience;
15
- /** The app's id for the user who enqueued it. */
16
- readonly owner: string;
17
- /** Server-side restriction on which runner owners may take a `named` job. */
18
- readonly audienceAllow: readonly string[] | undefined;
19
- /** Job ids that must all be `ok` before this becomes claimable. */
20
- readonly dependsOn: readonly string[];
21
- readonly state: JobState;
22
- readonly lease: Lease | null;
23
- readonly createdAt: number;
24
- /**
25
- * When the job became claimable — enqueue time for a job with no
26
- * dependencies, or the moment its last dependency reached `ok`.
27
- *
28
- * **The TTL clock starts here, not at `createdAt`.** Starting it at enqueue
29
- * would expire a dependent job for the crime of waiting on a slow
30
- * dependency (byollm_001 Rev 1 §D, TTL clock resolved in build review).
31
- * `null` means still blocked.
32
- */
33
- readonly claimableAt: number | null;
34
- /** How long an unclaimed job may wait once claimable. */
35
- readonly ttlMs: number;
36
- /** Optional absolute deadline, independent of the TTL. */
37
- readonly deadlineAt: number | null;
38
- /**
39
- * Runners that released this job with reason `refused` — their local
40
- * allowlist declined it. Never offered to them again
41
- * ({@link MUSTS.REFUSAL_NOT_REOFFERED}).
42
- */
43
- readonly refusedBy: readonly string[];
44
- /** How many times this job has been claimed, including lease-expiry retries. */
45
- readonly attempts: number;
46
- readonly outcome: JobOutcome | null;
47
- readonly provenance: ResultProvenance | null;
48
- readonly updatedAt: number;
49
- }
50
- /** A paired daemon as the server stores it. */
51
- interface RunnerRecord {
52
- readonly id: string;
53
- /** The app's id for the user this runner is bound to — exactly one. */
54
- readonly owner: string;
55
- /** SHA-256 of the bearer token. The token itself is never stored. */
56
- readonly tokenHash: string;
57
- readonly label: string;
58
- readonly platform: "darwin" | "linux" | "win32";
59
- readonly daemonVersion: string;
60
- readonly capabilities: readonly Capability[];
61
- readonly paused: boolean;
62
- /** Set once; a revoked runner never un-revokes. */
63
- readonly revokedAt: number | null;
64
- readonly lastHeartbeatAt: number;
65
- readonly createdAt: number;
66
- }
67
- /** An in-flight device-code pairing. */
68
- interface PairingRecord {
69
- /** SHA-256 of the device code. The code itself is never stored. */
70
- readonly deviceCodeHash: string;
71
- /** The short code the user reads. Unique among live pairings. */
72
- readonly userCode: string;
73
- readonly state: "pending" | "approved" | "denied";
74
- /** Set when approved — learned from the approving user's own session. */
75
- readonly owner: string | null;
76
- readonly runnerId: string | null;
77
- /**
78
- * The bearer token, held until the daemon's next poll collects it, then
79
- * cleared. Delivered exactly once.
80
- */
81
- readonly runnerTokenOnce: string | null;
82
- readonly label: string;
83
- readonly platform: "darwin" | "linux" | "win32";
84
- readonly daemonVersion: string;
85
- readonly capabilities: readonly Capability[];
86
- readonly expiresAt: number;
87
- readonly createdAt: number;
88
- }
89
- /** What the app supplies to enqueue a job. */
90
- interface EnqueueInput {
91
- readonly kind: JobKind;
92
- readonly payload: JobPayload;
93
- readonly owner: string;
94
- /** Defaults to `self` — the safe direction. */
95
- readonly audience?: Audience;
96
- readonly audienceAllow?: readonly string[];
97
- readonly dependsOn?: readonly string[];
98
- /** Defaults to the server config's `defaultTtlMs`. */
99
- readonly ttlMs?: number;
100
- readonly deadlineAt?: number;
101
- /** Caller-supplied id, for idempotent enqueue. */
102
- readonly id?: string;
103
- }
104
-
105
- /**
106
- * The adapter seam.
107
- *
108
- * Everything in `@byollm/server` above this interface is storage-agnostic;
109
- * everything below it is one adapter. `MemoryJobStore` is the reference
110
- * implementation and the thing the conformance kit certifies first — an
111
- * adapter is correct when the same kit passes against it.
112
- */
113
- interface JobStore {
114
- /** Create a job. Idempotent when `input.id` is supplied and already exists. */
115
- create(input: EnqueueInput, now: number): Promise<JobRecord>;
116
- get(jobId: string): Promise<JobRecord | null>;
117
- /**
118
- * Atomically claim up to `max` jobs for a runner
119
- * ({@link MUSTS.CLAIM_ATOMIC}).
120
- *
121
- * An implementation MUST apply, inside the same atomic step:
122
- * - state is `queued`;
123
- * - `claimableAt` is non-null and `<= now` (dependencies satisfied,
124
- * {@link MUSTS.DEPENDS_ON_GATING});
125
- * - the job's kind appears in `capabilities`
126
- * ({@link MUSTS.CLAIM_REQUIRES_CAPABILITY});
127
- * - the audience rules admit this runner
128
- * ({@link MUSTS.AUDIENCE_BOTH_SIDES});
129
- * - `runnerId` is not in the job's `refusedBy`
130
- * ({@link MUSTS.REFUSAL_NOT_REOFFERED}).
131
- *
132
- * SQL-backed adapters SHOULD use `FOR UPDATE SKIP LOCKED`.
133
- */
134
- claim(args: ClaimArgs): Promise<JobRecord[]>;
135
- /**
136
- * Renew leases for the jobs a runner believes it holds, and report which it
137
- * has lost. A job whose lease expired un-renewed returns to `queued`
138
- * ({@link MUSTS.LEASE_RECLAIMABLE}).
139
- */
140
- renewLeases(args: RenewArgs): Promise<RenewResult>;
141
- /**
142
- * Record a terminal outcome. Idempotent by job id: the first terminal
143
- * outcome wins ({@link MUSTS.RESULT_IDEMPOTENT}).
144
- *
145
- * Recording `ok` MUST also unblock dependents whose remaining dependencies
146
- * are all `ok`, setting their `claimableAt` — which is when their TTL clock
147
- * starts ({@link MUSTS.TTL_EXPIRY}).
148
- */
149
- complete(args: CompleteArgs): Promise<CompleteResult>;
150
- /**
151
- * Return jobs to `queued`. When `reason` is `refused`, the runner MUST be
152
- * added to each job's `refusedBy`.
153
- */
154
- release(args: ReleaseArgs): Promise<string[]>;
155
- /**
156
- * Move every claimable-but-unclaimed job past its TTL, and every job past
157
- * its absolute deadline, to `expired`. Returns what changed.
158
- *
159
- * Called opportunistically by the handlers; an adapter MAY also run it on a
160
- * schedule. It MUST be idempotent — firing twice is always safe.
161
- */
162
- expireDue(now: number): Promise<JobRecord[]>;
163
- /** Cancel a job by app request. Returns the job, or null if unknown. */
164
- cancel(jobId: string, now: number): Promise<JobRecord | null>;
165
- /** Jobs a runner currently holds — used to build the heartbeat cancel list. */
166
- listClaimedBy(runnerId: string): Promise<JobRecord[]>;
167
- /** Jobs awaiting cancellation that a given runner holds. */
168
- listCancelRequests(runnerId: string): Promise<string[]>;
169
- }
170
- interface ClaimArgs {
171
- readonly runnerId: string;
172
- /** The runner's owner, for audience matching. */
173
- readonly runnerOwner: string;
174
- readonly capabilities: readonly Capability[];
175
- readonly max: number;
176
- readonly leaseMs: number;
177
- readonly now: number;
178
- }
179
- interface RenewArgs {
180
- readonly runnerId: string;
181
- readonly jobIds: readonly string[];
182
- readonly leaseMs: number;
183
- readonly now: number;
184
- }
185
- interface RenewResult {
186
- readonly renewed: readonly {
187
- jobId: string;
188
- expiresAt: number;
189
- }[];
190
- /** Jobs the runner claimed to hold but no longer does. */
191
- readonly lost: readonly string[];
192
- }
193
- interface CompleteArgs {
194
- readonly jobId: string;
195
- readonly runnerId: string;
196
- readonly outcome: JobOutcome;
197
- readonly provenance: ResultProvenance;
198
- readonly now: number;
199
- }
200
- interface CompleteResult {
201
- /** False when this submission lost an idempotency race or the lease was gone. */
202
- readonly accepted: boolean;
203
- readonly job: JobRecord | null;
204
- }
205
- interface ReleaseArgs {
206
- readonly runnerId: string;
207
- readonly jobIds: readonly string[];
208
- readonly reason: "shutdown" | "pause" | "revoked" | "backend-down" | "refused";
209
- readonly now: number;
210
- }
211
- /** Runner registry and pairing state. */
212
- interface RunnerStore {
213
- /** Begin a device-code pairing. */
214
- createPairing(record: PairingRecord): Promise<void>;
215
- getPairingByDeviceCodeHash(hash: string): Promise<PairingRecord | null>;
216
- getPairingByUserCode(userCode: string): Promise<PairingRecord | null>;
217
- /**
218
- * Approve a pairing on behalf of an authenticated user, creating the
219
- * runner. The `owner` MUST come from the approving user's own session — a
220
- * daemon can never assert who it is ({@link MUSTS.PAIR_ONE_USER}).
221
- */
222
- approvePairing(args: ApproveArgs): Promise<RunnerRecord>;
223
- denyPairing(userCode: string, now: number): Promise<void>;
224
- /** Clear the one-shot token after the daemon collects it. */
225
- consumePairingToken(deviceCodeHash: string): Promise<void>;
226
- getRunnerByTokenHash(hash: string): Promise<RunnerRecord | null>;
227
- getRunner(runnerId: string): Promise<RunnerRecord | null>;
228
- /** Record a heartbeat: capabilities, version, pause state, liveness. */
229
- touchRunner(args: TouchArgs): Promise<RunnerRecord | null>;
230
- /** Revoke a runner. Once revoked, never un-revoked. */
231
- revokeRunner(runnerId: string, now: number): Promise<void>;
232
- /** Live runners for an owner — used by the no-runner signal. */
233
- listRunners(owner?: string): Promise<RunnerRecord[]>;
234
- }
235
- interface ApproveArgs {
236
- readonly userCode: string;
237
- /** From the approving user's session, never from the daemon. */
238
- readonly owner: string;
239
- readonly runnerId: string;
240
- readonly runnerToken: string;
241
- readonly tokenHash: string;
242
- readonly now: number;
243
- }
244
- interface TouchArgs {
245
- readonly runnerId: string;
246
- readonly capabilities: readonly Capability[];
247
- readonly daemonVersion: string;
248
- readonly paused: boolean;
249
- readonly now: number;
250
- }
251
- /** A store providing both halves. Most adapters implement one object. */
252
- interface ByollmStore extends JobStore, RunnerStore {
253
- }
254
-
255
- export type { ApproveArgs as A, ByollmStore as B, ClaimArgs as C, EnqueueInput as E, JobRecord as J, PairingRecord as P, RunnerRecord as R, TouchArgs as T, RenewArgs as a, RenewResult as b, CompleteArgs as c, CompleteResult as d, ReleaseArgs as e, JobStore as f, RunnerStore as g };