@byok-sdk/server 0.12.0 → 0.13.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/dist/connections.d.ts +71 -0
- package/dist/device-selection.d.ts +30 -0
- package/dist/event-queue.d.ts +37 -2
- package/dist/index.d.ts +155 -88
- package/dist/index.js +1838 -3370
- package/dist/index.js.map +1 -1
- package/dist/rate-limiter.d.ts +38 -0
- package/dist/relay.d.ts +86 -0
- package/dist/snapshot.d.ts +64 -0
- package/dist/sqlite-support.d.ts +3 -3
- package/dist/stores/sqlite/__tests__/atomic-restart.test.d.ts +1 -0
- package/dist/stores/sqlite/__tests__/conformance.test.d.ts +1 -0
- package/dist/stores/sqlite/index.d.ts +140 -0
- package/dist/stores.d.ts +65 -0
- package/dist/task-handle.d.ts +61 -0
- package/dist/types.d.ts +167 -146
- package/package.json +6 -7
- package/dist/auth.d.ts +0 -218
- package/dist/blob-store.d.ts +0 -89
- package/dist/heartbeat.d.ts +0 -30
- package/dist/http.d.ts +0 -24
- package/dist/hub.d.ts +0 -947
- package/dist/ids.d.ts +0 -3
- package/dist/pairing.d.ts +0 -87
- package/dist/sqlite-blob-store.d.ts +0 -94
- package/dist/sqlite-task-store.d.ts +0 -124
- package/dist/task-store.d.ts +0 -127
- package/dist/ws-server.d.ts +0 -23
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { RuntimeInfo, ToolsetId } from '@byok-sdk/protocol';
|
|
2
|
+
/**
|
|
3
|
+
* What this process has OBSERVED about a device's live connection.
|
|
4
|
+
*
|
|
5
|
+
* This is not a second authority over anything durable. The device row
|
|
6
|
+
* (`DeviceRecord`, `@byok-sdk/cloud`) owns identity and the capability list
|
|
7
|
+
* every admission gate reads; the mailbox owns delivery. What lives here is the
|
|
8
|
+
* part of `conn.hello` the kernel deliberately does not persist — the runtime
|
|
9
|
+
* DISCOVERY block and the client's self-reported version/toolset inventory —
|
|
10
|
+
* plus "when did we last hear from this device at all".
|
|
11
|
+
*
|
|
12
|
+
* Why the kernel does not persist it: `conn.hello.runtimes` describes what a
|
|
13
|
+
* device build could run, changes on every daemon restart, and authorizes
|
|
14
|
+
* nothing (the steer gate reads the CLAIM snapshot, never this). Storing it
|
|
15
|
+
* durably would create a stale second description of a device that outlives the
|
|
16
|
+
* process that saw it. Keeping it as an in-process observation is honest about
|
|
17
|
+
* its lifetime: restart the server and it is gone, exactly like the connection
|
|
18
|
+
* it describes.
|
|
19
|
+
*
|
|
20
|
+
* `connected` is therefore "observed alive and not since forgotten", not "a
|
|
21
|
+
* socket is open" — there are no sockets any more. Two observations set it: the
|
|
22
|
+
* device's own `conn.hello` over `POST /byok/messages`, and a `GET /byok/events`
|
|
23
|
+
* poll (a device that is polling is present even if it never announced). One
|
|
24
|
+
* clears it: revocation, which deletes the device row and everything scoped to
|
|
25
|
+
* it.
|
|
26
|
+
*/
|
|
27
|
+
export interface DeviceConnection {
|
|
28
|
+
connected: boolean;
|
|
29
|
+
/** ISO-8601 instant of the most recent observation. */
|
|
30
|
+
lastSeen: string;
|
|
31
|
+
clientVersion?: string;
|
|
32
|
+
runtimes?: RuntimeInfo[];
|
|
33
|
+
configuredToolsets?: ToolsetId[];
|
|
34
|
+
}
|
|
35
|
+
/** `conn.hello`'s discovery half, as observed on one accepted announcement. */
|
|
36
|
+
export interface DeviceAnnouncement {
|
|
37
|
+
readonly clientVersion?: string;
|
|
38
|
+
readonly runtimes?: readonly RuntimeInfo[];
|
|
39
|
+
readonly configuredToolsets?: readonly ToolsetId[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* In-process device observations, in first-observation order.
|
|
43
|
+
*
|
|
44
|
+
* Insertion order is load-bearing for ambient dispatch selection
|
|
45
|
+
* (`device-selection.ts`): "the first connected device" must be stable and
|
|
46
|
+
* explainable, and a `Map` gives that for free without a second index.
|
|
47
|
+
*/
|
|
48
|
+
export declare class DeviceConnections {
|
|
49
|
+
#private;
|
|
50
|
+
/**
|
|
51
|
+
* Record an accepted `conn.hello`. Discovery fields are REPLACED wholesale,
|
|
52
|
+
* never merged: a daemon that restarted with a runtime removed must not keep
|
|
53
|
+
* advertising it because an older hello mentioned it.
|
|
54
|
+
*/
|
|
55
|
+
announce(deviceId: string, announcement: DeviceAnnouncement, at: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Record any other sign of life (an inbound envelope, a long-poll read).
|
|
58
|
+
* Deliberately additive: it refreshes `lastSeen` and marks the device present
|
|
59
|
+
* without touching the discovery block, because none of those signals carry
|
|
60
|
+
* one and clearing it would lose what the last hello said.
|
|
61
|
+
*/
|
|
62
|
+
touch(deviceId: string, at: string): void;
|
|
63
|
+
get(deviceId: string): DeviceConnection | undefined;
|
|
64
|
+
isConnected(deviceId: string): boolean;
|
|
65
|
+
connectedCount(): number;
|
|
66
|
+
/** Device ids in first-observation order — the order ambient selection walks. */
|
|
67
|
+
ids(): readonly string[];
|
|
68
|
+
/** Drop everything scoped to a device. Called when its registration is deleted (§6.3). */
|
|
69
|
+
forget(deviceId: string): void;
|
|
70
|
+
clear(): void;
|
|
71
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ToolsetId } from '@byok-sdk/protocol';
|
|
2
|
+
/**
|
|
3
|
+
* Ambient device selection for a `dispatch()` that named no `deviceId`.
|
|
4
|
+
*
|
|
5
|
+
* `DispatchInput.deviceId` stays optional (ADR-034), so this rule survives the
|
|
6
|
+
* fold. It is a SCHEDULING convenience and nothing more: every admission gate
|
|
7
|
+
* that actually protects something — Agent capability, strict-agent-only,
|
|
8
|
+
* egress, toolset selection — runs afterwards against the durable device row
|
|
9
|
+
* inside the kernel, on the device this picked exactly as on one the caller
|
|
10
|
+
* named. Picking wrong therefore costs a refusal, never a wrongly-authorized
|
|
11
|
+
* dispatch.
|
|
12
|
+
*
|
|
13
|
+
* "First connected" means first OBSERVED (`connections.ts` preserves
|
|
14
|
+
* first-observation order), which is stable and explainable, unlike any
|
|
15
|
+
* load-shaped ordering this package has no information to compute.
|
|
16
|
+
*/
|
|
17
|
+
export interface DeviceCandidate {
|
|
18
|
+
readonly deviceId: string;
|
|
19
|
+
/** The durable capability list from the device's last accepted `conn.hello`. */
|
|
20
|
+
readonly capabilities: readonly string[] | undefined;
|
|
21
|
+
/** The logical toolset inventory the same announcement reported, if any. */
|
|
22
|
+
readonly configuredToolsets: readonly ToolsetId[] | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface AmbientSelectionQuery {
|
|
25
|
+
/** Every toolset must be present; an unknown inventory is never guessed at. */
|
|
26
|
+
readonly requiredToolsets?: readonly ToolsetId[];
|
|
27
|
+
/** Agent-bound dispatch is the only caller allowed to land on a strict-agent-only device. */
|
|
28
|
+
readonly allowStrictAgentOnly?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function pickFirstConnectedDevice(candidates: readonly DeviceCandidate[], query?: AmbientSelectionQuery): string | undefined;
|
package/dist/event-queue.d.ts
CHANGED
|
@@ -1,18 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A tiny append-only, multi-reader async queue. `push` never blocks; `close`
|
|
3
3
|
* marks the queue done. `subscribe()` returns a fresh async iterator that
|
|
4
|
-
* always replays from the beginning of the buffer, so a consumer that
|
|
5
|
-
* `events()` at any point still
|
|
4
|
+
* always replays from the beginning of the retained buffer, so a consumer that
|
|
5
|
+
* calls `events()` at any point still sees everything still retained for that
|
|
6
|
+
* task's lifetime.
|
|
7
|
+
*
|
|
8
|
+
* Bounded, deliberately (WP3B §3): this queue is a NOTIFICATION relay, not a
|
|
9
|
+
* record of what happened — the durable facts live in the cloud stores a
|
|
10
|
+
* `ByokServer` reads back (`tasks.get`, `TaskHandle.result()`). A consumer that
|
|
11
|
+
* stops iterating must therefore cost bounded memory, not unbounded growth. On
|
|
12
|
+
* overflow the OLDEST entries are dropped and, exactly once per queue, a
|
|
13
|
+
* caller-supplied `truncationMarker` is appended so a reader can tell a
|
|
14
|
+
* complete feed from a clipped one instead of silently seeing a gap.
|
|
6
15
|
*
|
|
7
16
|
* Framework-agnostic on purpose (no Node/WS/Hono types here) so it can be
|
|
8
17
|
* unit-tested and reused regardless of transport.
|
|
9
18
|
*/
|
|
19
|
+
export interface AsyncEventQueueOptions<T> {
|
|
20
|
+
/**
|
|
21
|
+
* Maximum entries retained before drop-oldest engages. Omitted means
|
|
22
|
+
* unbounded — only appropriate for a queue whose producer is itself bounded.
|
|
23
|
+
*/
|
|
24
|
+
readonly maxBuffered?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Appended once, after the first drop, so the truncation is observable.
|
|
27
|
+
* Omitted means a bounded queue that drops silently.
|
|
28
|
+
*/
|
|
29
|
+
readonly truncationMarker?: T;
|
|
30
|
+
}
|
|
10
31
|
export declare class AsyncEventQueue<T> {
|
|
11
32
|
private readonly buffer;
|
|
33
|
+
private nextSequence;
|
|
12
34
|
private closed;
|
|
13
35
|
private waiters;
|
|
36
|
+
private readonly maxBuffered;
|
|
37
|
+
private readonly truncationMarker;
|
|
38
|
+
private truncationNoted;
|
|
39
|
+
constructor(options?: AsyncEventQueueOptions<T>);
|
|
40
|
+
/** True once this queue has dropped at least one entry. */
|
|
41
|
+
get truncated(): boolean;
|
|
14
42
|
push(value: T): void;
|
|
15
43
|
close(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Drop the oldest entries back down to the bound. The marker is queue
|
|
46
|
+
* metadata, not a buffered entry: every subscriber observes it at most once
|
|
47
|
+
* after the first drop, while all retained capacity remains available to
|
|
48
|
+
* real events.
|
|
49
|
+
*/
|
|
50
|
+
private enforceBound;
|
|
16
51
|
private wake;
|
|
17
52
|
private waitForMore;
|
|
18
53
|
/** Async-iterate the buffer from index 0, waiting for new pushes until closed. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,65 +1,85 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
3
|
-
import { type
|
|
4
|
-
import {
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { type PairingCodeInfo } from '@byok-sdk/cloud';
|
|
3
|
+
import { type AgentRef } from '@byok-sdk/protocol';
|
|
4
|
+
import type { MailboxRetentionInput, MailboxRetentionResult } from '@byok-sdk/core';
|
|
5
5
|
import type { ByokServerEvent, AgentContentReadRequest, AgentHomeProjectionRequest, AgentHomeProjectionStatusReadback, AgentEgressReceipt, CreateByokServerOptions, DispatchInput, FreshAgentEgressDispatchInput, HubStats, MachineInfo, TaskHandle, TaskSnapshot } from './types';
|
|
6
|
-
export type { ByokServerEvent, AgentContentReadRequest, AgentHomeProjectionRequest, AgentHomeProjectionStatusReadback, AgentEgressReceipt, CreateByokServerOptions, DispatchInput, FreshAgentEgressDispatchInput, HubStats, MachineInfo, ServerTaskEvent, TaskHandle, TaskResult, TaskSnapshot, } from './types';
|
|
7
|
-
export type { CreateTaskInput, TaskRecord, TaskStore } from './task-store';
|
|
8
|
-
export { IllegalTaskTransitionError, InMemoryTaskStore } from './task-store';
|
|
6
|
+
export type { ByokServerEvent, AgentContentReadRequest, AgentHomeProjectionRequest, AgentHomeProjectionStatusReadback, AgentEgressReceipt, ByokServerStorage, CreateByokServerOptions, DispatchInput, FreshAgentEgressDispatchInput, HubStats, MachineInfo, ServerTaskEvent, TaskHandle, TaskResult, TaskSnapshot, } from './types';
|
|
9
7
|
/**
|
|
10
|
-
* M5 (approval targeting, docs/protocol.md §5.3):
|
|
11
|
-
* this
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* M5 (approval targeting, docs/protocol.md §5.3): `TaskHandle.approve`/`reject`'s
|
|
9
|
+
* `opts.approvalId` targeting throws this when the id names an approval the
|
|
10
|
+
* task has already superseded, so a caller needs it to `instanceof`-check and
|
|
11
|
+
* inspect the two ids. Re-exported from `@byok-sdk/cloud`, which owns the gate
|
|
12
|
+
* both the embedded and the hosted surface are decided by — one class, one
|
|
13
|
+
* `instanceof` that works across both.
|
|
16
14
|
*/
|
|
17
|
-
export { StaleApprovalError } from '
|
|
15
|
+
export { StaleApprovalError } from '@byok-sdk/cloud';
|
|
18
16
|
/**
|
|
19
|
-
* S0 (GAP-002): `TaskHandle.steer`
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* S0 (GAP-002): `TaskHandle.steer` throws this when the runtime that claimed
|
|
18
|
+
* the task cannot be steered, when the task isn't running, or when it's already
|
|
19
|
+
* terminal. The GATE is the kernel's — it reads the claim-time capability
|
|
20
|
+
* snapshot and nothing else — and so is the `code`. The CLASS is this
|
|
21
|
+
* package's, because it carries `state: TaskState`, the wire vocabulary this
|
|
22
|
+
* surface speaks and the kernel deliberately has no field for. See
|
|
23
|
+
* `task-handle.ts` for the full reasoning and for why `SteerRejectionCode` and
|
|
24
|
+
* {@link StaleApprovalError} stay kernel re-exports.
|
|
24
25
|
*/
|
|
25
|
-
export { SteerRejectedError } from './
|
|
26
|
-
export type { SteerRejectionCode } from '
|
|
27
|
-
export { AgentHomeProjectionCompletionError } from './hub';
|
|
28
|
-
export type { AgentHomeProjectionCompletionErrorCode } from './hub';
|
|
29
|
-
export { PairingAttemptConflictError, PairingCodeInvalidError } from './pairing';
|
|
30
|
-
export type { PairingAttemptBinding, PairingCodeClaims, PairingCodeInfo, PairingCompletion } from './pairing';
|
|
31
|
-
export type { AccessTokenClaims, AuthenticatedDevice, DeviceRecord, TenantId, TokenSigner, } from './auth';
|
|
26
|
+
export { SteerRejectedError } from './task-handle';
|
|
27
|
+
export type { SteerRejectionCode } from '@byok-sdk/cloud';
|
|
32
28
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* (`resolveByDeviceId`, for the two pre-tenant wire endpoints) exists only
|
|
37
|
-
* inside this package — exporting the class would hand every embedder a
|
|
38
|
-
* cross-tenant device oracle for free.
|
|
29
|
+
* Auth v2 types an embedder needs to talk about devices and tokens. All owned
|
|
30
|
+
* by `@byok-sdk/cloud` now — this package no longer has an auth plane of its
|
|
31
|
+
* own to keep in agreement with one.
|
|
39
32
|
*/
|
|
40
|
-
export {
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
export type {
|
|
44
|
-
export { SqliteTaskStore } from './sqlite-task-store';
|
|
45
|
-
export type { SqliteBlobStoreOptions } from './sqlite-blob-store';
|
|
46
|
-
export { SqliteBlobStore } from './sqlite-blob-store';
|
|
33
|
+
export type { AccessTokenClaims, DeviceRecord, PairingCodeInfo, TenantId, TokenSigner } from '@byok-sdk/cloud';
|
|
34
|
+
export { createHmacTokenSigner } from '@byok-sdk/cloud';
|
|
35
|
+
/** Cutoffs and result of {@link ByokServer.mailbox.collectRetired}, owned by `@byok-sdk/core`. */
|
|
36
|
+
export type { MailboxRetentionInput, MailboxRetentionResult } from '@byok-sdk/core';
|
|
47
37
|
export { SqliteUnavailableError } from './sqlite-support';
|
|
48
38
|
export type { RateLimiterOptions } from './rate-limiter';
|
|
39
|
+
export { DEFAULT_TASK_EVENT_BUFFER_LIMIT, DEFAULT_TASK_EVENT_RETENTION_MS } from './relay';
|
|
40
|
+
/** Page size `tasks.list()` uses when the caller names none. */
|
|
41
|
+
export declare const DEFAULT_TASK_PAGE_LIMIT = 100;
|
|
42
|
+
/** Input to {@link ByokServer.pairing.createPairingCode}. */
|
|
43
|
+
export interface CreatePairingCodeInput {
|
|
44
|
+
/**
|
|
45
|
+
* The product the redeeming device pairs into. Must be this instance's own
|
|
46
|
+
* `productId`: an embedded server serves exactly one product, and a code for
|
|
47
|
+
* some other product would mint a device every bearer-authed route then
|
|
48
|
+
* refuses (`instanceProductId`, `@byok-sdk/cloud`). Fail closed rather than
|
|
49
|
+
* silently issuing an unusable code.
|
|
50
|
+
*
|
|
51
|
+
* The TENANT is not a parameter: it is derived from `productId` once, at
|
|
52
|
+
* construction (`serverTenantId`, `stores.ts`), because this surface has no
|
|
53
|
+
* second tenant to name.
|
|
54
|
+
*/
|
|
55
|
+
readonly productId: string;
|
|
56
|
+
/** Overrides the default single-use code lifetime. */
|
|
57
|
+
readonly ttlMs?: number;
|
|
58
|
+
}
|
|
59
|
+
/** One bounded page of this server's tasks. */
|
|
60
|
+
export interface TaskPage {
|
|
61
|
+
readonly tasks: readonly TaskSnapshot[];
|
|
62
|
+
/**
|
|
63
|
+
* Pass as the next call's `cursor`. ABSENT means the walk is over — a caller
|
|
64
|
+
* stops on absence, not on an empty page, so a page that exactly fills
|
|
65
|
+
* `limit` with nothing after it still terminates.
|
|
66
|
+
*/
|
|
67
|
+
readonly nextCursor?: string;
|
|
68
|
+
}
|
|
69
|
+
/** Query for {@link ByokServer.tasks.list}. */
|
|
70
|
+
export interface TaskListQuery {
|
|
71
|
+
/** Maximum snapshots in the page. Defaults to {@link DEFAULT_TASK_PAGE_LIMIT}. */
|
|
72
|
+
readonly limit?: number;
|
|
73
|
+
/** The `nextCursor` from the previous page; absent starts at the beginning. */
|
|
74
|
+
readonly cursor?: string;
|
|
75
|
+
}
|
|
49
76
|
/** The object `createByokServer` returns — the SaaS-embedder-facing surface. */
|
|
50
77
|
export interface ByokServer {
|
|
51
|
-
/** Hono app exposing the
|
|
78
|
+
/** Hono app exposing every device route, plus the opt-in `/healthz`. Mount it, or use its `.fetch` with `@hono/node-server`. */
|
|
52
79
|
hono: Hono;
|
|
53
|
-
/** Wire up the `GET /byok/ws` upgrade on the raw Node HTTP server serving `hono`. */
|
|
54
|
-
attachWebSocket(server: HttpServer): void;
|
|
55
80
|
pairing: {
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
* device will be paired into (docs/protocol.md §6.1) — the SaaS's own
|
|
59
|
-
* auth/device-flow UI is the only party that knows them, and the device
|
|
60
|
-
* never gets to name its own. There is no claimless overload.
|
|
61
|
-
*/
|
|
62
|
-
createPairingCode(claims: PairingCodeClaims): PairingCodeInfo;
|
|
81
|
+
/** Mint a single-use pairing code for this server's product and tenant (docs/protocol.md §6.1). */
|
|
82
|
+
createPairingCode(input: CreatePairingCodeInput): Promise<PairingCodeInfo>;
|
|
63
83
|
};
|
|
64
84
|
dispatch(input: DispatchInput): Promise<TaskHandle>;
|
|
65
85
|
/** Dispatch a fresh Agent execution whose runtime will mint its session after start. */
|
|
@@ -68,18 +88,28 @@ export interface ByokServer {
|
|
|
68
88
|
requestAgentContentRead(input: AgentContentReadRequest): Promise<void>;
|
|
69
89
|
/** Enqueue one task-free, exact-device Agent-home projection. */
|
|
70
90
|
enqueueAgentHomeProjection(input: AgentHomeProjectionRequest): Promise<AgentHomeProjectionStatusReadback>;
|
|
71
|
-
/**
|
|
72
|
-
readAgentHomeProjection(deviceId: string, requestId: string): AgentHomeProjectionStatusReadback | undefined
|
|
91
|
+
/** Durable desired-state and terminal-outcome readback for one exact device-and-Agent request. */
|
|
92
|
+
readAgentHomeProjection(deviceId: string, agentRef: AgentRef, requestId: string): Promise<AgentHomeProjectionStatusReadback | undefined>;
|
|
73
93
|
tasks: {
|
|
74
|
-
get(taskId: string): TaskSnapshot | undefined
|
|
75
|
-
|
|
94
|
+
get(taskId: string): Promise<TaskSnapshot | undefined>;
|
|
95
|
+
/**
|
|
96
|
+
* One bounded page, keyset-paged by task id. Paged rather than "all of
|
|
97
|
+
* them" because the underlying store is: an unbounded `list()` would have
|
|
98
|
+
* to walk every page internally and hand back a snapshot that was never
|
|
99
|
+
* consistent at any single instant.
|
|
100
|
+
*/
|
|
101
|
+
list(query?: TaskListQuery): Promise<TaskPage>;
|
|
102
|
+
};
|
|
103
|
+
/** Trusted embedder access to committed blob download grants. */
|
|
104
|
+
blobs: {
|
|
105
|
+
getDownloadUrl(blobId: string): Promise<string | undefined>;
|
|
76
106
|
};
|
|
77
|
-
/**
|
|
107
|
+
/** Reliable Agent egress receipt readback. */
|
|
78
108
|
egress: {
|
|
79
|
-
get(deviceId: string, eventId: string): AgentEgressReceipt | undefined
|
|
109
|
+
get(deviceId: string, agentRef: AgentRef, eventId: string): Promise<AgentEgressReceipt | undefined>;
|
|
80
110
|
};
|
|
81
111
|
machines: {
|
|
82
|
-
list(): MachineInfo[]
|
|
112
|
+
list(): Promise<MachineInfo[]>;
|
|
83
113
|
};
|
|
84
114
|
events: {
|
|
85
115
|
subscribe(): AsyncIterable<ByokServerEvent>;
|
|
@@ -87,48 +117,85 @@ export interface ByokServer {
|
|
|
87
117
|
/**
|
|
88
118
|
* Device revocation (§6.3) — server-side only, no wire message. Revoking a
|
|
89
119
|
* device DELETES its registration, so its next `/byok/challenge`,
|
|
90
|
-
* `/byok/token`,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
120
|
+
* `/byok/token`, or authed HTTP call gets a 401 — the same answer as for a
|
|
121
|
+
* device id that was never registered — and its only recourse is to re-run
|
|
122
|
+
* `/byok/pair`. The device-scoped state the row owned (outstanding challenge
|
|
123
|
+
* nonces, presence, inbound dedup) is deleted with it; what the device DID
|
|
124
|
+
* (tasks, receipts) is history and survives.
|
|
95
125
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
126
|
+
* DEVICE-ID ONLY. The hosted control plane's own revocation is tenant-first
|
|
127
|
+
* (a tenant may only revoke a device it owns), but an embedded server owns
|
|
128
|
+
* exactly ONE tenant and binds it here itself: `TenantId` is a branded type an
|
|
129
|
+
* embedder cannot mint, and nothing on this surface — `ByokServer`,
|
|
130
|
+
* `MachineInfo`, `PairingCodeInfo` — hands one back, so a tenant-first
|
|
131
|
+
* parameter would make this method uncallable from outside the package rather
|
|
132
|
+
* than safer. The scoping it provided is unchanged, just not the caller's to
|
|
133
|
+
* state: a device id this server does not know resolves to nothing and is a
|
|
134
|
+
* silent no-op.
|
|
99
135
|
*/
|
|
100
136
|
devices: {
|
|
101
|
-
revoke(
|
|
137
|
+
revoke(deviceId: string): Promise<void>;
|
|
102
138
|
};
|
|
103
139
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
140
|
+
* Mailbox retention for this server's tenant — the host control-plane
|
|
141
|
+
* operation core defines (`MailboxStore.collectRetired`), forwarded verbatim.
|
|
142
|
+
*
|
|
143
|
+
* A pass-through, deliberately, and NOT a retention policy: the caller names
|
|
144
|
+
* both cutoffs, so this package invents no TTL, runs no timer, and holds no
|
|
145
|
+
* second opinion about when a device's undelivered work is declared lost.
|
|
146
|
+
* Nothing in `@byok-sdk/core`, `@byok-sdk/cloud` or this façade drives the
|
|
147
|
+
* sweep on its own, which is exactly why an embedder needs a way to reach it:
|
|
148
|
+
* without one, an embedded server retires nothing, ever, and the
|
|
149
|
+
* `cursor_too_old` floor can never move.
|
|
150
|
+
*
|
|
151
|
+
* Acked rows appended before `ackedBefore` are DELETED; unacked rows appended
|
|
152
|
+
* before `expireUnackedBefore` are dead-lettered as `expired` and stay
|
|
153
|
+
* visible, which is what moves `recoverableFrom` and turns a device polling
|
|
154
|
+
* from a lost cursor into a `409 cursor_too_old` resync instead of a silently
|
|
155
|
+
* short page. Both cutoffs must be canonical ISO-8601 UTC.
|
|
156
|
+
*/
|
|
157
|
+
mailbox: {
|
|
158
|
+
collectRetired(input: MailboxRetentionInput): Promise<MailboxRetentionResult>;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Release what this instance holds: the relay's per-task feeds and their
|
|
162
|
+
* reclamation timers, and the connection observations. Call it on shutdown so
|
|
163
|
+
* nothing keeps the process alive or leaks a handle in tests; safe to call
|
|
164
|
+
* more than once. SQLite embedders that need to await handle release should
|
|
165
|
+
* call {@link ByokServer.close} instead.
|
|
108
166
|
*/
|
|
109
167
|
stop(): void;
|
|
168
|
+
/** Drain pending store calls and release the selected storage authority. */
|
|
169
|
+
close(): Promise<void>;
|
|
110
170
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
171
|
+
* A plain, serializable snapshot of this server's current state. See
|
|
172
|
+
* {@link HubStats} for the field-by-field contract.
|
|
173
|
+
*
|
|
174
|
+
* Async because `taskCountsByState` is COMPUTED from the durable task store
|
|
175
|
+
* on every call rather than mirrored into a counter this package would then
|
|
176
|
+
* have to keep in agreement with it — that mirror was the second task
|
|
177
|
+
* authority the fold exists to remove. Deliberately in-process only: never
|
|
178
|
+
* exposed over HTTP by this SDK itself (see
|
|
179
|
+
* `CreateByokServerOptions.healthzRoute`); an embedder that wants any of it
|
|
180
|
+
* surfaced remotely builds its own authenticated route around this method.
|
|
119
181
|
*/
|
|
120
|
-
stats(): HubStats
|
|
182
|
+
stats(): Promise<HubStats>;
|
|
121
183
|
}
|
|
122
184
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
185
|
+
* Embedded reference coordinator: a thin façade over `@byok-sdk/cloud`'s
|
|
186
|
+
* kernel, composed against the explicitly selected embedded stores.
|
|
187
|
+
*
|
|
188
|
+
* What that means concretely — and it is the whole point of WP3B — is that this
|
|
189
|
+
* package owns NO coordination semantics any more. Pairing, tokens, the inbound
|
|
190
|
+
* gate, task ownership, first-terminal-wins, approvals, steering, cancellation,
|
|
191
|
+
* long-poll redelivery and the `cursor_too_old` floor are all the kernel's, and
|
|
192
|
+
* a device cannot tell this from a hosted deployment. What is left here is the
|
|
193
|
+
* embedded shape: one product, one tenant, a `TaskHandle` for hosts that want
|
|
194
|
+
* one, an in-process notification relay, and the observability an embedder used
|
|
195
|
+
* to get from the hub.
|
|
196
|
+
*
|
|
197
|
+
* State is in-memory by default. Explicit SQLite mode persists the six
|
|
198
|
+
* coordination interfaces whose contracts cross a process restart; all other
|
|
199
|
+
* ports remain process-local.
|
|
133
200
|
*/
|
|
134
201
|
export declare function createByokServer(opts: CreateByokServerOptions): ByokServer;
|