@byollm/server 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/chunk-7RKXFPBZ.js +72 -0
- package/dist/chunk-7RKXFPBZ.js.map +1 -0
- package/dist/chunk-HL6EYHQ7.js +422 -0
- package/dist/chunk-HL6EYHQ7.js.map +1 -0
- package/dist/delivery-36nIe-b3.d.ts +73 -0
- package/dist/handlers-D7lWfwno.d.ts +52 -0
- package/dist/index.d.ts +223 -0
- package/dist/index.js +634 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +33 -0
- package/dist/next.js +17 -0
- package/dist/next.js.map +1 -0
- package/dist/store-D23N6iiP.d.ts +255 -0
- package/dist/supabase/index.d.ts +30 -0
- package/dist/supabase/index.js +414 -0
- package/dist/supabase/index.js.map +1 -0
- package/package.json +52 -0
- package/supabase/migrations/20260809000000_byollm_runner.sql +507 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoRunnerAvailableError,
|
|
3
|
+
ResultTimeoutError
|
|
4
|
+
} from "../chunk-7RKXFPBZ.js";
|
|
5
|
+
|
|
6
|
+
// src/supabase/realtime.ts
|
|
7
|
+
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
8
|
+
var NO_RUNNER_GRACE_MS = 1e4;
|
|
9
|
+
function supabaseRealtimeDelivery(client) {
|
|
10
|
+
return (deps) => new SupabaseRealtimeDelivery(client, deps);
|
|
11
|
+
}
|
|
12
|
+
var SupabaseRealtimeDelivery = class {
|
|
13
|
+
#client;
|
|
14
|
+
#deps;
|
|
15
|
+
constructor(client, deps) {
|
|
16
|
+
this.#client = client;
|
|
17
|
+
this.#deps = deps;
|
|
18
|
+
}
|
|
19
|
+
async waitFor(jobId, options = {}) {
|
|
20
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
21
|
+
const current = await this.#deps.read(jobId);
|
|
22
|
+
if (current && isTerminal(current.state)) return current;
|
|
23
|
+
const channel = this.#client.channel(`byollm_job_${jobId}`).on(
|
|
24
|
+
"postgres_changes",
|
|
25
|
+
{
|
|
26
|
+
event: "UPDATE",
|
|
27
|
+
schema: "public",
|
|
28
|
+
table: "byollm_jobs",
|
|
29
|
+
filter: `id=eq.${jobId}`
|
|
30
|
+
},
|
|
31
|
+
() => {
|
|
32
|
+
void this.#check(jobId);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
const settled = Promise.withResolvers();
|
|
36
|
+
this.#resolve = settled.resolve;
|
|
37
|
+
channel.subscribe();
|
|
38
|
+
void this.#check(jobId);
|
|
39
|
+
const timer = setTimeout(() => {
|
|
40
|
+
settled.reject(new ResultTimeoutError(jobId, timeoutMs));
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
const watcher = this.#watchAvailability(jobId, options, settled);
|
|
43
|
+
const abort = () => {
|
|
44
|
+
settled.reject(new Error("wait aborted"));
|
|
45
|
+
};
|
|
46
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
47
|
+
try {
|
|
48
|
+
return await settled.promise;
|
|
49
|
+
} finally {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
clearInterval(watcher);
|
|
52
|
+
options.signal?.removeEventListener("abort", abort);
|
|
53
|
+
await this.#client.removeChannel(channel);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
#resolve;
|
|
57
|
+
async #check(jobId) {
|
|
58
|
+
const current = await this.#deps.read(jobId);
|
|
59
|
+
if (current && isTerminal(current.state)) this.#resolve?.(current);
|
|
60
|
+
}
|
|
61
|
+
/** Poll runner liveness; there is no row event for "nothing is happening". */
|
|
62
|
+
#watchAvailability(jobId, options, settled) {
|
|
63
|
+
let noRunnerSince = null;
|
|
64
|
+
return setInterval(() => {
|
|
65
|
+
void (async () => {
|
|
66
|
+
const availability = await this.#deps.availability(jobId);
|
|
67
|
+
if (availability.available || availability.blocked) {
|
|
68
|
+
noRunnerSince = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
noRunnerSince ??= Date.now();
|
|
72
|
+
if (Date.now() - noRunnerSince < NO_RUNNER_GRACE_MS) return;
|
|
73
|
+
const reason = availability.reason ?? "no-runner-online";
|
|
74
|
+
const substitute = await options.onNoRunner?.(reason);
|
|
75
|
+
if (substitute) {
|
|
76
|
+
settled.resolve(substitute);
|
|
77
|
+
} else {
|
|
78
|
+
settled.reject(new NoRunnerAvailableError(jobId, reason));
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
}, 2e3);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
function isTerminal(state) {
|
|
85
|
+
return state === "ok" || state === "error" || state === "canceled" || state === "expired";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/supabase/index.ts
|
|
89
|
+
var ms = (iso2) => iso2 === null ? null : Date.parse(iso2);
|
|
90
|
+
var iso = (epochMs) => new Date(epochMs).toISOString();
|
|
91
|
+
function toJob(row) {
|
|
92
|
+
const leaseExpires = ms(row.lease_expires_at);
|
|
93
|
+
return {
|
|
94
|
+
id: row.id,
|
|
95
|
+
kind: row.kind,
|
|
96
|
+
payload: row.payload,
|
|
97
|
+
audience: row.audience,
|
|
98
|
+
owner: row.owner,
|
|
99
|
+
audienceAllow: row.audience_allow ?? void 0,
|
|
100
|
+
dependsOn: row.depends_on,
|
|
101
|
+
state: row.state,
|
|
102
|
+
lease: row.lease_runner !== null && leaseExpires !== null ? { runnerId: row.lease_runner, expiresAt: leaseExpires } : null,
|
|
103
|
+
createdAt: Date.parse(row.created_at),
|
|
104
|
+
claimableAt: ms(row.claimable_at),
|
|
105
|
+
ttlMs: row.ttl_ms,
|
|
106
|
+
deadlineAt: ms(row.deadline_at),
|
|
107
|
+
refusedBy: row.refused_by,
|
|
108
|
+
attempts: row.attempts,
|
|
109
|
+
outcome: row.outcome,
|
|
110
|
+
provenance: row.provenance,
|
|
111
|
+
updatedAt: Date.parse(row.updated_at)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function toRunner(row) {
|
|
115
|
+
return {
|
|
116
|
+
id: row.id,
|
|
117
|
+
owner: row.owner,
|
|
118
|
+
tokenHash: row.token_hash,
|
|
119
|
+
label: row.label,
|
|
120
|
+
platform: row.platform,
|
|
121
|
+
daemonVersion: row.daemon_version,
|
|
122
|
+
capabilities: row.capabilities,
|
|
123
|
+
paused: row.paused,
|
|
124
|
+
revokedAt: ms(row.revoked_at),
|
|
125
|
+
lastHeartbeatAt: Date.parse(row.last_heartbeat_at),
|
|
126
|
+
createdAt: Date.parse(row.created_at)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function toPairing(row) {
|
|
130
|
+
return {
|
|
131
|
+
deviceCodeHash: row.device_code_hash,
|
|
132
|
+
userCode: row.user_code,
|
|
133
|
+
state: row.state,
|
|
134
|
+
owner: row.owner,
|
|
135
|
+
runnerId: row.runner_id,
|
|
136
|
+
runnerTokenOnce: row.runner_token_once,
|
|
137
|
+
label: row.label,
|
|
138
|
+
platform: row.platform,
|
|
139
|
+
daemonVersion: row.daemon_version,
|
|
140
|
+
capabilities: row.capabilities,
|
|
141
|
+
expiresAt: Date.parse(row.expires_at),
|
|
142
|
+
createdAt: Date.parse(row.created_at)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function supabaseStore(options) {
|
|
146
|
+
const db = options.client;
|
|
147
|
+
const defaultTtlMs = options.defaultTtlMs ?? 15 * 6e4;
|
|
148
|
+
function unwrap(result) {
|
|
149
|
+
if (result.error) throw new Error(`supabase: ${result.error.message}`);
|
|
150
|
+
if (result.data === null || result.data === void 0) {
|
|
151
|
+
throw new Error("supabase: no data returned");
|
|
152
|
+
}
|
|
153
|
+
return result.data;
|
|
154
|
+
}
|
|
155
|
+
function unwrapMaybe(result) {
|
|
156
|
+
if (result.error) throw new Error(`supabase: ${result.error.message}`);
|
|
157
|
+
return result.data ?? null;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
// -- jobs ---------------------------------------------------------------
|
|
161
|
+
async create(input, now) {
|
|
162
|
+
const dependsOn = [...input.dependsOn ?? []];
|
|
163
|
+
let claimableAt = iso(now);
|
|
164
|
+
if (dependsOn.length > 0) {
|
|
165
|
+
const deps = unwrap(
|
|
166
|
+
await db.from("byollm_jobs").select("id,state").in("id", dependsOn)
|
|
167
|
+
);
|
|
168
|
+
const allDone = deps.length === dependsOn.length && deps.every((dep) => dep.state === "ok");
|
|
169
|
+
claimableAt = allDone ? iso(now) : null;
|
|
170
|
+
}
|
|
171
|
+
const row = {
|
|
172
|
+
...input.id === void 0 ? {} : { id: input.id },
|
|
173
|
+
kind: input.kind,
|
|
174
|
+
payload: input.payload,
|
|
175
|
+
audience: input.audience ?? "self",
|
|
176
|
+
owner: input.owner,
|
|
177
|
+
audience_allow: input.audienceAllow ? [...input.audienceAllow] : null,
|
|
178
|
+
depends_on: dependsOn,
|
|
179
|
+
claimable_at: claimableAt,
|
|
180
|
+
ttl_ms: input.ttlMs ?? defaultTtlMs,
|
|
181
|
+
deadline_at: input.deadlineAt === void 0 ? null : iso(input.deadlineAt)
|
|
182
|
+
};
|
|
183
|
+
const inserted = unwrapMaybe(
|
|
184
|
+
await db.from("byollm_jobs").upsert(row, { onConflict: "id", ignoreDuplicates: true }).select().maybeSingle()
|
|
185
|
+
);
|
|
186
|
+
if (inserted) return toJob(inserted);
|
|
187
|
+
const existing = unwrap(
|
|
188
|
+
await db.from("byollm_jobs").select().eq("id", input.id ?? "").single()
|
|
189
|
+
);
|
|
190
|
+
return toJob(existing);
|
|
191
|
+
},
|
|
192
|
+
async get(jobId) {
|
|
193
|
+
const row = unwrapMaybe(
|
|
194
|
+
await db.from("byollm_jobs").select().eq("id", jobId).maybeSingle()
|
|
195
|
+
);
|
|
196
|
+
return row === null ? null : toJob(row);
|
|
197
|
+
},
|
|
198
|
+
async claim(args) {
|
|
199
|
+
const rows = unwrap(
|
|
200
|
+
await db.rpc("byollm_claim_jobs", {
|
|
201
|
+
p_runner_id: args.runnerId,
|
|
202
|
+
p_capabilities: args.capabilities,
|
|
203
|
+
p_max: args.max,
|
|
204
|
+
p_lease_ms: args.leaseMs
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
return rows.map(toJob);
|
|
208
|
+
},
|
|
209
|
+
async renewLeases(args) {
|
|
210
|
+
await db.rpc("byollm_expire_due");
|
|
211
|
+
if (args.jobIds.length === 0) return { renewed: [], lost: [] };
|
|
212
|
+
const expiresAt = iso(args.now + args.leaseMs);
|
|
213
|
+
const renewedRows = unwrap(
|
|
214
|
+
await db.from("byollm_jobs").update({
|
|
215
|
+
state: "running",
|
|
216
|
+
lease_expires_at: expiresAt,
|
|
217
|
+
updated_at: iso(args.now)
|
|
218
|
+
}).eq("lease_runner", args.runnerId).in("id", [...args.jobIds]).in("state", ["claimed", "running"]).select("id")
|
|
219
|
+
);
|
|
220
|
+
const renewedIds = new Set(renewedRows.map((row) => row.id));
|
|
221
|
+
return {
|
|
222
|
+
renewed: renewedRows.map((row) => ({
|
|
223
|
+
jobId: row.id,
|
|
224
|
+
expiresAt: args.now + args.leaseMs
|
|
225
|
+
})),
|
|
226
|
+
// Anything the runner thinks it holds but did not renew is gone.
|
|
227
|
+
lost: args.jobIds.filter((id) => !renewedIds.has(id))
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
async complete(args) {
|
|
231
|
+
const state = args.outcome.outcome === "ok" ? "ok" : args.outcome.outcome === "canceled" ? "canceled" : "error";
|
|
232
|
+
const rows = unwrap(
|
|
233
|
+
await db.from("byollm_jobs").update({
|
|
234
|
+
state,
|
|
235
|
+
lease_runner: null,
|
|
236
|
+
lease_expires_at: null,
|
|
237
|
+
outcome: args.outcome,
|
|
238
|
+
provenance: args.provenance,
|
|
239
|
+
updated_at: iso(args.now)
|
|
240
|
+
}).eq("id", args.jobId).eq("lease_runner", args.runnerId).in("state", ["claimed", "running"]).select()
|
|
241
|
+
);
|
|
242
|
+
const written = rows[0];
|
|
243
|
+
if (written === void 0) {
|
|
244
|
+
const current = unwrapMaybe(
|
|
245
|
+
await db.from("byollm_jobs").select().eq("id", args.jobId).maybeSingle()
|
|
246
|
+
);
|
|
247
|
+
return {
|
|
248
|
+
accepted: false,
|
|
249
|
+
job: current === null ? null : toJob(current)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return { accepted: true, job: toJob(written) };
|
|
253
|
+
},
|
|
254
|
+
async release(args) {
|
|
255
|
+
if (args.jobIds.length === 0) return [];
|
|
256
|
+
const held = unwrap(
|
|
257
|
+
await db.from("byollm_jobs").select("id,refused_by").eq("lease_runner", args.runnerId).in("id", [...args.jobIds])
|
|
258
|
+
);
|
|
259
|
+
const released = [];
|
|
260
|
+
for (const row of held) {
|
|
261
|
+
const refusedBy = args.reason === "refused" ? [.../* @__PURE__ */ new Set([...row.refused_by, args.runnerId])] : row.refused_by;
|
|
262
|
+
const { error } = await db.from("byollm_jobs").update({
|
|
263
|
+
state: "queued",
|
|
264
|
+
lease_runner: null,
|
|
265
|
+
lease_expires_at: null,
|
|
266
|
+
// Newly available again, so the TTL clock restarts.
|
|
267
|
+
claimable_at: iso(args.now),
|
|
268
|
+
refused_by: refusedBy,
|
|
269
|
+
updated_at: iso(args.now)
|
|
270
|
+
}).eq("id", row.id).eq("lease_runner", args.runnerId);
|
|
271
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
272
|
+
released.push(row.id);
|
|
273
|
+
}
|
|
274
|
+
return released;
|
|
275
|
+
},
|
|
276
|
+
async expireDue(_now) {
|
|
277
|
+
const { error } = await db.rpc("byollm_expire_due");
|
|
278
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
279
|
+
return [];
|
|
280
|
+
},
|
|
281
|
+
async cancel(jobId, now) {
|
|
282
|
+
const current = unwrapMaybe(
|
|
283
|
+
await db.from("byollm_jobs").select().eq("id", jobId).maybeSingle()
|
|
284
|
+
);
|
|
285
|
+
if (current === null) return null;
|
|
286
|
+
if (current.state === "queued") {
|
|
287
|
+
const rows = unwrap(
|
|
288
|
+
await db.from("byollm_jobs").update({ state: "canceled", updated_at: iso(now) }).eq("id", jobId).eq("state", "queued").select()
|
|
289
|
+
);
|
|
290
|
+
const canceled = rows[0];
|
|
291
|
+
return toJob(canceled ?? current);
|
|
292
|
+
}
|
|
293
|
+
if (current.state === "claimed" || current.state === "running") {
|
|
294
|
+
const { error } = await db.from("byollm_job_cancels").upsert({ job_id: jobId, requested_at: iso(now) });
|
|
295
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
296
|
+
}
|
|
297
|
+
return toJob(current);
|
|
298
|
+
},
|
|
299
|
+
async listClaimedBy(runnerId) {
|
|
300
|
+
const rows = unwrap(
|
|
301
|
+
await db.from("byollm_jobs").select().eq("lease_runner", runnerId)
|
|
302
|
+
);
|
|
303
|
+
return rows.map(toJob);
|
|
304
|
+
},
|
|
305
|
+
async listCancelRequests(runnerId) {
|
|
306
|
+
const rows = unwrap(
|
|
307
|
+
await db.from("byollm_job_cancels").select("job_id, byollm_jobs!inner(lease_runner)").eq("byollm_jobs.lease_runner", runnerId)
|
|
308
|
+
);
|
|
309
|
+
return rows.map((row) => row.job_id);
|
|
310
|
+
},
|
|
311
|
+
// -- pairing and runners -------------------------------------------------
|
|
312
|
+
async createPairing(record) {
|
|
313
|
+
const { error } = await db.from("byollm_pairings").insert({
|
|
314
|
+
device_code_hash: record.deviceCodeHash,
|
|
315
|
+
user_code: record.userCode,
|
|
316
|
+
state: record.state,
|
|
317
|
+
label: record.label,
|
|
318
|
+
platform: record.platform,
|
|
319
|
+
daemon_version: record.daemonVersion,
|
|
320
|
+
capabilities: record.capabilities,
|
|
321
|
+
expires_at: iso(record.expiresAt)
|
|
322
|
+
});
|
|
323
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
324
|
+
},
|
|
325
|
+
async getPairingByDeviceCodeHash(hash) {
|
|
326
|
+
const row = unwrapMaybe(
|
|
327
|
+
await db.from("byollm_pairings").select().eq("device_code_hash", hash).maybeSingle()
|
|
328
|
+
);
|
|
329
|
+
return row === null ? null : toPairing(row);
|
|
330
|
+
},
|
|
331
|
+
async getPairingByUserCode(userCode) {
|
|
332
|
+
const row = unwrapMaybe(
|
|
333
|
+
await db.from("byollm_pairings").select().eq("user_code", userCode).maybeSingle()
|
|
334
|
+
);
|
|
335
|
+
return row === null ? null : toPairing(row);
|
|
336
|
+
},
|
|
337
|
+
async approvePairing(args) {
|
|
338
|
+
const pairing = unwrap(
|
|
339
|
+
await db.from("byollm_pairings").select().eq("user_code", args.userCode).single()
|
|
340
|
+
);
|
|
341
|
+
if (Date.parse(pairing.expires_at) <= args.now) {
|
|
342
|
+
throw new Error("pairing code has expired");
|
|
343
|
+
}
|
|
344
|
+
if (pairing.state !== "pending") {
|
|
345
|
+
throw new Error(`pairing is already ${pairing.state}`);
|
|
346
|
+
}
|
|
347
|
+
const runner = unwrap(
|
|
348
|
+
await db.from("byollm_runners").insert({
|
|
349
|
+
owner: args.owner,
|
|
350
|
+
token_hash: args.tokenHash,
|
|
351
|
+
label: pairing.label,
|
|
352
|
+
platform: pairing.platform,
|
|
353
|
+
daemon_version: pairing.daemon_version,
|
|
354
|
+
capabilities: pairing.capabilities
|
|
355
|
+
}).select().single()
|
|
356
|
+
);
|
|
357
|
+
const { error } = await db.from("byollm_pairings").update({
|
|
358
|
+
state: "approved",
|
|
359
|
+
owner: args.owner,
|
|
360
|
+
runner_id: runner.id,
|
|
361
|
+
runner_token_once: args.runnerToken
|
|
362
|
+
}).eq("device_code_hash", pairing.device_code_hash);
|
|
363
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
364
|
+
return toRunner(runner);
|
|
365
|
+
},
|
|
366
|
+
async denyPairing(userCode, _now) {
|
|
367
|
+
const { error } = await db.from("byollm_pairings").update({ state: "denied" }).eq("user_code", userCode);
|
|
368
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
369
|
+
},
|
|
370
|
+
async consumePairingToken(deviceCodeHash) {
|
|
371
|
+
const { error } = await db.from("byollm_pairings").update({ runner_token_once: null }).eq("device_code_hash", deviceCodeHash);
|
|
372
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
373
|
+
},
|
|
374
|
+
async getRunnerByTokenHash(hash) {
|
|
375
|
+
const row = unwrapMaybe(
|
|
376
|
+
await db.from("byollm_runners").select().eq("token_hash", hash).maybeSingle()
|
|
377
|
+
);
|
|
378
|
+
return row === null ? null : toRunner(row);
|
|
379
|
+
},
|
|
380
|
+
async getRunner(runnerId) {
|
|
381
|
+
const row = unwrapMaybe(
|
|
382
|
+
await db.from("byollm_runners").select().eq("id", runnerId).maybeSingle()
|
|
383
|
+
);
|
|
384
|
+
return row === null ? null : toRunner(row);
|
|
385
|
+
},
|
|
386
|
+
async touchRunner(args) {
|
|
387
|
+
const row = unwrapMaybe(
|
|
388
|
+
await db.from("byollm_runners").update({
|
|
389
|
+
capabilities: args.capabilities,
|
|
390
|
+
daemon_version: args.daemonVersion,
|
|
391
|
+
paused: args.paused,
|
|
392
|
+
last_heartbeat_at: iso(args.now)
|
|
393
|
+
}).eq("id", args.runnerId).select().maybeSingle()
|
|
394
|
+
);
|
|
395
|
+
return row === null ? null : toRunner(row);
|
|
396
|
+
},
|
|
397
|
+
async revokeRunner(runnerId, now) {
|
|
398
|
+
const { error } = await db.from("byollm_runners").update({ revoked_at: iso(now) }).eq("id", runnerId).is("revoked_at", null);
|
|
399
|
+
if (error) throw new Error(`supabase: ${error.message}`);
|
|
400
|
+
},
|
|
401
|
+
async listRunners(owner) {
|
|
402
|
+
const query = db.from("byollm_runners").select();
|
|
403
|
+
const rows = unwrap(
|
|
404
|
+
owner === void 0 ? await query : await query.eq("owner", owner)
|
|
405
|
+
);
|
|
406
|
+
return rows.map(toRunner);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
export {
|
|
411
|
+
supabaseRealtimeDelivery,
|
|
412
|
+
supabaseStore
|
|
413
|
+
};
|
|
414
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/supabase/realtime.ts","../../src/supabase/index.ts"],"sourcesContent":["import type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { DeliveredResult } from \"@byollm/protocol\";\nimport {\n NoRunnerAvailableError,\n ResultTimeoutError,\n type PollingDeliveryDeps,\n type ResultDelivery,\n type WaitOptions,\n} from \"../delivery.js\";\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60_000;\n/**\n * How long a sustained no-runner signal must persist before it is believed.\n * A daemon restarting must not fail every job in flight.\n */\nconst NO_RUNNER_GRACE_MS = 10_000;\n\n/**\n * Realtime delivery: the app learns a job finished when Postgres says so.\n *\n * byollm_003 Rev 1 requires the server→app path be an explicit channel rather\n * than an implied in-request `await`. Polling is the portable default;\n * this is the one worth having when the app is already on Supabase, because\n * a result arrives in milliseconds instead of on the next poll tick.\n *\n * The no-runner watch still polls, deliberately: runner liveness is a\n * *derived* signal (nobody with matching capability has heartbeated lately),\n * and there is no row change to subscribe to for \"something stopped\n * happening\".\n */\nexport function supabaseRealtimeDelivery(\n client: SupabaseClient,\n): (deps: PollingDeliveryDeps) => ResultDelivery {\n return (deps) => new SupabaseRealtimeDelivery(client, deps);\n}\n\nclass SupabaseRealtimeDelivery implements ResultDelivery {\n readonly #client: SupabaseClient;\n readonly #deps: PollingDeliveryDeps;\n\n constructor(client: SupabaseClient, deps: PollingDeliveryDeps) {\n this.#client = client;\n this.#deps = deps;\n }\n\n async waitFor(\n jobId: string,\n options: WaitOptions = {},\n ): Promise<DeliveredResult> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n // Read first. The job may already be terminal, and subscribing to a\n // channel for an event that has already happened waits forever.\n const current = await this.#deps.read(jobId);\n if (current && isTerminal(current.state)) return current;\n\n const channel = this.#client.channel(`byollm_job_${jobId}`).on(\n \"postgres_changes\",\n {\n event: \"UPDATE\",\n schema: \"public\",\n table: \"byollm_jobs\",\n filter: `id=eq.${jobId}`,\n },\n () => {\n void this.#check(jobId);\n },\n );\n\n const settled = Promise.withResolvers<DeliveredResult>();\n this.#resolve = settled.resolve;\n\n // `subscribe()` returns the channel, not a promise — awaiting it would be\n // a no-op that reads as if it waited for the subscription to be live.\n channel.subscribe();\n\n // A second read after subscribing closes the race where the job finished\n // between the first read and the subscription taking effect.\n void this.#check(jobId);\n\n const timer = setTimeout(() => {\n settled.reject(new ResultTimeoutError(jobId, timeoutMs));\n }, timeoutMs);\n\n const watcher = this.#watchAvailability(jobId, options, settled);\n const abort = (): void => {\n settled.reject(new Error(\"wait aborted\"));\n };\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n\n try {\n return await settled.promise;\n } finally {\n clearTimeout(timer);\n clearInterval(watcher);\n options.signal?.removeEventListener(\"abort\", abort);\n await this.#client.removeChannel(channel);\n }\n }\n\n #resolve: ((result: DeliveredResult) => void) | undefined;\n\n async #check(jobId: string): Promise<void> {\n const current = await this.#deps.read(jobId);\n if (current && isTerminal(current.state)) this.#resolve?.(current);\n }\n\n /** Poll runner liveness; there is no row event for \"nothing is happening\". */\n #watchAvailability(\n jobId: string,\n options: WaitOptions,\n settled: PromiseWithResolvers<DeliveredResult>,\n ): NodeJS.Timeout {\n let noRunnerSince: number | null = null;\n\n return setInterval(() => {\n void (async () => {\n const availability = await this.#deps.availability(jobId);\n if (availability.available || availability.blocked) {\n noRunnerSince = null;\n return;\n }\n noRunnerSince ??= Date.now();\n if (Date.now() - noRunnerSince < NO_RUNNER_GRACE_MS) return;\n\n const reason = availability.reason ?? \"no-runner-online\";\n const substitute = await options.onNoRunner?.(reason);\n if (substitute) {\n settled.resolve(substitute);\n } else {\n settled.reject(new NoRunnerAvailableError(jobId, reason));\n }\n })();\n }, 2_000);\n }\n}\n\nfunction isTerminal(state: string): boolean {\n return (\n state === \"ok\" ||\n state === \"error\" ||\n state === \"canceled\" ||\n state === \"expired\"\n );\n}\n","import type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { Capability, JobOutcome, JobState } from \"@byollm/protocol\";\nimport type {\n EnqueueInput,\n JobRecord,\n PairingRecord,\n RunnerRecord,\n} from \"../records.js\";\nimport type {\n ApproveArgs,\n ByollmStore,\n ClaimArgs,\n CompleteArgs,\n CompleteResult,\n ReleaseArgs,\n RenewArgs,\n RenewResult,\n TouchArgs,\n} from \"../store.js\";\n\n/**\n * `@byollm/server/supabase` — the first-party Supabase adapter.\n *\n * The piece the of-tomorrow-framework's runner module consumes verbatim.\n * Migrations ship in `supabase/migrations`; the atomic claim lives in a\n * `security definer` RPC using `FOR UPDATE SKIP LOCKED`, and the audience\n * rules are mirrored in SQL so the server refuses independently of the daemon\n * (byollm_003 §Server-side MUSTs).\n *\n * Requires the **service role** key: a runner authenticates with a bearer\n * token of its own, which is not a Supabase session, so the protocol handler\n * cannot run under RLS as the runner's user. RLS still governs everything the\n * *browser* does — the app-side policies in the migration are what protect\n * one user's jobs from another.\n *\n * @packageDocumentation\n */\n\n/** Row shape of `byollm_jobs`. */\ninterface JobRow {\n id: string;\n kind: string;\n payload: unknown;\n audience: \"self\" | \"named\" | \"public\";\n owner: string;\n audience_allow: string[] | null;\n depends_on: string[];\n state: JobState;\n lease_runner: string | null;\n lease_expires_at: string | null;\n claimable_at: string | null;\n ttl_ms: number;\n deadline_at: string | null;\n refused_by: string[];\n attempts: number;\n outcome: JobOutcome | null;\n provenance: JobRecord[\"provenance\"];\n created_at: string;\n updated_at: string;\n}\n\n/** Row shape of `byollm_runners`. */\ninterface RunnerRow {\n id: string;\n owner: string;\n token_hash: string;\n label: string;\n platform: \"darwin\" | \"linux\" | \"win32\";\n daemon_version: string;\n capabilities: Capability[];\n paused: boolean;\n revoked_at: string | null;\n last_heartbeat_at: string;\n created_at: string;\n}\n\n/** Row shape of `byollm_pairings`. */\ninterface PairingRow {\n device_code_hash: string;\n user_code: string;\n state: \"pending\" | \"approved\" | \"denied\";\n owner: string | null;\n runner_id: string | null;\n runner_token_once: string | null;\n label: string;\n platform: \"darwin\" | \"linux\" | \"win32\";\n daemon_version: string;\n capabilities: Capability[];\n expires_at: string;\n created_at: string;\n}\n\nconst ms = (iso: string | null): number | null =>\n iso === null ? null : Date.parse(iso);\n\nconst iso = (epochMs: number): string => new Date(epochMs).toISOString();\n\nfunction toJob(row: JobRow): JobRecord {\n const leaseExpires = ms(row.lease_expires_at);\n return {\n id: row.id,\n kind: row.kind as JobRecord[\"kind\"],\n payload: row.payload as JobRecord[\"payload\"],\n audience: row.audience,\n owner: row.owner,\n audienceAllow: row.audience_allow ?? undefined,\n dependsOn: row.depends_on,\n state: row.state,\n lease:\n row.lease_runner !== null && leaseExpires !== null\n ? { runnerId: row.lease_runner, expiresAt: leaseExpires }\n : null,\n createdAt: Date.parse(row.created_at),\n claimableAt: ms(row.claimable_at),\n ttlMs: row.ttl_ms,\n deadlineAt: ms(row.deadline_at),\n refusedBy: row.refused_by,\n attempts: row.attempts,\n outcome: row.outcome,\n provenance: row.provenance,\n updatedAt: Date.parse(row.updated_at),\n };\n}\n\nfunction toRunner(row: RunnerRow): RunnerRecord {\n return {\n id: row.id,\n owner: row.owner,\n tokenHash: row.token_hash,\n label: row.label,\n platform: row.platform,\n daemonVersion: row.daemon_version,\n capabilities: row.capabilities,\n paused: row.paused,\n revokedAt: ms(row.revoked_at),\n lastHeartbeatAt: Date.parse(row.last_heartbeat_at),\n createdAt: Date.parse(row.created_at),\n };\n}\n\nfunction toPairing(row: PairingRow): PairingRecord {\n return {\n deviceCodeHash: row.device_code_hash,\n userCode: row.user_code,\n state: row.state,\n owner: row.owner,\n runnerId: row.runner_id,\n runnerTokenOnce: row.runner_token_once,\n label: row.label,\n platform: row.platform,\n daemonVersion: row.daemon_version,\n capabilities: row.capabilities,\n expiresAt: Date.parse(row.expires_at),\n createdAt: Date.parse(row.created_at),\n };\n}\n\nexport interface SupabaseStoreOptions {\n /** A client built with the **service role** key. */\n readonly client: SupabaseClient;\n /** Default TTL for a job once claimable. */\n readonly defaultTtlMs?: number;\n}\n\n/** Build the Supabase-backed store. */\nexport function supabaseStore(options: SupabaseStoreOptions): ByollmStore {\n const db = options.client;\n const defaultTtlMs = options.defaultTtlMs ?? 15 * 60_000;\n\n /**\n * Narrow one PostgREST response, or throw with the Postgres message.\n *\n * `supabase-js` types rows as `any` unless the project has generated\n * database types, so the assertion has to live somewhere. Confining it to\n * these two helpers — against the row interfaces declared above — keeps\n * every call site typed and leaves exactly one place to review.\n */\n /* eslint-disable @typescript-eslint/no-unnecessary-type-parameters --\n T appears only in the return type because these helpers *are* the cast.\n That is the point: one reviewable place where PostgREST's `any` becomes\n one of the row interfaces above. */\n function unwrap<T>(result: {\n data: unknown;\n error: { message: string } | null;\n }): T {\n if (result.error) throw new Error(`supabase: ${result.error.message}`);\n if (result.data === null || result.data === undefined) {\n throw new Error(\"supabase: no data returned\");\n }\n return result.data as T;\n }\n\n /** Same, but a missing row is a legitimate answer rather than an error. */\n function unwrapMaybe<T>(result: {\n data: unknown;\n error: { message: string } | null;\n }): T | null {\n if (result.error) throw new Error(`supabase: ${result.error.message}`);\n return (result.data ?? null) as T | null;\n }\n /* eslint-enable @typescript-eslint/no-unnecessary-type-parameters */\n\n return {\n // -- jobs ---------------------------------------------------------------\n\n async create(input: EnqueueInput, now: number): Promise<JobRecord> {\n const dependsOn = [...(input.dependsOn ?? [])];\n\n // A job with dependencies starts blocked; the trigger sets\n // `claimable_at` when the last one reaches `ok`, which is where its TTL\n // clock starts.\n let claimableAt: string | null = iso(now);\n if (dependsOn.length > 0) {\n const deps = unwrap<JobRow[]>(\n await db.from(\"byollm_jobs\").select(\"id,state\").in(\"id\", dependsOn),\n ) as { id: string; state: JobState }[];\n const allDone =\n deps.length === dependsOn.length &&\n deps.every((dep) => dep.state === \"ok\");\n claimableAt = allDone ? iso(now) : null;\n }\n\n const row = {\n ...(input.id === undefined ? {} : { id: input.id }),\n kind: input.kind,\n payload: input.payload,\n audience: input.audience ?? \"self\",\n owner: input.owner,\n audience_allow: input.audienceAllow ? [...input.audienceAllow] : null,\n depends_on: dependsOn,\n claimable_at: claimableAt,\n ttl_ms: input.ttlMs ?? defaultTtlMs,\n deadline_at:\n input.deadlineAt === undefined ? null : iso(input.deadlineAt),\n };\n\n // Idempotent by caller-supplied id, matching the reference store: an\n // app's retry must not duplicate work.\n const inserted = unwrapMaybe<JobRow>(\n await db\n .from(\"byollm_jobs\")\n .upsert(row, { onConflict: \"id\", ignoreDuplicates: true })\n .select()\n .maybeSingle(),\n );\n\n if (inserted) return toJob(inserted);\n const existing = unwrap<JobRow>(\n await db\n .from(\"byollm_jobs\")\n .select()\n .eq(\"id\", input.id ?? \"\")\n .single(),\n );\n return toJob(existing);\n },\n\n async get(jobId: string): Promise<JobRecord | null> {\n const row = unwrapMaybe<JobRow>(\n await db.from(\"byollm_jobs\").select().eq(\"id\", jobId).maybeSingle(),\n );\n return row === null ? null : toJob(row);\n },\n\n async claim(args: ClaimArgs): Promise<JobRecord[]> {\n // One RPC, one transaction, `FOR UPDATE SKIP LOCKED` inside\n // ({@link MUSTS.CLAIM_ATOMIC}).\n const rows = unwrap<JobRow[]>(\n await db.rpc(\"byollm_claim_jobs\", {\n p_runner_id: args.runnerId,\n p_capabilities: args.capabilities,\n p_max: args.max,\n p_lease_ms: args.leaseMs,\n }),\n );\n return rows.map(toJob);\n },\n\n async renewLeases(args: RenewArgs): Promise<RenewResult> {\n await db.rpc(\"byollm_expire_due\");\n if (args.jobIds.length === 0) return { renewed: [], lost: [] };\n\n const expiresAt = iso(args.now + args.leaseMs);\n const renewedRows = unwrap<JobRow[]>(\n await db\n .from(\"byollm_jobs\")\n .update({\n state: \"running\",\n lease_expires_at: expiresAt,\n updated_at: iso(args.now),\n })\n .eq(\"lease_runner\", args.runnerId)\n .in(\"id\", [...args.jobIds])\n .in(\"state\", [\"claimed\", \"running\"])\n .select(\"id\"),\n );\n\n const renewedIds = new Set(renewedRows.map((row) => row.id));\n return {\n renewed: renewedRows.map((row) => ({\n jobId: row.id,\n expiresAt: args.now + args.leaseMs,\n })),\n // Anything the runner thinks it holds but did not renew is gone.\n lost: args.jobIds.filter((id) => !renewedIds.has(id)),\n };\n },\n\n async complete(args: CompleteArgs): Promise<CompleteResult> {\n const state: JobState =\n args.outcome.outcome === \"ok\"\n ? \"ok\"\n : args.outcome.outcome === \"canceled\"\n ? \"canceled\"\n : \"error\";\n\n // The `in('state', ...)` predicate is the idempotency guard: a job that\n // already reached a terminal state matches nothing, so the first\n // outcome wins ({@link MUSTS.RESULT_IDEMPOTENT}). The `lease_runner`\n // predicate is {@link MUSTS.LEASE_HONORED}.\n const rows = unwrap<JobRow[]>(\n await db\n .from(\"byollm_jobs\")\n .update({\n state,\n lease_runner: null,\n lease_expires_at: null,\n outcome: args.outcome,\n provenance: args.provenance,\n updated_at: iso(args.now),\n })\n .eq(\"id\", args.jobId)\n .eq(\"lease_runner\", args.runnerId)\n .in(\"state\", [\"claimed\", \"running\"])\n .select(),\n );\n\n const written = rows[0];\n if (written === undefined) {\n const current = unwrapMaybe<JobRow>(\n await db\n .from(\"byollm_jobs\")\n .select()\n .eq(\"id\", args.jobId)\n .maybeSingle(),\n );\n return {\n accepted: false,\n job: current === null ? null : toJob(current),\n };\n }\n return { accepted: true, job: toJob(written) };\n },\n\n async release(args: ReleaseArgs): Promise<string[]> {\n if (args.jobIds.length === 0) return [];\n\n const held = unwrap<{ id: string; refused_by: string[] }[]>(\n await db\n .from(\"byollm_jobs\")\n .select(\"id,refused_by\")\n .eq(\"lease_runner\", args.runnerId)\n .in(\"id\", [...args.jobIds]),\n );\n\n const released: string[] = [];\n for (const row of held) {\n const refusedBy =\n args.reason === \"refused\"\n ? [...new Set([...row.refused_by, args.runnerId])]\n : row.refused_by;\n\n const { error } = await db\n .from(\"byollm_jobs\")\n .update({\n state: \"queued\",\n lease_runner: null,\n lease_expires_at: null,\n // Newly available again, so the TTL clock restarts.\n claimable_at: iso(args.now),\n refused_by: refusedBy,\n updated_at: iso(args.now),\n })\n .eq(\"id\", row.id)\n .eq(\"lease_runner\", args.runnerId);\n if (error) throw new Error(`supabase: ${error.message}`);\n released.push(row.id);\n }\n return released;\n },\n\n async expireDue(_now: number): Promise<JobRecord[]> {\n // The sweep is a single idempotent SQL function; it reports a count\n // rather than rows, and the caller only needs to know it ran.\n const { error } = await db.rpc(\"byollm_expire_due\");\n if (error) throw new Error(`supabase: ${error.message}`);\n return [];\n },\n\n async cancel(jobId: string, now: number): Promise<JobRecord | null> {\n const current = unwrapMaybe<JobRow>(\n await db.from(\"byollm_jobs\").select().eq(\"id\", jobId).maybeSingle(),\n );\n if (current === null) return null;\n\n if (current.state === \"queued\") {\n const rows = unwrap<JobRow[]>(\n await db\n .from(\"byollm_jobs\")\n .update({ state: \"canceled\", updated_at: iso(now) })\n .eq(\"id\", jobId)\n .eq(\"state\", \"queued\")\n .select(),\n );\n const canceled = rows[0];\n return toJob(canceled ?? current);\n }\n\n if (current.state === \"claimed\" || current.state === \"running\") {\n // Held by a runner: the cancel travels on the next heartbeat and the\n // runner reports `canceled` itself.\n const { error } = await db\n .from(\"byollm_job_cancels\")\n .upsert({ job_id: jobId, requested_at: iso(now) });\n if (error) throw new Error(`supabase: ${error.message}`);\n }\n return toJob(current);\n },\n\n async listClaimedBy(runnerId: string): Promise<JobRecord[]> {\n const rows = unwrap<JobRow[]>(\n await db.from(\"byollm_jobs\").select().eq(\"lease_runner\", runnerId),\n );\n return rows.map(toJob);\n },\n\n async listCancelRequests(runnerId: string): Promise<string[]> {\n const rows = unwrap<{ job_id: string }[]>(\n await db\n .from(\"byollm_job_cancels\")\n .select(\"job_id, byollm_jobs!inner(lease_runner)\")\n .eq(\"byollm_jobs.lease_runner\", runnerId),\n ) as { job_id: string }[];\n return rows.map((row) => row.job_id);\n },\n\n // -- pairing and runners -------------------------------------------------\n\n async createPairing(record: PairingRecord): Promise<void> {\n const { error } = await db.from(\"byollm_pairings\").insert({\n device_code_hash: record.deviceCodeHash,\n user_code: record.userCode,\n state: record.state,\n label: record.label,\n platform: record.platform,\n daemon_version: record.daemonVersion,\n capabilities: record.capabilities,\n expires_at: iso(record.expiresAt),\n });\n if (error) throw new Error(`supabase: ${error.message}`);\n },\n\n async getPairingByDeviceCodeHash(\n hash: string,\n ): Promise<PairingRecord | null> {\n const row = unwrapMaybe<PairingRow>(\n await db\n .from(\"byollm_pairings\")\n .select()\n .eq(\"device_code_hash\", hash)\n .maybeSingle(),\n );\n return row === null ? null : toPairing(row);\n },\n\n async getPairingByUserCode(\n userCode: string,\n ): Promise<PairingRecord | null> {\n const row = unwrapMaybe<PairingRow>(\n await db\n .from(\"byollm_pairings\")\n .select()\n .eq(\"user_code\", userCode)\n .maybeSingle(),\n );\n return row === null ? null : toPairing(row);\n },\n\n async approvePairing(args: ApproveArgs): Promise<RunnerRecord> {\n // Deliberately *not* the browser RPC: this path runs under the service\n // role with an `owner` the caller has already authenticated. Apps using\n // Supabase Auth in the browser should call `byollm_approve_pairing`\n // instead, which takes the owner from `auth.uid()` and cannot be told\n // who the user is.\n const pairing = unwrap<PairingRow>(\n await db\n .from(\"byollm_pairings\")\n .select()\n .eq(\"user_code\", args.userCode)\n .single(),\n );\n\n if (Date.parse(pairing.expires_at) <= args.now) {\n throw new Error(\"pairing code has expired\");\n }\n if (pairing.state !== \"pending\") {\n throw new Error(`pairing is already ${pairing.state}`);\n }\n\n const runner = unwrap<RunnerRow>(\n await db\n .from(\"byollm_runners\")\n .insert({\n owner: args.owner,\n token_hash: args.tokenHash,\n label: pairing.label,\n platform: pairing.platform,\n daemon_version: pairing.daemon_version,\n capabilities: pairing.capabilities,\n })\n .select()\n .single(),\n );\n\n const { error } = await db\n .from(\"byollm_pairings\")\n .update({\n state: \"approved\",\n owner: args.owner,\n runner_id: runner.id,\n runner_token_once: args.runnerToken,\n })\n .eq(\"device_code_hash\", pairing.device_code_hash);\n if (error) throw new Error(`supabase: ${error.message}`);\n\n return toRunner(runner);\n },\n\n async denyPairing(userCode: string, _now: number): Promise<void> {\n const { error } = await db\n .from(\"byollm_pairings\")\n .update({ state: \"denied\" })\n .eq(\"user_code\", userCode);\n if (error) throw new Error(`supabase: ${error.message}`);\n },\n\n async consumePairingToken(deviceCodeHash: string): Promise<void> {\n const { error } = await db\n .from(\"byollm_pairings\")\n .update({ runner_token_once: null })\n .eq(\"device_code_hash\", deviceCodeHash);\n if (error) throw new Error(`supabase: ${error.message}`);\n },\n\n async getRunnerByTokenHash(hash: string): Promise<RunnerRecord | null> {\n const row = unwrapMaybe<RunnerRow>(\n await db\n .from(\"byollm_runners\")\n .select()\n .eq(\"token_hash\", hash)\n .maybeSingle(),\n );\n return row === null ? null : toRunner(row);\n },\n\n async getRunner(runnerId: string): Promise<RunnerRecord | null> {\n const row = unwrapMaybe<RunnerRow>(\n await db\n .from(\"byollm_runners\")\n .select()\n .eq(\"id\", runnerId)\n .maybeSingle(),\n );\n return row === null ? null : toRunner(row);\n },\n\n async touchRunner(args: TouchArgs): Promise<RunnerRecord | null> {\n const row = unwrapMaybe<RunnerRow>(\n await db\n .from(\"byollm_runners\")\n .update({\n capabilities: args.capabilities,\n daemon_version: args.daemonVersion,\n paused: args.paused,\n last_heartbeat_at: iso(args.now),\n })\n .eq(\"id\", args.runnerId)\n .select()\n .maybeSingle(),\n );\n return row === null ? null : toRunner(row);\n },\n\n async revokeRunner(runnerId: string, now: number): Promise<void> {\n // `is('revoked_at', null)` keeps revocation one-way: an already-revoked\n // runner keeps its first revocation time.\n const { error } = await db\n .from(\"byollm_runners\")\n .update({ revoked_at: iso(now) })\n .eq(\"id\", runnerId)\n .is(\"revoked_at\", null);\n if (error) throw new Error(`supabase: ${error.message}`);\n },\n\n async listRunners(owner?: string): Promise<RunnerRecord[]> {\n const query = db.from(\"byollm_runners\").select();\n const rows = unwrap<RunnerRow[]>(\n owner === undefined ? await query : await query.eq(\"owner\", owner),\n );\n return rows.map(toRunner);\n },\n };\n}\n\nexport { supabaseRealtimeDelivery } from \"./realtime.js\";\n"],"mappings":"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","names":["iso"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@byollm/server",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Framework-agnostic BYOLLM protocol handlers, a reference in-memory store, a Next.js mount, and a Supabase adapter.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./next": {
|
|
13
|
+
"types": "./dist/next.d.ts",
|
|
14
|
+
"import": "./dist/next.js"
|
|
15
|
+
},
|
|
16
|
+
"./supabase": {
|
|
17
|
+
"types": "./dist/supabase/index.d.ts",
|
|
18
|
+
"import": "./dist/supabase/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"supabase/migrations",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22.12"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@byollm/protocol": "0.1.0-alpha.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@supabase/supabase-js": "^2.58.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"@supabase/supabase-js": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@supabase/supabase-js": "^2.112.2"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"clean": "rm -rf dist .tsbuild"
|
|
51
|
+
}
|
|
52
|
+
}
|