@byollm/server 0.1.0-alpha.4 → 0.1.0-alpha.41
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/README.md +124 -4
- package/dist/{chunk-MGJX6626.js → chunk-K5E6JS5A.js} +128 -72
- package/dist/chunk-K5E6JS5A.js.map +1 -0
- package/dist/{chunk-7RKXFPBZ.js → chunk-SAK63KNU.js} +17 -3
- package/dist/chunk-SAK63KNU.js.map +1 -0
- package/dist/{delivery-36nIe-b3.d.ts → delivery-C6VzgMgH.d.ts} +15 -3
- package/dist/{handlers-CF3yE-t2.d.ts → handlers-BJYm2kdq.d.ts} +1 -1
- package/dist/index.d.ts +149 -13
- package/dist/index.js +405 -33
- package/dist/index.js.map +1 -1
- package/dist/next.d.ts +2 -2
- package/dist/next.js +1 -1
- package/dist/{store-gFEEN1Dt.d.ts → store-Dno2fnHH.d.ts} +128 -13
- package/dist/supabase/index.d.ts +2 -2
- package/dist/supabase/index.js +65 -35
- package/dist/supabase/index.js.map +1 -1
- package/package.json +2 -2
- package/supabase/migrations/20260819000000_drop_runner_token.sql +87 -0
- package/supabase/migrations/20260819010000_completed_by_lease_id.sql +25 -0
- package/supabase/migrations/20260821000000_rename_collected.sql +91 -0
- package/dist/chunk-7RKXFPBZ.js.map +0 -1
- package/dist/chunk-MGJX6626.js.map +0 -1
|
@@ -49,7 +49,9 @@ var PollingDelivery = class {
|
|
|
49
49
|
if (now() - noRunnerSince >= graceMs) {
|
|
50
50
|
const reason = availability.reason ?? "no-runner-online";
|
|
51
51
|
const substitute = await options.onNoRunner?.(reason);
|
|
52
|
-
if (substitute)
|
|
52
|
+
if (substitute !== void 0) {
|
|
53
|
+
return labelFallback(jobId, substitute);
|
|
54
|
+
}
|
|
53
55
|
throw new NoRunnerAvailableError(jobId, reason);
|
|
54
56
|
}
|
|
55
57
|
}
|
|
@@ -63,10 +65,22 @@ var PollingDelivery = class {
|
|
|
63
65
|
function isTerminalState(state) {
|
|
64
66
|
return state === "ok" || state === "error" || state === "canceled" || state === "expired";
|
|
65
67
|
}
|
|
68
|
+
function labelFallback(jobId, substitute) {
|
|
69
|
+
if (typeof substitute === "string") {
|
|
70
|
+
return {
|
|
71
|
+
jobId,
|
|
72
|
+
state: "ok",
|
|
73
|
+
outcome: { outcome: "ok", text: substitute },
|
|
74
|
+
fallback: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { ...substitute, fallback: true };
|
|
78
|
+
}
|
|
66
79
|
|
|
67
80
|
export {
|
|
68
81
|
NoRunnerAvailableError,
|
|
69
82
|
ResultTimeoutError,
|
|
70
|
-
PollingDelivery
|
|
83
|
+
PollingDelivery,
|
|
84
|
+
labelFallback
|
|
71
85
|
};
|
|
72
|
-
//# sourceMappingURL=chunk-
|
|
86
|
+
//# sourceMappingURL=chunk-SAK63KNU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/delivery.ts"],"sourcesContent":["import type { DeliveredResult } from \"@byollm/protocol\";\n\n/** Why a wait ended without a result. */\nexport class NoRunnerAvailableError extends Error {\n override readonly name = \"NoRunnerAvailableError\";\n constructor(\n readonly jobId: string,\n readonly reason: string,\n ) {\n super(\n `no runner is available to execute job ${jobId} (${reason}). ` +\n `Fall back to a hosted model, or prompt the user to start their runner.`,\n );\n }\n}\n\n/** The wait exceeded its timeout while a runner was still plausibly working. */\nexport class ResultTimeoutError extends Error {\n override readonly name = \"ResultTimeoutError\";\n constructor(\n readonly jobId: string,\n readonly timeoutMs: number,\n ) {\n super(`job ${jobId} did not finish within ${String(timeoutMs)}ms`);\n }\n}\n\nexport interface WaitOptions {\n /** Give up after this long. Default 5 minutes. */\n readonly timeoutMs?: number;\n /**\n * Called instead of throwing when no runner can take the job. Return a\n * substitute and the wait resolves with it; return nothing and\n * {@link NoRunnerAvailableError} is thrown.\n *\n * **A string is enough.** It is the app's own fallback answer — a hosted\n * model's text, a cached reply — not wire data, and requiring a whole\n * `DeliveredResult` for it was ceremony that invited invented shapes. The\n * README's own example got it wrong, which is how this was found.\n *\n * **Whatever comes back is labelled `fallback: true` by the wait, not by\n * the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come\n * from the user's own compute must not be reportable as though it did, and\n * that stays true whether an app returns a bare string or a full record it\n * assembled itself. The stamp is applied after this function returns, so\n * there is no shape an app can hand back that hides what it is.\n */\n readonly onNoRunner?: (\n reason: string,\n ) =>\n | string\n | DeliveredResult\n | undefined\n | Promise<string | DeliveredResult | undefined>;\n /** Abort the wait. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * How an app learns a job finished.\n *\n * byollm_003 Rev 1 is explicit that this is a *channel* — webhook, Realtime\n * subscription, or poll — and never an implied in-request `await`. The\n * polling implementation below is the portable default; the Supabase adapter\n * substitutes Realtime for the same interface.\n */\nexport interface ResultDelivery {\n waitFor(jobId: string, options?: WaitOptions): Promise<DeliveredResult>;\n}\n\nexport interface PollingDeliveryDeps {\n /** Current state of the job, or null if unknown. */\n readonly read: (jobId: string) => Promise<DeliveredResult | null>;\n /** Whether a runner could still take this job. */\n readonly availability: (\n jobId: string,\n ) => Promise<{ available: boolean; reason?: string; blocked: boolean }>;\n readonly sleep?: (ms: number) => Promise<void>;\n /**\n * Injectable clock. It must advance in step with {@link sleep}: a test that\n * stubs one and not the other gets a loop whose grace window never elapses.\n */\n readonly now?: () => number;\n /**\n * How long a sustained no-runner signal must persist before it is believed.\n * Defaults to {@link NO_RUNNER_GRACE_MS}.\n */\n readonly graceMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60_000;\nconst POLL_INTERVAL_MS = 500;\n/**\n * How long to let a job sit with no available runner before giving up.\n *\n * Not zero: a daemon restarting, or one whose heartbeat is momentarily late,\n * would otherwise fail every job in flight. The signal has to be sustained\n * before it is believed.\n */\nconst NO_RUNNER_GRACE_MS = 10_000;\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * The portable delivery channel: poll the store until the job is terminal.\n *\n * Correct everywhere and adequate for most apps. An adapter with a push\n * channel should replace it — see the Supabase adapter's Realtime delivery.\n */\nexport class PollingDelivery implements ResultDelivery {\n readonly #deps: PollingDeliveryDeps;\n\n constructor(deps: PollingDeliveryDeps) {\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 const sleep = this.#deps.sleep ?? defaultSleep;\n const now = this.#deps.now ?? Date.now;\n const graceMs = this.#deps.graceMs ?? NO_RUNNER_GRACE_MS;\n const started = now();\n let noRunnerSince: number | null = null;\n\n for (;;) {\n options.signal?.throwIfAborted();\n\n const current = await this.#deps.read(jobId);\n if (current && isTerminalState(current.state)) return current;\n\n const availability = await this.#deps.availability(jobId);\n if (availability.available || availability.blocked) {\n // `blocked` means the job is waiting on a dependency, which is not the\n // same event as \"nobody can run this\" ({@link MUSTS.NO_RUNNER_SIGNAL}).\n noRunnerSince = null;\n } else {\n noRunnerSince ??= now();\n if (now() - noRunnerSince >= graceMs) {\n const reason = availability.reason ?? \"no-runner-online\";\n const substitute = await options.onNoRunner?.(reason);\n if (substitute !== undefined) {\n return labelFallback(jobId, substitute);\n }\n throw new NoRunnerAvailableError(jobId, reason);\n }\n }\n\n if (now() - started >= timeoutMs) {\n throw new ResultTimeoutError(jobId, timeoutMs);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n }\n}\n\nfunction isTerminalState(state: string): boolean {\n return (\n state === \"ok\" ||\n state === \"error\" ||\n state === \"canceled\" ||\n state === \"expired\"\n );\n}\n\n/**\n * Turn an app's fallback into a delivered result, marked as one.\n *\n * Exported because there are two delivery channels — polling here, Supabase\n * Realtime next door — and a label applied by one of them is a label an app\n * gets or does not get depending on which store it chose. That is exactly the\n * kind of divergence a \"delivery adapter must not change what a result means\"\n * rule exists to prevent.\n *\n * Two jobs, and the second is the one that matters. A string becomes the\n * obvious record — that is the sugar. Everything, string or record, gets\n * `fallback: true` — that is {@link MUSTS.FALLBACK_LABELED}, and it is\n * applied here rather than trusted from the caller because an app that\n * assembled its own record could otherwise return something indistinguishable\n * from a runner's answer. Spreading the caller's object first and the flag\n * second is deliberate: a supplied `fallback` cannot overwrite it.\n */\nexport function labelFallback(\n jobId: string,\n substitute: string | DeliveredResult,\n): DeliveredResult {\n if (typeof substitute === \"string\") {\n return {\n jobId,\n state: \"ok\",\n outcome: { outcome: \"ok\", text: substitute },\n fallback: true,\n };\n }\n return { ...substitute, fallback: true };\n}\n"],"mappings":";AAGO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAEhD,YACW,OACA,QACT;AACA;AAAA,MACE,yCAAyC,KAAK,KAAK,MAAM;AAAA,IAE3D;AANS;AACA;AAAA,EAMX;AAAA,EAPW;AAAA,EACA;AAAA,EAHO,OAAO;AAU3B;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAE5C,YACW,OACA,WACT;AACA,UAAM,OAAO,KAAK,0BAA0B,OAAO,SAAS,CAAC,IAAI;AAHxD;AACA;AAAA,EAGX;AAAA,EAJW;AAAA,EACA;AAAA,EAHO,OAAO;AAO3B;AAiEA,IAAM,qBAAqB,IAAI;AAC/B,IAAM,mBAAmB;AAQzB,IAAM,qBAAqB;AAE3B,IAAM,eAAe,CAAC,OACpB,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAQ3C,IAAM,kBAAN,MAAgD;AAAA,EAC5C;AAAA,EAET,YAAY,MAA2B;AACrC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QACJ,OACA,UAAuB,CAAC,GACE;AAC1B,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,UAAM,MAAM,KAAK,MAAM,OAAO,KAAK;AACnC,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,UAAM,UAAU,IAAI;AACpB,QAAI,gBAA+B;AAEnC,eAAS;AACP,cAAQ,QAAQ,eAAe;AAE/B,YAAM,UAAU,MAAM,KAAK,MAAM,KAAK,KAAK;AAC3C,UAAI,WAAW,gBAAgB,QAAQ,KAAK,EAAG,QAAO;AAEtD,YAAM,eAAe,MAAM,KAAK,MAAM,aAAa,KAAK;AACxD,UAAI,aAAa,aAAa,aAAa,SAAS;AAGlD,wBAAgB;AAAA,MAClB,OAAO;AACL,0BAAkB,IAAI;AACtB,YAAI,IAAI,IAAI,iBAAiB,SAAS;AACpC,gBAAM,SAAS,aAAa,UAAU;AACtC,gBAAM,aAAa,MAAM,QAAQ,aAAa,MAAM;AACpD,cAAI,eAAe,QAAW;AAC5B,mBAAO,cAAc,OAAO,UAAU;AAAA,UACxC;AACA,gBAAM,IAAI,uBAAuB,OAAO,MAAM;AAAA,QAChD;AAAA,MACF;AAEA,UAAI,IAAI,IAAI,WAAW,WAAW;AAChC,cAAM,IAAI,mBAAmB,OAAO,SAAS;AAAA,MAC/C;AACA,YAAM,MAAM,gBAAgB;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,SACE,UAAU,QACV,UAAU,WACV,UAAU,cACV,UAAU;AAEd;AAmBO,SAAS,cACd,OACA,YACiB;AACjB,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,SAAS,EAAE,SAAS,MAAM,MAAM,WAAW;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO,EAAE,GAAG,YAAY,UAAU,KAAK;AACzC;","names":[]}
|
|
@@ -19,10 +19,22 @@ interface WaitOptions {
|
|
|
19
19
|
readonly timeoutMs?: number;
|
|
20
20
|
/**
|
|
21
21
|
* Called instead of throwing when no runner can take the job. Return a
|
|
22
|
-
* substitute
|
|
23
|
-
*
|
|
22
|
+
* substitute and the wait resolves with it; return nothing and
|
|
23
|
+
* {@link NoRunnerAvailableError} is thrown.
|
|
24
|
+
*
|
|
25
|
+
* **A string is enough.** It is the app's own fallback answer — a hosted
|
|
26
|
+
* model's text, a cached reply — not wire data, and requiring a whole
|
|
27
|
+
* `DeliveredResult` for it was ceremony that invited invented shapes. The
|
|
28
|
+
* README's own example got it wrong, which is how this was found.
|
|
29
|
+
*
|
|
30
|
+
* **Whatever comes back is labelled `fallback: true` by the wait, not by
|
|
31
|
+
* the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come
|
|
32
|
+
* from the user's own compute must not be reportable as though it did, and
|
|
33
|
+
* that stays true whether an app returns a bare string or a full record it
|
|
34
|
+
* assembled itself. The stamp is applied after this function returns, so
|
|
35
|
+
* there is no shape an app can hand back that hides what it is.
|
|
24
36
|
*/
|
|
25
|
-
readonly onNoRunner?: (reason: string) => DeliveredResult | undefined | Promise<DeliveredResult | undefined>;
|
|
37
|
+
readonly onNoRunner?: (reason: string) => string | DeliveredResult | undefined | Promise<string | DeliveredResult | undefined>;
|
|
26
38
|
/** Abort the wait. */
|
|
27
39
|
readonly signal?: AbortSignal;
|
|
28
40
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,134 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-
|
|
3
|
-
export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-
|
|
4
|
-
import { B as ByollmStore,
|
|
5
|
-
export {
|
|
6
|
-
import { H as HandlerConfig } from './handlers-
|
|
7
|
-
export { B as ByollmHandlers, a as HandlerResult, S as SERVED_PROTOCOL_VERSION } from './handlers-
|
|
1
|
+
import { StoredKeys, JobKind, DeliveredResult, Endpoint, Capability } from '@byollm/protocol';
|
|
2
|
+
import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-C6VzgMgH.js';
|
|
3
|
+
export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-C6VzgMgH.js';
|
|
4
|
+
import { B as ByollmStore, J as JobRecord, E as EnqueueInput, R as RunnerRecord, S as StoredJobInput, C as ClaimArgs, a as RenewArgs, b as RenewResult, A as AdoptArgs, c as CompleteArgs, d as CompleteResult, e as ReleaseArgs, P as PairingRecord, f as ApproveArgs, T as TouchArgs } from './store-Dno2fnHH.js';
|
|
5
|
+
export { g as CompleteHolder, h as JobStore, i as RunnerStore } from './store-Dno2fnHH.js';
|
|
6
|
+
import { H as HandlerConfig } from './handlers-BJYm2kdq.js';
|
|
7
|
+
export { B as ByollmHandlers, a as HandlerResult, S as SERVED_PROTOCOL_VERSION } from './handlers-BJYm2kdq.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The cloud lane — cloud_004 §9.4.
|
|
11
|
+
*
|
|
12
|
+
* `app.enqueue(...)` is identical in every lane; the lane picks the connection
|
|
13
|
+
* plane. In `direct` mode a daemon reaches the site's own handlers. In `cloud`
|
|
14
|
+
* mode it reaches a relay instead, and the site's side of that is this file.
|
|
15
|
+
*
|
|
16
|
+
* ## What actually changes, and what deliberately does not
|
|
17
|
+
*
|
|
18
|
+
* Enqueue does not change at all. The job is validated, sealed at rest to the
|
|
19
|
+
* site's own key and stored, exactly as before — jobs-at-rest encryption is a
|
|
20
|
+
* direct-mode property that the cloud lane inherits rather than replaces.
|
|
21
|
+
*
|
|
22
|
+
* What changes is *who asks for the payload and when*. On the direct plane the
|
|
23
|
+
* daemon asks, and the site answers synchronously because it is the upstream.
|
|
24
|
+
* Through a relay the site is not the upstream, so nobody asks: the site has to
|
|
25
|
+
* find out that a device claimed its job, and seal to that device. Hence a
|
|
26
|
+
* pump rather than a handler.
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* enqueue ──stub──▶ relay (payload stays here, sealed at rest)
|
|
30
|
+
* │
|
|
31
|
+
* pump ◀──who claimed it, and what key?
|
|
32
|
+
* ──payload sealed to that device──▶
|
|
33
|
+
* pump ◀──sealed result── ──▶ store.complete → the app's delivery channel
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ## Why the site polls
|
|
37
|
+
*
|
|
38
|
+
* Everything in this product is outbound. A relay that called site webhooks
|
|
39
|
+
* would need every site publicly reachable, which is the connectivity problem
|
|
40
|
+
* the hub exists to remove — and a serverless site has nowhere to receive a
|
|
41
|
+
* webhook anyway. So the site polls, exactly as a daemon does.
|
|
42
|
+
*/
|
|
43
|
+
interface CloudLaneOptions {
|
|
44
|
+
/** Where the relay lives, e.g. `https://relay.byollm.cloud`. */
|
|
45
|
+
readonly relayOrigin: string;
|
|
46
|
+
/** This site's id at the relay. */
|
|
47
|
+
readonly siteId: string;
|
|
48
|
+
/** Injectable fetch, for tests and for proxies. */
|
|
49
|
+
readonly fetch?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
/** What one pump cycle did, for logging and for tests. */
|
|
52
|
+
/**
|
|
53
|
+
* A relay that could not answer this request — alpha.31.
|
|
54
|
+
*
|
|
55
|
+
* `retryable` is the whole point: a draining pod and a bad signature are both
|
|
56
|
+
* failures, and treating them alike is how a site either falls over on every
|
|
57
|
+
* deploy or stays silently disconnected for a week.
|
|
58
|
+
*/
|
|
59
|
+
declare class RelayUnavailable extends Error {
|
|
60
|
+
readonly retryable: boolean;
|
|
61
|
+
/** The protocol's own code, when the relay sent one. */
|
|
62
|
+
readonly code: string;
|
|
63
|
+
constructor(message: string, retryable: boolean, code: string);
|
|
64
|
+
}
|
|
65
|
+
interface PumpReport {
|
|
66
|
+
/** Jobs sealed to a claiming device this cycle. */
|
|
67
|
+
readonly sealed: string[];
|
|
68
|
+
/** Results opened, verified and written to the store. */
|
|
69
|
+
readonly completed: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Jobs the relay offered that this site refused to seal for.
|
|
72
|
+
*
|
|
73
|
+
* Never silent: a site that cannot open its own at-rest envelope has a key
|
|
74
|
+
* problem, and a device waiting on a payload that will never come is
|
|
75
|
+
* exactly the case `awaiting-payload` exists to bound.
|
|
76
|
+
*/
|
|
77
|
+
readonly refused: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Why this cycle stopped early, when it did — alpha.31.
|
|
80
|
+
*
|
|
81
|
+
* A relay can legitimately say "ask me later": a pod draining through its
|
|
82
|
+
* `preStop` window answers `503 not-ready` to every routed call, and that
|
|
83
|
+
* happens on **every deploy**. Before this existed the lane read the body
|
|
84
|
+
* of that answer, found no `jobs` in it, and threw `TypeError: finished.jobs
|
|
85
|
+
* is not iterable` — a site falling over because its relay was polite.
|
|
86
|
+
*
|
|
87
|
+
* Absent on an ordinary cycle. Present, with the reason, when the lane
|
|
88
|
+
* deferred: a site that quietly did nothing and a site that was told to wait
|
|
89
|
+
* must not look the same in a log.
|
|
90
|
+
*/
|
|
91
|
+
readonly deferred?: string;
|
|
92
|
+
}
|
|
93
|
+
declare class CloudLane {
|
|
94
|
+
#private;
|
|
95
|
+
constructor(deps: {
|
|
96
|
+
options: CloudLaneOptions;
|
|
97
|
+
store: ByollmStore;
|
|
98
|
+
siteKeys: StoredKeys;
|
|
99
|
+
now: () => number;
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* Publish a job's stub for routing.
|
|
103
|
+
*
|
|
104
|
+
* The stub and nothing else — byollm_009 §6 makes that exhaustive by
|
|
105
|
+
* construction, so this cannot leak a payload even by mistake: there is no
|
|
106
|
+
* field on `JobStub` to put one in.
|
|
107
|
+
*/
|
|
108
|
+
publish(record: JobRecord): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Withdraw a job at the relay — cloud_008 §2.2.
|
|
111
|
+
*
|
|
112
|
+
* `app.cancel()` marks the site's own row terminal, which stops the *next*
|
|
113
|
+
* seal. It cannot stop a device that is already running the work, because
|
|
114
|
+
* on this lane the site is not the upstream: only the relay talks to the
|
|
115
|
+
* daemon, and it answered `cancel: []` unconditionally.
|
|
116
|
+
*
|
|
117
|
+
* So the cancellation has to travel. The relay marks the job, stops
|
|
118
|
+
* offering it, and names it to the holding device at its next heartbeat —
|
|
119
|
+
* the same path the direct plane has always had, arriving one hop later.
|
|
120
|
+
*/
|
|
121
|
+
cancel(jobId: string): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* One cycle: seal for anything claimed, collect anything finished.
|
|
124
|
+
*
|
|
125
|
+
* Idempotent and safe to call as often as you like. Exposed as a single
|
|
126
|
+
* cycle rather than hidden behind a timer so a caller decides its own
|
|
127
|
+
* cadence — a serverless site runs it on a cron, a long-lived one on an
|
|
128
|
+
* interval, and a test runs it exactly when it means to.
|
|
129
|
+
*/
|
|
130
|
+
pump(): Promise<PumpReport>;
|
|
131
|
+
}
|
|
8
132
|
|
|
9
133
|
/** Why a job cannot presently run. */
|
|
10
134
|
type NoRunnerReason = "no-runner-paired" | "no-runner-online" | "no-matching-capability" | "audience-admits-nobody";
|
|
@@ -53,6 +177,15 @@ interface ByollmAppOptions {
|
|
|
53
177
|
* holds plaintext (byollm_009 §10).
|
|
54
178
|
*/
|
|
55
179
|
readonly siteKeys: StoredKeys;
|
|
180
|
+
/**
|
|
181
|
+
* Which connection plane this site uses — cloud_004 §9.4.
|
|
182
|
+
*
|
|
183
|
+
* Omitted means `direct`: a daemon reaches this site's own handlers, and
|
|
184
|
+
* everything works as it always has. Supplying a relay switches the plane
|
|
185
|
+
* and nothing else — `enqueue` is identical in every lane, which is the
|
|
186
|
+
* property that lets the same app move between them by config.
|
|
187
|
+
*/
|
|
188
|
+
readonly lane?: CloudLaneOptions;
|
|
56
189
|
}
|
|
57
190
|
/**
|
|
58
191
|
* An enqueued job, with the delivery channel attached.
|
|
@@ -80,6 +213,8 @@ interface JobHandle {
|
|
|
80
213
|
*/
|
|
81
214
|
declare class ByollmApp {
|
|
82
215
|
#private;
|
|
216
|
+
/** Present only in the cloud lane; the site's side of the relay. */
|
|
217
|
+
readonly cloud: CloudLane | undefined;
|
|
83
218
|
constructor(options: ByollmAppOptions);
|
|
84
219
|
/**
|
|
85
220
|
* Enqueue a job.
|
|
@@ -97,7 +232,7 @@ declare class ByollmApp {
|
|
|
97
232
|
* Check `provenance.untrusted` before rendering. It is true for every
|
|
98
233
|
* `named`/`public` job, because that text came from someone else's machine
|
|
99
234
|
* and the app must not present it as its own AI's answer
|
|
100
|
-
* ({@link MUSTS.
|
|
235
|
+
* ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
|
|
101
236
|
*/
|
|
102
237
|
result(jobId: string): Promise<DeliveredResult | null>;
|
|
103
238
|
/** Ask a runner to stop. Queued jobs cancel at once; held jobs at the next heartbeat. */
|
|
@@ -220,8 +355,6 @@ declare function formatSiteKeys(keys: StoredKeys): string;
|
|
|
220
355
|
|
|
221
356
|
/** A device code: the secret the daemon polls with. Never shown to a user. */
|
|
222
357
|
declare function generateDeviceCode(): string;
|
|
223
|
-
/** A runner bearer token. */
|
|
224
|
-
declare function generateRunnerToken(): string;
|
|
225
358
|
/** A runner id. */
|
|
226
359
|
declare function generateRunnerId(): string;
|
|
227
360
|
/** A job id. */
|
|
@@ -264,20 +397,23 @@ declare class MemoryStore implements ByollmStore {
|
|
|
264
397
|
get(jobId: string): Promise<JobRecord | null>;
|
|
265
398
|
claim(args: ClaimArgs): Promise<JobRecord[]>;
|
|
266
399
|
renewLeases(args: RenewArgs): Promise<RenewResult>;
|
|
400
|
+
adopt(args: AdoptArgs): Promise<JobRecord | null>;
|
|
267
401
|
complete(args: CompleteArgs): Promise<CompleteResult>;
|
|
268
402
|
subscribe(jobId: string, onChange: () => void): () => void;
|
|
269
403
|
release(args: ReleaseArgs): Promise<string[]>;
|
|
270
404
|
expireDue(now: number): Promise<JobRecord[]>;
|
|
271
405
|
cancel(jobId: string, now: number): Promise<JobRecord | null>;
|
|
272
406
|
listClaimedBy(runnerId: string): Promise<JobRecord[]>;
|
|
273
|
-
listCancelRequests(runnerId: string): Promise<
|
|
407
|
+
listCancelRequests(runnerId: string): Promise<{
|
|
408
|
+
jobId: string;
|
|
409
|
+
leaseId: string;
|
|
410
|
+
}[]>;
|
|
274
411
|
createPairing(record: PairingRecord): Promise<void>;
|
|
275
412
|
getPairingByDeviceCodeHash(hash: string): Promise<PairingRecord | null>;
|
|
276
413
|
getPairingByUserCode(userCode: string): Promise<PairingRecord | null>;
|
|
277
414
|
approvePairing(args: ApproveArgs): Promise<RunnerRecord>;
|
|
278
415
|
denyPairing(userCode: string, _now: number): Promise<void>;
|
|
279
416
|
consumePairingToken(deviceCodeHash: string): Promise<void>;
|
|
280
|
-
getRunnerByTokenHash(hash: string): Promise<RunnerRecord | null>;
|
|
281
417
|
getRunner(runnerId: string): Promise<RunnerRecord | null>;
|
|
282
418
|
touchRunner(args: TouchArgs): Promise<RunnerRecord | null>;
|
|
283
419
|
revokeRunner(runnerId: string, now: number): Promise<void>;
|
|
@@ -288,4 +424,4 @@ declare class MemoryStore implements ByollmStore {
|
|
|
288
424
|
/** The capability that would serve a kind, if any. */
|
|
289
425
|
declare function capabilityFor(capabilities: readonly Capability[], kind: string): Capability | undefined;
|
|
290
426
|
|
|
291
|
-
export { ApproveArgs, type AvailabilityQuery, ByollmApp, type ByollmAppOptions, ByollmStore, ClaimArgs, CompleteArgs, CompleteResult, EnqueueInput, HandlerConfig, type JobHandle, JobRecord, MemoryStore, type MemoryStoreOptions, type NoRunnerReason, PairingRecord, PollingDeliveryDeps, ReleaseArgs, RenewArgs, RenewResult, ResultDelivery, type RunnerAvailability, RunnerRecord, TouchArgs, WaitOptions, capabilityFor, createFetchHandler, formatSiteKeys, generateDeviceCode, generateJobId, generateRunnerId,
|
|
427
|
+
export { AdoptArgs, ApproveArgs, type AvailabilityQuery, ByollmApp, type ByollmAppOptions, ByollmStore, ClaimArgs, CloudLane, type CloudLaneOptions, CompleteArgs, CompleteResult, EnqueueInput, HandlerConfig, type JobHandle, JobRecord, MemoryStore, type MemoryStoreOptions, type NoRunnerReason, PairingRecord, PollingDeliveryDeps, type PumpReport, RelayUnavailable, ReleaseArgs, RenewArgs, RenewResult, ResultDelivery, type RunnerAvailability, RunnerRecord, TouchArgs, WaitOptions, capabilityFor, createFetchHandler, formatSiteKeys, generateDeviceCode, generateJobId, generateRunnerId, generateSiteKeys, generateUserCode, hashSecret, normalizeUserCode, routeEndpoint, secretsMatch, signatureFrom, siteKeysFromEnv };
|