@ceralive/cerastream 2026.7.1 → 2026.7.3
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 +5 -1
- package/dist/client.d.ts +6 -1
- package/dist/client.js +24 -6
- package/dist/constants.d.ts +9 -1
- package/dist/constants.js +9 -1
- package/dist/errors.d.ts +28 -2
- package/dist/errors.js +34 -2
- package/dist/events.d.ts +158 -2
- package/dist/events.js +36 -1
- package/dist/messages.d.ts +82 -0
- package/dist/messages.js +15 -1
- package/dist/types.d.ts +47 -0
- package/dist/types.js +33 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,10 +6,12 @@ engine's JSON-RPC 2.0 / NDJSON control plane over a Unix domain socket.
|
|
|
6
6
|
|
|
7
7
|
- JSON-RPC 2.0 envelope + `hello` handshake schemas
|
|
8
8
|
- The eight v1 control methods (Zod params + result schemas + inferred types)
|
|
9
|
-
- The
|
|
9
|
+
- The eight server-push event payloads (discriminated union)
|
|
10
10
|
- Two-tier error codes (RPC + runtime)
|
|
11
11
|
- A unified engine config schema (= `start` params)
|
|
12
12
|
- A `CerastreamClient` interface + `connect()` factory (UDS transport)
|
|
13
|
+
- Additive `client.getCapabilities()` discovery for platform, source, encoder, and
|
|
14
|
+
local preview availability
|
|
13
15
|
|
|
14
16
|
The Zod schemas, types, and constants are the frozen wire contract; `connect()`
|
|
15
17
|
drives that contract over an NDJSON/UDS transport with no native dependencies.
|
|
@@ -45,6 +47,8 @@ import { connect } from "@ceralive/cerastream";
|
|
|
45
47
|
|
|
46
48
|
const client = await connect({ autoReconnect: true }); // hello handshake runs here
|
|
47
49
|
console.log(client.hello.engine_version);
|
|
50
|
+
const capabilities = await client.getCapabilities();
|
|
51
|
+
console.log(capabilities.preview?.bound);
|
|
48
52
|
|
|
49
53
|
const sub = await client.subscribeEvents({ topics: ["status", "bitrate"] }, (ev) => {
|
|
50
54
|
if (ev.type === "bitrate") console.log("bitrate", ev.current_bitrate);
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type HelloResult } from "./envelope.js";
|
|
2
2
|
import { type EventParams } from "./events.js";
|
|
3
|
-
import { type ListDevicesParams, type ListDevicesResult, type PreviewSessionParams, type PreviewSessionResult, type ReloadConfigParams, type ReloadConfigResult, type SetBitrateParams, type SetBitrateResult, type StartParams, type StartResult, type StopParams, type StopResult, type SubscribeEventsParams, type SubscribeEventsResult, type SwitchInputParams, type SwitchInputResult } from "./messages.js";
|
|
3
|
+
import { type GetCapabilitiesResult, type ListDevicesParams, type ListDevicesResult, type PreviewSessionParams, type PreviewSessionResult, type ReloadConfigParams, type ReloadConfigResult, type SetBitrateParams, type SetBitrateResult, type StartParams, type StartResult, type StopParams, type StopResult, type SubscribeEventsParams, type SubscribeEventsResult, type SwitchInputParams, type SwitchInputResult } from "./messages.js";
|
|
4
4
|
/** Options for {@link connect}. All optional — `connect({})` is valid. */
|
|
5
5
|
export interface ConnectOptions {
|
|
6
6
|
/** Control socket path override. Defaults to the resolved /run/cerastream/control.sock. */
|
|
@@ -77,6 +77,11 @@ export interface CerastreamClient {
|
|
|
77
77
|
* @returns The discovered capture devices.
|
|
78
78
|
*/
|
|
79
79
|
listDevices(params?: ListDevicesParams): Promise<ListDevicesResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Read the engine's additive capability contract, including local preview
|
|
82
|
+
* availability. This request intentionally sits outside the frozen v1 map.
|
|
83
|
+
*/
|
|
84
|
+
getCapabilities(): Promise<GetCapabilitiesResult>;
|
|
80
85
|
/**
|
|
81
86
|
* Subscribe to the live event stream; `handler` fires for each pushed event.
|
|
82
87
|
* @param params The topics to subscribe to (absent ⇒ all topics).
|
package/dist/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CONTROL_SOCKET_PATH, PROTOCOL_VERSION } from "./constants.js";
|
|
2
2
|
import { helloResultSchema, rpcErrorSchema, rpcResponseSchema, } from "./envelope.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
3
4
|
import { CerastreamConnectionError, CerastreamRpcError, CerastreamTimeoutError, } from "./errors.js";
|
|
4
5
|
import { eventParamsSchema } from "./events.js";
|
|
5
6
|
import { controlSocketPath } from "./paths.js";
|
|
6
|
-
import { requestSchemas, } from "./messages.js";
|
|
7
|
+
import { getCapabilitiesResultSchema, requestSchemas, } from "./messages.js";
|
|
7
8
|
import { LineSocket } from "./transport.js";
|
|
8
9
|
const DEFAULTS = {
|
|
9
10
|
requestTimeoutMs: 10_000,
|
|
@@ -49,7 +50,7 @@ class ClientImpl {
|
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
catch (err) {
|
|
52
|
-
throw new CerastreamConnectionError(`failed to connect to cerastream control socket at ${this.socketPath}`, err);
|
|
53
|
+
throw new CerastreamConnectionError(`failed to connect to cerastream control socket at ${this.socketPath}`, err, classifyConnectError(err, this.socketPath));
|
|
53
54
|
}
|
|
54
55
|
this.socket = socket;
|
|
55
56
|
}
|
|
@@ -78,6 +79,9 @@ class ClientImpl {
|
|
|
78
79
|
listDevices(params) {
|
|
79
80
|
return this.call("list-devices", params);
|
|
80
81
|
}
|
|
82
|
+
async getCapabilities() {
|
|
83
|
+
return getCapabilitiesResultSchema.parse(await this.rawRequest("get-capabilities", undefined));
|
|
84
|
+
}
|
|
81
85
|
previewSession(params) {
|
|
82
86
|
return this.call("preview-session", params);
|
|
83
87
|
}
|
|
@@ -98,7 +102,7 @@ class ClientImpl {
|
|
|
98
102
|
this.intentionalClose = true;
|
|
99
103
|
for (const sub of this.subscriptions)
|
|
100
104
|
sub.close();
|
|
101
|
-
const conn = new CerastreamConnectionError("client closed");
|
|
105
|
+
const conn = new CerastreamConnectionError("client closed", undefined, "closed");
|
|
102
106
|
this.rejectAllPending(conn);
|
|
103
107
|
this.socket?.close();
|
|
104
108
|
this.socket = undefined;
|
|
@@ -114,7 +118,7 @@ class ClientImpl {
|
|
|
114
118
|
rawRequest(method, params) {
|
|
115
119
|
const socket = this.socket;
|
|
116
120
|
if (!socket) {
|
|
117
|
-
return Promise.reject(new CerastreamConnectionError("control connection is not open"));
|
|
121
|
+
return Promise.reject(new CerastreamConnectionError("control connection is not open", undefined, "closed"));
|
|
118
122
|
}
|
|
119
123
|
const id = this.nextId++;
|
|
120
124
|
const envelope = {
|
|
@@ -136,7 +140,7 @@ class ClientImpl {
|
|
|
136
140
|
catch (err) {
|
|
137
141
|
clearTimeout(timer);
|
|
138
142
|
this.pending.delete(id);
|
|
139
|
-
reject(new CerastreamConnectionError("failed to write request", err));
|
|
143
|
+
reject(new CerastreamConnectionError("failed to write request", err, "lost"));
|
|
140
144
|
}
|
|
141
145
|
});
|
|
142
146
|
}
|
|
@@ -203,7 +207,7 @@ class ClientImpl {
|
|
|
203
207
|
this.socket = undefined;
|
|
204
208
|
if (this.intentionalClose)
|
|
205
209
|
return;
|
|
206
|
-
const conn = new CerastreamConnectionError("control connection lost", err);
|
|
210
|
+
const conn = new CerastreamConnectionError("control connection lost", err, "lost");
|
|
207
211
|
this.rejectAllPending(conn);
|
|
208
212
|
if (this.autoReconnect)
|
|
209
213
|
void this.reconnectLoop();
|
|
@@ -260,6 +264,20 @@ function safeControlSocketPath() {
|
|
|
260
264
|
function isObject(value) {
|
|
261
265
|
return typeof value === "object" && value !== null;
|
|
262
266
|
}
|
|
267
|
+
// Classify a connect-time transport failure into a stable machine code. Bun's
|
|
268
|
+
// `connect` collapses several distinct errors onto ENOENT and does not reliably
|
|
269
|
+
// surface ECONNREFUSED for a stale AF_UNIX socket, so an explicit errno is
|
|
270
|
+
// honored when present, then the socket file's presence disambiguates: a path
|
|
271
|
+
// that is gone is `absent` (engine not up yet), a path that still exists but
|
|
272
|
+
// won't accept is `refused` (a crashed/stale listener).
|
|
273
|
+
function classifyConnectError(err, socketPath) {
|
|
274
|
+
const raw = isObject(err) ? err.code : undefined;
|
|
275
|
+
if (raw === "ECONNREFUSED")
|
|
276
|
+
return "refused";
|
|
277
|
+
if (raw === "EACCES" || raw === "EPERM")
|
|
278
|
+
return "unreachable";
|
|
279
|
+
return existsSync(socketPath) ? "refused" : "absent";
|
|
280
|
+
}
|
|
263
281
|
function sleep(ms) {
|
|
264
282
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
265
283
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const PROTOCOL_VERSION: "cerastream-ipc/1";
|
|
|
8
8
|
* additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
|
|
9
9
|
* only moves when the wire schema itself does, in lockstep across both languages.
|
|
10
10
|
*/
|
|
11
|
-
export declare const SCHEMA_VERSION: "0.
|
|
11
|
+
export declare const SCHEMA_VERSION: "0.8.0";
|
|
12
12
|
/** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
|
|
13
13
|
export declare const DEFAULT_IPC_DIR: "/run/cerastream";
|
|
14
14
|
/** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
|
|
@@ -56,6 +56,14 @@ export declare const SUPPORTED_PROFILES: readonly ["balanced", "low-latency", "r
|
|
|
56
56
|
* set or any preset's meaning changes. Mirrors the Rust `PROFILE_CATALOG_VERSION`.
|
|
57
57
|
*/
|
|
58
58
|
export declare const PROFILE_CATALOG_VERSION: "1.0.0";
|
|
59
|
+
/**
|
|
60
|
+
* Named engine features `get-capabilities` advertises in `features`. This is the
|
|
61
|
+
* fail-closed negotiation contract: CeraUI sends a new out-of-schema field ONLY
|
|
62
|
+
* when its feature is listed, so an engine that predates a feature (and silently
|
|
63
|
+
* ignores the field) never applies a semantic the caller assumed. Mirrors the
|
|
64
|
+
* Rust `ENGINE_FEATURES`.
|
|
65
|
+
*/
|
|
66
|
+
export declare const ENGINE_FEATURES: readonly ["video-passthrough"];
|
|
59
67
|
/** Engine binary name (systemd-owned; CeraUI never spawns it — ADR-0005). */
|
|
60
68
|
export declare const CERASTREAM_BIN: "cerastream";
|
|
61
69
|
export declare const DEFAULT_MIN_BITRATE = 300;
|
package/dist/constants.js
CHANGED
|
@@ -11,7 +11,7 @@ export const PROTOCOL_VERSION = "cerastream-ipc/1";
|
|
|
11
11
|
* additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
|
|
12
12
|
* only moves when the wire schema itself does, in lockstep across both languages.
|
|
13
13
|
*/
|
|
14
|
-
export const SCHEMA_VERSION = "0.
|
|
14
|
+
export const SCHEMA_VERSION = "0.8.0";
|
|
15
15
|
/** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
|
|
16
16
|
export const DEFAULT_IPC_DIR = "/run/cerastream";
|
|
17
17
|
/** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
|
|
@@ -65,6 +65,14 @@ export const SUPPORTED_PROFILES = [
|
|
|
65
65
|
* set or any preset's meaning changes. Mirrors the Rust `PROFILE_CATALOG_VERSION`.
|
|
66
66
|
*/
|
|
67
67
|
export const PROFILE_CATALOG_VERSION = "1.0.0";
|
|
68
|
+
/**
|
|
69
|
+
* Named engine features `get-capabilities` advertises in `features`. This is the
|
|
70
|
+
* fail-closed negotiation contract: CeraUI sends a new out-of-schema field ONLY
|
|
71
|
+
* when its feature is listed, so an engine that predates a feature (and silently
|
|
72
|
+
* ignores the field) never applies a semantic the caller assumed. Mirrors the
|
|
73
|
+
* Rust `ENGINE_FEATURES`.
|
|
74
|
+
*/
|
|
75
|
+
export const ENGINE_FEATURES = ["video-passthrough"];
|
|
68
76
|
/** Engine binary name (systemd-owned; CeraUI never spawns it — ADR-0005). */
|
|
69
77
|
export const CERASTREAM_BIN = "cerastream";
|
|
70
78
|
// ---- config defaults (mirror ceracoder, the engine being replaced) ----
|
package/dist/errors.d.ts
CHANGED
|
@@ -22,15 +22,41 @@ export declare class CerastreamRpcError extends Error {
|
|
|
22
22
|
readonly requestId: number | string | null;
|
|
23
23
|
constructor(code: number, message: string, dataCode: string | undefined, requestId: number | string | null);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Machine-readable classification of a {@link CerastreamConnectionError}, stable
|
|
27
|
+
* across releases so CeraUI's start-failure taxonomy can branch on it without
|
|
28
|
+
* parsing messages. Distinguishes the three connect-phase failure shapes the
|
|
29
|
+
* engine's systemd lifecycle produces (an absent socket during boot, a stale
|
|
30
|
+
* socket left by a crashed instance, an otherwise-unreachable socket) from a
|
|
31
|
+
* mid-session drop or an intentional client close:
|
|
32
|
+
*
|
|
33
|
+
* - `absent` — the control socket path does not exist (ENOENT): the engine
|
|
34
|
+
* is not running yet, or systemd has torn the old socket down
|
|
35
|
+
* during a restart. Maps to `engine_unavailable`/`engine_restarting`.
|
|
36
|
+
* - `refused` — the socket path exists but the connection was refused
|
|
37
|
+
* (ECONNREFUSED): a stale socket a crashed/`SIGKILL`ed engine
|
|
38
|
+
* left behind, or one mid-`bind` on restart. Maps to
|
|
39
|
+
* `engine_restarting`/`engine_unavailable`.
|
|
40
|
+
* - `unreachable` — any other connect-time transport failure (e.g. EACCES).
|
|
41
|
+
* - `lost` — an established connection dropped (socket closed/errored) or
|
|
42
|
+
* a write failed while a request was in flight.
|
|
43
|
+
* - `closed` — the client was closed, or a request was issued on a
|
|
44
|
+
* not-open connection.
|
|
45
|
+
*/
|
|
46
|
+
export declare const CERASTREAM_CONNECT_ERROR_CODES: readonly ["absent", "refused", "unreachable", "lost", "closed"];
|
|
47
|
+
export type CerastreamConnectErrorCode = (typeof CERASTREAM_CONNECT_ERROR_CODES)[number];
|
|
25
48
|
/**
|
|
26
49
|
* Thrown when the control connection is lost (socket closed/errored) while a
|
|
27
50
|
* request was in flight, or when {@link connect} cannot reach the engine and
|
|
28
|
-
* auto-reconnect is disabled.
|
|
51
|
+
* auto-reconnect is disabled. The {@link code} field classifies the failure —
|
|
52
|
+
* absent vs refused vs unreachable vs lost vs closed — for machine routing.
|
|
29
53
|
*/
|
|
30
54
|
export declare class CerastreamConnectionError extends Error {
|
|
55
|
+
/** Stable machine-readable classification of the connection failure. */
|
|
56
|
+
readonly code: CerastreamConnectErrorCode;
|
|
31
57
|
/** The underlying transport error, when there was one. */
|
|
32
58
|
readonly cause: unknown;
|
|
33
|
-
constructor(message: string, cause?: unknown);
|
|
59
|
+
constructor(message: string, cause?: unknown, code?: CerastreamConnectErrorCode);
|
|
34
60
|
}
|
|
35
61
|
/** Thrown when a request exceeds the configured per-request timeout. */
|
|
36
62
|
export declare class CerastreamTimeoutError extends Error {
|
package/dist/errors.js
CHANGED
|
@@ -31,17 +31,49 @@ export class CerastreamRpcError extends Error {
|
|
|
31
31
|
this.requestId = requestId;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Machine-readable classification of a {@link CerastreamConnectionError}, stable
|
|
36
|
+
* across releases so CeraUI's start-failure taxonomy can branch on it without
|
|
37
|
+
* parsing messages. Distinguishes the three connect-phase failure shapes the
|
|
38
|
+
* engine's systemd lifecycle produces (an absent socket during boot, a stale
|
|
39
|
+
* socket left by a crashed instance, an otherwise-unreachable socket) from a
|
|
40
|
+
* mid-session drop or an intentional client close:
|
|
41
|
+
*
|
|
42
|
+
* - `absent` — the control socket path does not exist (ENOENT): the engine
|
|
43
|
+
* is not running yet, or systemd has torn the old socket down
|
|
44
|
+
* during a restart. Maps to `engine_unavailable`/`engine_restarting`.
|
|
45
|
+
* - `refused` — the socket path exists but the connection was refused
|
|
46
|
+
* (ECONNREFUSED): a stale socket a crashed/`SIGKILL`ed engine
|
|
47
|
+
* left behind, or one mid-`bind` on restart. Maps to
|
|
48
|
+
* `engine_restarting`/`engine_unavailable`.
|
|
49
|
+
* - `unreachable` — any other connect-time transport failure (e.g. EACCES).
|
|
50
|
+
* - `lost` — an established connection dropped (socket closed/errored) or
|
|
51
|
+
* a write failed while a request was in flight.
|
|
52
|
+
* - `closed` — the client was closed, or a request was issued on a
|
|
53
|
+
* not-open connection.
|
|
54
|
+
*/
|
|
55
|
+
export const CERASTREAM_CONNECT_ERROR_CODES = [
|
|
56
|
+
"absent",
|
|
57
|
+
"refused",
|
|
58
|
+
"unreachable",
|
|
59
|
+
"lost",
|
|
60
|
+
"closed",
|
|
61
|
+
];
|
|
34
62
|
/**
|
|
35
63
|
* Thrown when the control connection is lost (socket closed/errored) while a
|
|
36
64
|
* request was in flight, or when {@link connect} cannot reach the engine and
|
|
37
|
-
* auto-reconnect is disabled.
|
|
65
|
+
* auto-reconnect is disabled. The {@link code} field classifies the failure —
|
|
66
|
+
* absent vs refused vs unreachable vs lost vs closed — for machine routing.
|
|
38
67
|
*/
|
|
39
68
|
export class CerastreamConnectionError extends Error {
|
|
69
|
+
/** Stable machine-readable classification of the connection failure. */
|
|
70
|
+
code;
|
|
40
71
|
/** The underlying transport error, when there was one. */
|
|
41
72
|
cause;
|
|
42
|
-
constructor(message, cause) {
|
|
73
|
+
constructor(message, cause, code = "unreachable") {
|
|
43
74
|
super(message);
|
|
44
75
|
this.name = "CerastreamConnectionError";
|
|
76
|
+
this.code = code;
|
|
45
77
|
this.cause = cause;
|
|
46
78
|
}
|
|
47
79
|
}
|
package/dist/events.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export declare const activeEncodeSchema: z.ZodObject<{
|
|
|
13
13
|
active_input: z.ZodOptional<z.ZodString>;
|
|
14
14
|
decoder: z.ZodOptional<z.ZodString>;
|
|
15
15
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
16
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
18
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
16
21
|
}, z.core.$strip>;
|
|
17
22
|
export type ActiveEncode = z.infer<typeof activeEncodeSchema>;
|
|
18
23
|
/**
|
|
@@ -48,6 +53,11 @@ export declare const statusEventSchema: z.ZodObject<{
|
|
|
48
53
|
active_input: z.ZodOptional<z.ZodString>;
|
|
49
54
|
decoder: z.ZodOptional<z.ZodString>;
|
|
50
55
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
56
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
57
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
58
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
51
61
|
}, z.core.$strip>>;
|
|
52
62
|
}, z.core.$strip>;
|
|
53
63
|
/** Payload of a {@link statusEventSchema} event. */
|
|
@@ -106,6 +116,14 @@ export declare const deviceEventSchema: z.ZodObject<{
|
|
|
106
116
|
network: "network";
|
|
107
117
|
}>>;
|
|
108
118
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
119
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
120
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
121
|
+
hdmi: "hdmi";
|
|
122
|
+
usb: "usb";
|
|
123
|
+
bluetooth: "bluetooth";
|
|
124
|
+
onboard: "onboard";
|
|
125
|
+
}>>;
|
|
126
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
109
127
|
}, z.core.$strip>;
|
|
110
128
|
}, z.core.$strip>;
|
|
111
129
|
/** Payload of a {@link deviceEventSchema} event. */
|
|
@@ -162,6 +180,40 @@ export declare const previewEventSchema: z.ZodObject<{
|
|
|
162
180
|
}, z.core.$strip>;
|
|
163
181
|
/** Payload of a {@link previewEventSchema} event. */
|
|
164
182
|
export type PreviewEvent = z.infer<typeof previewEventSchema>;
|
|
183
|
+
/**
|
|
184
|
+
* `audio-level` event — the always-on audio level meter (ADR-0007, additive Todo
|
|
185
|
+
* 21). Either a real per-channel level from the current owner (the idle
|
|
186
|
+
* `sidecar` or the `streaming` audio leg), or `unavailable: true` + `reason` for
|
|
187
|
+
* a handoff gap or a degenerate `audio.mode` — never a fabricated level. A level
|
|
188
|
+
* carries `source`/`channels`/`rms_db`/`peak_db`/`floor_db`; an unavailable event
|
|
189
|
+
* carries `unavailable`/`reason` and omits the level fields. `floor_db` (`-1e6`)
|
|
190
|
+
* is the sentinel a consumer maps back to "silence" (JSON has no `-Infinity`).
|
|
191
|
+
* Independent of the preview-scoped `audio-level` WebSocket frame.
|
|
192
|
+
*/
|
|
193
|
+
export declare const audioLevelEventSchema: z.ZodObject<{
|
|
194
|
+
type: z.ZodLiteral<"audio-level">;
|
|
195
|
+
seq: z.ZodNumber;
|
|
196
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
197
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
198
|
+
owner: z.ZodEnum<{
|
|
199
|
+
streaming: "streaming";
|
|
200
|
+
sidecar: "sidecar";
|
|
201
|
+
}>;
|
|
202
|
+
}, z.core.$strip>>;
|
|
203
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
204
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
205
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
206
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
207
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
208
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
209
|
+
device_busy: "device_busy";
|
|
210
|
+
no_device: "no_device";
|
|
211
|
+
mode_none: "mode_none";
|
|
212
|
+
handoff: "handoff";
|
|
213
|
+
}>>;
|
|
214
|
+
}, z.core.$strip>;
|
|
215
|
+
/** Payload of a {@link audioLevelEventSchema} event. */
|
|
216
|
+
export type AudioLevelEvent = z.infer<typeof audioLevelEventSchema>;
|
|
165
217
|
/** Discriminated union of every v1 event payload (the inner `params`). */
|
|
166
218
|
export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
167
219
|
type: z.ZodLiteral<"status">;
|
|
@@ -186,6 +238,11 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
186
238
|
active_input: z.ZodOptional<z.ZodString>;
|
|
187
239
|
decoder: z.ZodOptional<z.ZodString>;
|
|
188
240
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
241
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
242
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
243
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
244
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
245
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
189
246
|
}, z.core.$strip>>;
|
|
190
247
|
}, z.core.$strip>, z.ZodObject<{
|
|
191
248
|
type: z.ZodLiteral<"switch">;
|
|
@@ -232,6 +289,14 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
232
289
|
network: "network";
|
|
233
290
|
}>>;
|
|
234
291
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
292
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
293
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
294
|
+
hdmi: "hdmi";
|
|
295
|
+
usb: "usb";
|
|
296
|
+
bluetooth: "bluetooth";
|
|
297
|
+
onboard: "onboard";
|
|
298
|
+
}>>;
|
|
299
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
235
300
|
}, z.core.$strip>;
|
|
236
301
|
}, z.core.$strip>, z.ZodObject<{
|
|
237
302
|
type: z.ZodLiteral<"bitrate">;
|
|
@@ -266,6 +331,27 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
266
331
|
seq: z.ZodNumber;
|
|
267
332
|
session_id: z.ZodString;
|
|
268
333
|
phase: z.ZodString;
|
|
334
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
335
|
+
type: z.ZodLiteral<"audio-level">;
|
|
336
|
+
seq: z.ZodNumber;
|
|
337
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
338
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
339
|
+
owner: z.ZodEnum<{
|
|
340
|
+
streaming: "streaming";
|
|
341
|
+
sidecar: "sidecar";
|
|
342
|
+
}>;
|
|
343
|
+
}, z.core.$strip>>;
|
|
344
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
345
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
346
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
347
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
348
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
349
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
350
|
+
device_busy: "device_busy";
|
|
351
|
+
no_device: "no_device";
|
|
352
|
+
mode_none: "mode_none";
|
|
353
|
+
handoff: "handoff";
|
|
354
|
+
}>>;
|
|
269
355
|
}, z.core.$strip>], "type">;
|
|
270
356
|
export type EventParams = z.infer<typeof eventParamsSchema>;
|
|
271
357
|
/** Full event envelope: { jsonrpc, method:"event", params } with typed params. */
|
|
@@ -295,6 +381,11 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
295
381
|
active_input: z.ZodOptional<z.ZodString>;
|
|
296
382
|
decoder: z.ZodOptional<z.ZodString>;
|
|
297
383
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
384
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
385
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
386
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
387
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
388
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
298
389
|
}, z.core.$strip>>;
|
|
299
390
|
}, z.core.$strip>, z.ZodObject<{
|
|
300
391
|
type: z.ZodLiteral<"switch">;
|
|
@@ -341,6 +432,14 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
341
432
|
network: "network";
|
|
342
433
|
}>>;
|
|
343
434
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
435
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
436
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
437
|
+
hdmi: "hdmi";
|
|
438
|
+
usb: "usb";
|
|
439
|
+
bluetooth: "bluetooth";
|
|
440
|
+
onboard: "onboard";
|
|
441
|
+
}>>;
|
|
442
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
344
443
|
}, z.core.$strip>;
|
|
345
444
|
}, z.core.$strip>, z.ZodObject<{
|
|
346
445
|
type: z.ZodLiteral<"bitrate">;
|
|
@@ -375,11 +474,33 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
375
474
|
seq: z.ZodNumber;
|
|
376
475
|
session_id: z.ZodString;
|
|
377
476
|
phase: z.ZodString;
|
|
477
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
478
|
+
type: z.ZodLiteral<"audio-level">;
|
|
479
|
+
seq: z.ZodNumber;
|
|
480
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
481
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
482
|
+
owner: z.ZodEnum<{
|
|
483
|
+
streaming: "streaming";
|
|
484
|
+
sidecar: "sidecar";
|
|
485
|
+
}>;
|
|
486
|
+
}, z.core.$strip>>;
|
|
487
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
488
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
489
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
490
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
491
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
492
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
493
|
+
device_busy: "device_busy";
|
|
494
|
+
no_device: "no_device";
|
|
495
|
+
mode_none: "mode_none";
|
|
496
|
+
handoff: "handoff";
|
|
497
|
+
}>>;
|
|
378
498
|
}, z.core.$strip>], "type">;
|
|
379
499
|
}, z.core.$strip>;
|
|
380
500
|
export type CerastreamEvent = z.infer<typeof cerastreamEventSchema>;
|
|
381
|
-
/** The
|
|
382
|
-
|
|
501
|
+
/** The eight event topics (schema.md "Events" table) — count-assertion source.
|
|
502
|
+
* `audio-level` is additive (Todo 21) and appended last, preserving order. */
|
|
503
|
+
export declare const EVENT_TOPICS: readonly ["status", "switch", "device", "bitrate", "srt-stats", "error", "preview", "audio-level"];
|
|
383
504
|
export type EventTopicName = (typeof EVENT_TOPICS)[number];
|
|
384
505
|
/** topic → payload Zod schema. Tests assert every topic is represented here. */
|
|
385
506
|
export declare const eventSchemas: {
|
|
@@ -406,6 +527,11 @@ export declare const eventSchemas: {
|
|
|
406
527
|
active_input: z.ZodOptional<z.ZodString>;
|
|
407
528
|
decoder: z.ZodOptional<z.ZodString>;
|
|
408
529
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
530
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
531
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
532
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
533
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
534
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
409
535
|
}, z.core.$strip>>;
|
|
410
536
|
}, z.core.$strip>;
|
|
411
537
|
readonly switch: z.ZodObject<{
|
|
@@ -454,6 +580,14 @@ export declare const eventSchemas: {
|
|
|
454
580
|
network: "network";
|
|
455
581
|
}>>;
|
|
456
582
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
583
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
584
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
585
|
+
hdmi: "hdmi";
|
|
586
|
+
usb: "usb";
|
|
587
|
+
bluetooth: "bluetooth";
|
|
588
|
+
onboard: "onboard";
|
|
589
|
+
}>>;
|
|
590
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
457
591
|
}, z.core.$strip>;
|
|
458
592
|
}, z.core.$strip>;
|
|
459
593
|
readonly bitrate: z.ZodObject<{
|
|
@@ -493,4 +627,26 @@ export declare const eventSchemas: {
|
|
|
493
627
|
session_id: z.ZodString;
|
|
494
628
|
phase: z.ZodString;
|
|
495
629
|
}, z.core.$strip>;
|
|
630
|
+
readonly "audio-level": z.ZodObject<{
|
|
631
|
+
type: z.ZodLiteral<"audio-level">;
|
|
632
|
+
seq: z.ZodNumber;
|
|
633
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
634
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
635
|
+
owner: z.ZodEnum<{
|
|
636
|
+
streaming: "streaming";
|
|
637
|
+
sidecar: "sidecar";
|
|
638
|
+
}>;
|
|
639
|
+
}, z.core.$strip>>;
|
|
640
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
641
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
642
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
643
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
644
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
645
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
646
|
+
device_busy: "device_busy";
|
|
647
|
+
no_device: "no_device";
|
|
648
|
+
mode_none: "mode_none";
|
|
649
|
+
handoff: "handoff";
|
|
650
|
+
}>>;
|
|
651
|
+
}, z.core.$strip>;
|
|
496
652
|
};
|
package/dist/events.js
CHANGED
|
@@ -19,6 +19,11 @@ export const activeEncodeSchema = z.object({
|
|
|
19
19
|
active_input: z.string().optional(), // active input id feeding the encode
|
|
20
20
|
decoder: z.string().optional(), // runtime-selected decode element when transcoding
|
|
21
21
|
input_codec: z.string().optional(), // incoming pre-decode codec token: "h264"/"h265"/"mjpeg"
|
|
22
|
+
passthrough: z.boolean().optional(), // 0.5.0: true when a same-codec passthrough graph is live (no decode/re-encode)
|
|
23
|
+
gop_probe_outcome: z.string().optional(), // 0.5.0: GOP preflight outcome: "eligible"|"ineligible"|"unknown"|"skipped"
|
|
24
|
+
gop_probe_ms: z.number().int().nonnegative().optional(), // 0.5.0: GOP preflight duration in ms when a probe ran
|
|
25
|
+
frames_emitted: z.number().int().nonnegative().optional(), // 0.7.0: monotonic egress-buffer counter; advances across status heartbeats while frames flow
|
|
26
|
+
pipeline_playing: z.boolean().optional(), // 0.7.0: pipeline is in GStreamer PLAYING (false during an in-process reconnect)
|
|
22
27
|
});
|
|
23
28
|
/**
|
|
24
29
|
* `status` event — stream state change + heartbeat. The trailing fields are
|
|
@@ -96,6 +101,32 @@ export const previewEventSchema = z.object({
|
|
|
96
101
|
session_id: z.string(),
|
|
97
102
|
phase: z.string(),
|
|
98
103
|
});
|
|
104
|
+
/**
|
|
105
|
+
* `audio-level` event — the always-on audio level meter (ADR-0007, additive Todo
|
|
106
|
+
* 21). Either a real per-channel level from the current owner (the idle
|
|
107
|
+
* `sidecar` or the `streaming` audio leg), or `unavailable: true` + `reason` for
|
|
108
|
+
* a handoff gap or a degenerate `audio.mode` — never a fabricated level. A level
|
|
109
|
+
* carries `source`/`channels`/`rms_db`/`peak_db`/`floor_db`; an unavailable event
|
|
110
|
+
* carries `unavailable`/`reason` and omits the level fields. `floor_db` (`-1e6`)
|
|
111
|
+
* is the sentinel a consumer maps back to "silence" (JSON has no `-Infinity`).
|
|
112
|
+
* Independent of the preview-scoped `audio-level` WebSocket frame.
|
|
113
|
+
*/
|
|
114
|
+
export const audioLevelEventSchema = z.object({
|
|
115
|
+
type: z.literal("audio-level"),
|
|
116
|
+
seq,
|
|
117
|
+
source: z
|
|
118
|
+
.object({
|
|
119
|
+
identity: z.string().optional(), // reboot-stable device id (Todo 20 stable_id)
|
|
120
|
+
owner: z.enum(["sidecar", "streaming"]),
|
|
121
|
+
})
|
|
122
|
+
.optional(),
|
|
123
|
+
channels: z.number().int().nonnegative().optional(),
|
|
124
|
+
rms_db: z.array(z.number()).optional(),
|
|
125
|
+
peak_db: z.array(z.number()).optional(),
|
|
126
|
+
floor_db: z.number().optional(),
|
|
127
|
+
unavailable: z.literal(true).optional(),
|
|
128
|
+
reason: z.enum(["device_busy", "no_device", "mode_none", "handoff"]).optional(),
|
|
129
|
+
});
|
|
99
130
|
/** Discriminated union of every v1 event payload (the inner `params`). */
|
|
100
131
|
export const eventParamsSchema = z.discriminatedUnion("type", [
|
|
101
132
|
statusEventSchema,
|
|
@@ -105,6 +136,7 @@ export const eventParamsSchema = z.discriminatedUnion("type", [
|
|
|
105
136
|
srtStatsEventSchema,
|
|
106
137
|
runtimeErrorEventSchema,
|
|
107
138
|
previewEventSchema,
|
|
139
|
+
audioLevelEventSchema,
|
|
108
140
|
]);
|
|
109
141
|
/** Full event envelope: { jsonrpc, method:"event", params } with typed params. */
|
|
110
142
|
export const cerastreamEventSchema = z.object({
|
|
@@ -112,7 +144,8 @@ export const cerastreamEventSchema = z.object({
|
|
|
112
144
|
method: z.literal("event"),
|
|
113
145
|
params: eventParamsSchema,
|
|
114
146
|
});
|
|
115
|
-
/** The
|
|
147
|
+
/** The eight event topics (schema.md "Events" table) — count-assertion source.
|
|
148
|
+
* `audio-level` is additive (Todo 21) and appended last, preserving order. */
|
|
116
149
|
export const EVENT_TOPICS = [
|
|
117
150
|
"status",
|
|
118
151
|
"switch",
|
|
@@ -121,6 +154,7 @@ export const EVENT_TOPICS = [
|
|
|
121
154
|
"srt-stats",
|
|
122
155
|
"error",
|
|
123
156
|
"preview",
|
|
157
|
+
"audio-level",
|
|
124
158
|
];
|
|
125
159
|
/** topic → payload Zod schema. Tests assert every topic is represented here. */
|
|
126
160
|
export const eventSchemas = {
|
|
@@ -131,4 +165,5 @@ export const eventSchemas = {
|
|
|
131
165
|
"srt-stats": srtStatsEventSchema,
|
|
132
166
|
error: runtimeErrorEventSchema,
|
|
133
167
|
preview: previewEventSchema,
|
|
168
|
+
"audio-level": audioLevelEventSchema,
|
|
134
169
|
};
|
package/dist/messages.d.ts
CHANGED
|
@@ -28,10 +28,20 @@ export declare const startParamsSchema: z.ZodObject<{
|
|
|
28
28
|
resolution: z.ZodOptional<z.ZodString>;
|
|
29
29
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
30
30
|
audio: z.ZodOptional<z.ZodObject<{
|
|
31
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
32
|
+
default: "default";
|
|
33
|
+
none: "none";
|
|
34
|
+
device: "device";
|
|
35
|
+
}>>;
|
|
31
36
|
device: z.ZodOptional<z.ZodString>;
|
|
32
37
|
codec: z.ZodOptional<z.ZodString>;
|
|
33
38
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
34
39
|
}, z.core.$strip>>;
|
|
40
|
+
video_passthrough: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
auto: "auto";
|
|
42
|
+
force: "force";
|
|
43
|
+
off: "off";
|
|
44
|
+
}>>;
|
|
35
45
|
}, z.core.$strip>;
|
|
36
46
|
export type StartParams = z.infer<typeof startParamsSchema>;
|
|
37
47
|
export declare const startResultSchema: z.ZodObject<{
|
|
@@ -95,6 +105,11 @@ export declare const reloadConfigResultSchema: z.ZodObject<{
|
|
|
95
105
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
96
106
|
}, z.core.$strip>>;
|
|
97
107
|
}, z.core.$strip>;
|
|
108
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
109
|
+
encoder: "encoder";
|
|
110
|
+
"source-fixed": "source-fixed";
|
|
111
|
+
}>>;
|
|
112
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
98
113
|
}, z.core.$strip>;
|
|
99
114
|
export type ReloadConfigResult = z.infer<typeof reloadConfigResultSchema>;
|
|
100
115
|
export declare const setBitrateParamsSchema: z.ZodObject<{
|
|
@@ -105,6 +120,11 @@ export declare const setBitrateResultSchema: z.ZodObject<{
|
|
|
105
120
|
applied: z.ZodObject<{
|
|
106
121
|
max_bitrate: z.ZodNumber;
|
|
107
122
|
}, z.core.$strip>;
|
|
123
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
124
|
+
encoder: "encoder";
|
|
125
|
+
"source-fixed": "source-fixed";
|
|
126
|
+
}>>;
|
|
127
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
108
128
|
}, z.core.$strip>;
|
|
109
129
|
export type SetBitrateResult = z.infer<typeof setBitrateResultSchema>;
|
|
110
130
|
export declare const switchInputParamsSchema: z.ZodObject<{
|
|
@@ -172,6 +192,14 @@ export declare const listDevicesResultSchema: z.ZodObject<{
|
|
|
172
192
|
network: "network";
|
|
173
193
|
}>>;
|
|
174
194
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
195
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
196
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
197
|
+
hdmi: "hdmi";
|
|
198
|
+
usb: "usb";
|
|
199
|
+
bluetooth: "bluetooth";
|
|
200
|
+
onboard: "onboard";
|
|
201
|
+
}>>;
|
|
202
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
175
203
|
}, z.core.$strip>>;
|
|
176
204
|
}, z.core.$strip>;
|
|
177
205
|
export type ListDevicesResult = z.infer<typeof listDevicesResultSchema>;
|
|
@@ -183,6 +211,7 @@ export declare const eventTopicSchema: z.ZodEnum<{
|
|
|
183
211
|
switch: "switch";
|
|
184
212
|
"srt-stats": "srt-stats";
|
|
185
213
|
preview: "preview";
|
|
214
|
+
"audio-level": "audio-level";
|
|
186
215
|
}>;
|
|
187
216
|
export type EventTopic = z.infer<typeof eventTopicSchema>;
|
|
188
217
|
export declare const subscribeEventsParamsSchema: z.ZodObject<{
|
|
@@ -194,6 +223,7 @@ export declare const subscribeEventsParamsSchema: z.ZodObject<{
|
|
|
194
223
|
switch: "switch";
|
|
195
224
|
"srt-stats": "srt-stats";
|
|
196
225
|
preview: "preview";
|
|
226
|
+
"audio-level": "audio-level";
|
|
197
227
|
}>>>;
|
|
198
228
|
}, z.core.$strip>;
|
|
199
229
|
export type SubscribeEventsParams = z.infer<typeof subscribeEventsParamsSchema>;
|
|
@@ -206,6 +236,7 @@ export declare const subscribeEventsResultSchema: z.ZodObject<{
|
|
|
206
236
|
switch: "switch";
|
|
207
237
|
"srt-stats": "srt-stats";
|
|
208
238
|
preview: "preview";
|
|
239
|
+
"audio-level": "audio-level";
|
|
209
240
|
}>>;
|
|
210
241
|
}, z.core.$strip>;
|
|
211
242
|
export type SubscribeEventsResult = z.infer<typeof subscribeEventsResultSchema>;
|
|
@@ -274,6 +305,16 @@ export declare const platformCapsSchema: z.ZodObject<{
|
|
|
274
305
|
supports_h265: z.ZodBoolean;
|
|
275
306
|
hardware_accelerated: z.ZodBoolean;
|
|
276
307
|
max_resolution: z.ZodString;
|
|
308
|
+
hardware_kind: z.ZodOptional<z.ZodEnum<{
|
|
309
|
+
rk3588: "rk3588";
|
|
310
|
+
jetson: "jetson";
|
|
311
|
+
n100: "n100";
|
|
312
|
+
generic: "generic";
|
|
313
|
+
}>>;
|
|
314
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
315
|
+
detected: "detected";
|
|
316
|
+
override: "override";
|
|
317
|
+
}>>;
|
|
277
318
|
}, z.core.$strip>;
|
|
278
319
|
export type PlatformCaps = z.infer<typeof platformCapsSchema>;
|
|
279
320
|
export declare const previewAvailabilitySchema: z.ZodObject<{
|
|
@@ -287,6 +328,16 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
|
|
|
287
328
|
supports_h265: z.ZodBoolean;
|
|
288
329
|
hardware_accelerated: z.ZodBoolean;
|
|
289
330
|
max_resolution: z.ZodString;
|
|
331
|
+
hardware_kind: z.ZodOptional<z.ZodEnum<{
|
|
332
|
+
rk3588: "rk3588";
|
|
333
|
+
jetson: "jetson";
|
|
334
|
+
n100: "n100";
|
|
335
|
+
generic: "generic";
|
|
336
|
+
}>>;
|
|
337
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
338
|
+
detected: "detected";
|
|
339
|
+
override: "override";
|
|
340
|
+
}>>;
|
|
290
341
|
}, z.core.$strip>;
|
|
291
342
|
encoder: z.ZodObject<{
|
|
292
343
|
codecs: z.ZodArray<z.ZodString>;
|
|
@@ -319,6 +370,7 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
|
|
|
319
370
|
bound: z.ZodBoolean;
|
|
320
371
|
}, z.core.$strip>>;
|
|
321
372
|
network_embedded_audio: z.ZodOptional<z.ZodBoolean>;
|
|
373
|
+
features: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
322
374
|
}, z.core.$strip>;
|
|
323
375
|
export type GetCapabilitiesResult = z.infer<typeof getCapabilitiesResultSchema>;
|
|
324
376
|
/** The eight v1 control methods (the literal JSON-RPC `method` strings). */
|
|
@@ -371,10 +423,20 @@ export declare const requestSchemas: {
|
|
|
371
423
|
resolution: z.ZodOptional<z.ZodString>;
|
|
372
424
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
373
425
|
audio: z.ZodOptional<z.ZodObject<{
|
|
426
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
427
|
+
default: "default";
|
|
428
|
+
none: "none";
|
|
429
|
+
device: "device";
|
|
430
|
+
}>>;
|
|
374
431
|
device: z.ZodOptional<z.ZodString>;
|
|
375
432
|
codec: z.ZodOptional<z.ZodString>;
|
|
376
433
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
377
434
|
}, z.core.$strip>>;
|
|
435
|
+
video_passthrough: z.ZodOptional<z.ZodEnum<{
|
|
436
|
+
auto: "auto";
|
|
437
|
+
force: "force";
|
|
438
|
+
off: "off";
|
|
439
|
+
}>>;
|
|
378
440
|
}, z.core.$strip>;
|
|
379
441
|
readonly result: z.ZodObject<{
|
|
380
442
|
session_id: z.ZodString;
|
|
@@ -437,6 +499,11 @@ export declare const requestSchemas: {
|
|
|
437
499
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
438
500
|
}, z.core.$strip>>;
|
|
439
501
|
}, z.core.$strip>;
|
|
502
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
503
|
+
encoder: "encoder";
|
|
504
|
+
"source-fixed": "source-fixed";
|
|
505
|
+
}>>;
|
|
506
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
440
507
|
}, z.core.$strip>;
|
|
441
508
|
};
|
|
442
509
|
readonly "set-bitrate": {
|
|
@@ -447,6 +514,11 @@ export declare const requestSchemas: {
|
|
|
447
514
|
applied: z.ZodObject<{
|
|
448
515
|
max_bitrate: z.ZodNumber;
|
|
449
516
|
}, z.core.$strip>;
|
|
517
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
518
|
+
encoder: "encoder";
|
|
519
|
+
"source-fixed": "source-fixed";
|
|
520
|
+
}>>;
|
|
521
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
450
522
|
}, z.core.$strip>;
|
|
451
523
|
};
|
|
452
524
|
readonly "switch-input": {
|
|
@@ -498,6 +570,14 @@ export declare const requestSchemas: {
|
|
|
498
570
|
network: "network";
|
|
499
571
|
}>>;
|
|
500
572
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
573
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
574
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
575
|
+
hdmi: "hdmi";
|
|
576
|
+
usb: "usb";
|
|
577
|
+
bluetooth: "bluetooth";
|
|
578
|
+
onboard: "onboard";
|
|
579
|
+
}>>;
|
|
580
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
501
581
|
}, z.core.$strip>>;
|
|
502
582
|
}, z.core.$strip>;
|
|
503
583
|
};
|
|
@@ -511,6 +591,7 @@ export declare const requestSchemas: {
|
|
|
511
591
|
switch: "switch";
|
|
512
592
|
"srt-stats": "srt-stats";
|
|
513
593
|
preview: "preview";
|
|
594
|
+
"audio-level": "audio-level";
|
|
514
595
|
}>>>;
|
|
515
596
|
}, z.core.$strip>;
|
|
516
597
|
readonly result: z.ZodObject<{
|
|
@@ -522,6 +603,7 @@ export declare const requestSchemas: {
|
|
|
522
603
|
switch: "switch";
|
|
523
604
|
"srt-stats": "srt-stats";
|
|
524
605
|
preview: "preview";
|
|
606
|
+
"audio-level": "audio-level";
|
|
525
607
|
}>>;
|
|
526
608
|
}, z.core.$strip>;
|
|
527
609
|
};
|
package/dist/messages.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AUDIO_DELAY_MAX_MS } from "./constants.js";
|
|
3
3
|
import { helloParamsSchema, helloResultSchema, } from "./envelope.js";
|
|
4
|
-
import { balancerAlgorithmSchema, captureDeviceSchema, cerastreamConfigSchema, inputModeSchema, mediaClassSchema, previewTierSchema, streamStateSchema, } from "./types.js";
|
|
4
|
+
import { balancerAlgorithmSchema, bitrateControlSchema, captureDeviceSchema, cerastreamConfigSchema, inputModeSchema, mediaClassSchema, previewTierSchema, streamStateSchema, } from "./types.js";
|
|
5
5
|
// The eight v1 control methods (schema.md "v1 messages"). Each is matched
|
|
6
6
|
// byte-for-byte to schema.md: the JSON shape on the wire is the contract.
|
|
7
7
|
// ---- 1. start ----
|
|
@@ -44,6 +44,10 @@ export const reloadConfigParamsSchema = z.object({
|
|
|
44
44
|
});
|
|
45
45
|
export const reloadConfigResultSchema = z.object({
|
|
46
46
|
applied: reloadConfigParamsSchema, // post-clamp values actually applied
|
|
47
|
+
// additive (0.5.0): a bitrate-bearing reload during passthrough is NOT applied —
|
|
48
|
+
// "source-fixed" + reason "passthrough" report that. Absent on a transcode reload.
|
|
49
|
+
bitrate_control: bitrateControlSchema.optional(),
|
|
50
|
+
reason: z.string().optional(),
|
|
47
51
|
});
|
|
48
52
|
// ---- 4. set-bitrate ----
|
|
49
53
|
export const setBitrateParamsSchema = z.object({
|
|
@@ -51,6 +55,10 @@ export const setBitrateParamsSchema = z.object({
|
|
|
51
55
|
});
|
|
52
56
|
export const setBitrateResultSchema = z.object({
|
|
53
57
|
applied: z.object({ max_bitrate: z.number().int() }),
|
|
58
|
+
// additive (0.5.0). On passthrough there is no encoder to drive, so a request is
|
|
59
|
+
// NOT applied: "source-fixed" with reason "passthrough". Absent/"encoder" ⇒ applied.
|
|
60
|
+
bitrate_control: bitrateControlSchema.optional(),
|
|
61
|
+
reason: z.string().optional(),
|
|
54
62
|
});
|
|
55
63
|
// ---- 5. switch-input ----
|
|
56
64
|
export const switchInputParamsSchema = z.object({
|
|
@@ -91,6 +99,7 @@ export const eventTopicSchema = z.enum([
|
|
|
91
99
|
"srt-stats",
|
|
92
100
|
"error",
|
|
93
101
|
"preview",
|
|
102
|
+
"audio-level",
|
|
94
103
|
]);
|
|
95
104
|
export const subscribeEventsParamsSchema = z.object({
|
|
96
105
|
topics: z.array(eventTopicSchema).optional(), // default: all topics
|
|
@@ -154,6 +163,10 @@ export const platformCapsSchema = z.object({
|
|
|
154
163
|
supports_h265: z.boolean(),
|
|
155
164
|
hardware_accelerated: z.boolean(),
|
|
156
165
|
max_resolution: z.string(),
|
|
166
|
+
// additive: the engine's resolved hardware kind and how it was resolved.
|
|
167
|
+
// Optional so a pre-field engine (which omits both) still parses.
|
|
168
|
+
hardware_kind: z.enum(["rk3588", "jetson", "n100", "generic"]).optional(),
|
|
169
|
+
source: z.enum(["detected", "override"]).optional(),
|
|
157
170
|
});
|
|
158
171
|
// Preview-server availability (0.4.0, additive). Lets the UI tell an unbound /
|
|
159
172
|
// port-conflicted preview (enabled:true, bound:false) from a down engine.
|
|
@@ -173,6 +186,7 @@ export const getCapabilitiesResultSchema = z.object({
|
|
|
173
186
|
profile_catalog_version: z.string().optional(), // semver of the supported_profiles catalog
|
|
174
187
|
preview: previewAvailabilitySchema.optional(), // preview-server availability (unbound vs down)
|
|
175
188
|
network_embedded_audio: z.boolean().optional(), // engine routes network-ingest embedded audio to the mux
|
|
189
|
+
features: z.array(z.string()).optional(), // named engine features (e.g. "video-passthrough") for fail-closed negotiation
|
|
176
190
|
});
|
|
177
191
|
// ---- method registry (count-assertion source of truth) ----
|
|
178
192
|
/** The eight v1 control methods (the literal JSON-RPC `method` strings). */
|
package/dist/types.d.ts
CHANGED
|
@@ -32,6 +32,17 @@ export declare const videoCodecSchema: z.ZodEnum<{
|
|
|
32
32
|
h265: "h265";
|
|
33
33
|
}>;
|
|
34
34
|
export type VideoCodec = z.infer<typeof videoCodecSchema>;
|
|
35
|
+
export declare const videoPassthroughSchema: z.ZodEnum<{
|
|
36
|
+
auto: "auto";
|
|
37
|
+
force: "force";
|
|
38
|
+
off: "off";
|
|
39
|
+
}>;
|
|
40
|
+
export type VideoPassthrough = z.infer<typeof videoPassthroughSchema>;
|
|
41
|
+
export declare const bitrateControlSchema: z.ZodEnum<{
|
|
42
|
+
encoder: "encoder";
|
|
43
|
+
"source-fixed": "source-fixed";
|
|
44
|
+
}>;
|
|
45
|
+
export type BitrateControl = z.infer<typeof bitrateControlSchema>;
|
|
35
46
|
export declare const captureDeviceKindSchema: z.ZodEnum<{
|
|
36
47
|
audio: "audio";
|
|
37
48
|
hdmi: "hdmi";
|
|
@@ -43,6 +54,13 @@ export declare const captureDeviceKindSchema: z.ZodEnum<{
|
|
|
43
54
|
network: "network";
|
|
44
55
|
}>;
|
|
45
56
|
export type CaptureDeviceKind = z.infer<typeof captureDeviceKindSchema>;
|
|
57
|
+
export declare const deviceTransportSchema: z.ZodEnum<{
|
|
58
|
+
hdmi: "hdmi";
|
|
59
|
+
usb: "usb";
|
|
60
|
+
bluetooth: "bluetooth";
|
|
61
|
+
onboard: "onboard";
|
|
62
|
+
}>;
|
|
63
|
+
export type DeviceTransport = z.infer<typeof deviceTransportSchema>;
|
|
46
64
|
/** SRT transport config. Mirrors schema.md `start.srt` exactly. */
|
|
47
65
|
export declare const srtConfigSchema: z.ZodObject<{
|
|
48
66
|
host: z.ZodString;
|
|
@@ -73,7 +91,18 @@ export type BitrateConfig = z.infer<typeof bitrateConfigSchema>;
|
|
|
73
91
|
* later, negative = earlier); it is clamped to ±AUDIO_DELAY_MAX_MS when applied,
|
|
74
92
|
* never rejected — so the schema does NOT bound it.
|
|
75
93
|
*/
|
|
94
|
+
export declare const audioModeSchema: z.ZodEnum<{
|
|
95
|
+
default: "default";
|
|
96
|
+
none: "none";
|
|
97
|
+
device: "device";
|
|
98
|
+
}>;
|
|
99
|
+
export type AudioMode = z.infer<typeof audioModeSchema>;
|
|
76
100
|
export declare const audioConfigSchema: z.ZodObject<{
|
|
101
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
102
|
+
default: "default";
|
|
103
|
+
none: "none";
|
|
104
|
+
device: "device";
|
|
105
|
+
}>>;
|
|
77
106
|
device: z.ZodOptional<z.ZodString>;
|
|
78
107
|
codec: z.ZodOptional<z.ZodString>;
|
|
79
108
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
@@ -114,10 +143,20 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
|
|
|
114
143
|
resolution: z.ZodOptional<z.ZodString>;
|
|
115
144
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
116
145
|
audio: z.ZodOptional<z.ZodObject<{
|
|
146
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
147
|
+
default: "default";
|
|
148
|
+
none: "none";
|
|
149
|
+
device: "device";
|
|
150
|
+
}>>;
|
|
117
151
|
device: z.ZodOptional<z.ZodString>;
|
|
118
152
|
codec: z.ZodOptional<z.ZodString>;
|
|
119
153
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
120
154
|
}, z.core.$strip>>;
|
|
155
|
+
video_passthrough: z.ZodOptional<z.ZodEnum<{
|
|
156
|
+
auto: "auto";
|
|
157
|
+
force: "force";
|
|
158
|
+
off: "off";
|
|
159
|
+
}>>;
|
|
121
160
|
}, z.core.$strip>;
|
|
122
161
|
export type CerastreamConfig = z.infer<typeof cerastreamConfigSchema>;
|
|
123
162
|
export type PartialCerastreamConfig = z.input<typeof cerastreamConfigSchema>;
|
|
@@ -159,6 +198,14 @@ export declare const captureDeviceSchema: z.ZodObject<{
|
|
|
159
198
|
network: "network";
|
|
160
199
|
}>>;
|
|
161
200
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
201
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
202
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
203
|
+
hdmi: "hdmi";
|
|
204
|
+
usb: "usb";
|
|
205
|
+
bluetooth: "bluetooth";
|
|
206
|
+
onboard: "onboard";
|
|
207
|
+
}>>;
|
|
208
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
162
209
|
}, z.core.$strip>;
|
|
163
210
|
export type CaptureDevice = z.infer<typeof captureDeviceSchema>;
|
|
164
211
|
export declare const srtStatsSchema: z.ZodObject<{
|
package/dist/types.js
CHANGED
|
@@ -18,6 +18,16 @@ export const previewTierSchema = z.enum(["webcodecs", "webrtc"]);
|
|
|
18
18
|
// platform default. Wire values are the codec id strings the encoder caps also
|
|
19
19
|
// advertise ("h264"/"h265") — never an encoder element name.
|
|
20
20
|
export const videoCodecSchema = z.enum(["h264", "h265"]);
|
|
21
|
+
// Same-codec passthrough policy (0.5.0, additive). Absent ⇒ "auto". "auto" =
|
|
22
|
+
// passthrough only when adaptive bitrate is inactive and eligibility holds;
|
|
23
|
+
// "force" = passthrough whenever eligible (typed start failure otherwise);
|
|
24
|
+
// "off" = always transcode. See schema.md and the engine's layer-C matrix.
|
|
25
|
+
export const videoPassthroughSchema = z.enum(["auto", "force", "off"]);
|
|
26
|
+
// Which surface owns the running bitrate, reported additively on a set-bitrate /
|
|
27
|
+
// reload-config response (0.5.0). "encoder" = applied; "source-fixed" = a
|
|
28
|
+
// passthrough graph fixes the bitrate at the source, so the request was NOT
|
|
29
|
+
// applied (paired with reason "passthrough").
|
|
30
|
+
export const bitrateControlSchema = z.enum(["encoder", "source-fixed"]);
|
|
21
31
|
// Engine-typed capture-device family (0.4.0, additive). CeraUI groups a device
|
|
22
32
|
// by the engine's resolved kind (a UVC/USB dongle is uvc_h264/mjpeg, never
|
|
23
33
|
// mislabeled HDMI). Absent ⇒ CeraUI falls back to a bus/name heuristic.
|
|
@@ -31,6 +41,17 @@ export const captureDeviceKindSchema = z.enum([
|
|
|
31
41
|
"test",
|
|
32
42
|
"network",
|
|
33
43
|
]);
|
|
44
|
+
// How a capture device is physically attached (Todo 20, additive). The transport
|
|
45
|
+
// tag CeraUI renders beside the product name (e.g. "RØDE NT-USB · USB"). Distinct
|
|
46
|
+
// from captureDeviceKind: an HDMI-to-USB dongle is kind:uvc_h264 but transport:usb,
|
|
47
|
+
// while the SoC's dedicated HDMI-RX port is transport:hdmi. Absent on a legacy
|
|
48
|
+
// producer or when no hint identifies a transport.
|
|
49
|
+
export const deviceTransportSchema = z.enum([
|
|
50
|
+
"usb",
|
|
51
|
+
"hdmi",
|
|
52
|
+
"bluetooth",
|
|
53
|
+
"onboard",
|
|
54
|
+
]);
|
|
34
55
|
// ---- config sub-schemas (canonical; reused by `start` + the unified config) ----
|
|
35
56
|
/** SRT transport config. Mirrors schema.md `start.srt` exactly. */
|
|
36
57
|
export const srtConfigSchema = z.object({
|
|
@@ -67,7 +88,15 @@ export const bitrateConfigSchema = z
|
|
|
67
88
|
* later, negative = earlier); it is clamped to ±AUDIO_DELAY_MAX_MS when applied,
|
|
68
89
|
* never rejected — so the schema does NOT bound it.
|
|
69
90
|
*/
|
|
91
|
+
// How the program audio is sourced (0.6.0, additive). "none" = no audio branch
|
|
92
|
+
// (video-only TS); "default" = embedded audio for a network source, the selected
|
|
93
|
+
// device or a silent track for a capture source; "device" = the selected ALSA
|
|
94
|
+
// leg (requires `device`). Absent ⇒ legacy inference from `device`/source kind,
|
|
95
|
+
// so a pre-0.6.0 caller keeps working. Replaces leaking pseudo-source strings
|
|
96
|
+
// (e.g. "No audio") into `device`.
|
|
97
|
+
export const audioModeSchema = z.enum(["none", "default", "device"]);
|
|
70
98
|
export const audioConfigSchema = z.object({
|
|
99
|
+
mode: audioModeSchema.optional(),
|
|
71
100
|
device: z.string().optional(), // ALSA capture device id; absent ⇒ test-tone fallback
|
|
72
101
|
codec: z.string().optional(), // audio encoder codec id (e.g. "aac", "opus")
|
|
73
102
|
delay_ms: z.number().int().optional(), // signed A/V-sync delay (clamped at apply)
|
|
@@ -87,6 +116,7 @@ export const cerastreamConfigSchema = z.object({
|
|
|
87
116
|
resolution: z.string().optional(), // additive (0.4.0): "WxH" pixel form (never a UI token)
|
|
88
117
|
framerate: z.number().optional(), // additive (0.4.0): fps as a number, e.g. 29.97
|
|
89
118
|
audio: audioConfigSchema.optional(), // additive (0.4.0): audio device/codec/signed delay
|
|
119
|
+
video_passthrough: videoPassthroughSchema.optional(), // additive (0.5.0): auto|force|off; absent ⇒ auto
|
|
90
120
|
});
|
|
91
121
|
// convenience defaults for building a config client-side
|
|
92
122
|
export const DEFAULT_BITRATE_CONFIG = {
|
|
@@ -110,6 +140,9 @@ export const captureDeviceSchema = z.object({
|
|
|
110
140
|
caps: z.array(captureCapSchema).optional(),
|
|
111
141
|
kind: captureDeviceKindSchema.optional(), // additive (0.4.0): engine-typed device family; absent on legacy producers
|
|
112
142
|
alsa_card_id: z.string().optional(), // additive: ALSA card id for media_class:audio devices only; absent on video + legacy producers
|
|
143
|
+
product_name: z.string().optional(), // additive (Todo 20): real product name, deduped with a #N suffix when shared; absent ⇒ use display_name
|
|
144
|
+
transport: deviceTransportSchema.optional(), // additive (Todo 20): how the device is attached; absent on legacy producers
|
|
145
|
+
stable_id: z.string().optional(), // additive (Todo 20): reboot-stable hardware identity, distinct from input_id/device_path
|
|
113
146
|
});
|
|
114
147
|
// ---- transport telemetry (srt-stats event payload) ----
|
|
115
148
|
export const srtStatsSchema = z.object({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ceralive/cerastream",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.3",
|
|
4
4
|
"description": "Type-safe TypeScript schema + IPC client for the cerastream streaming engine (JSON-RPC 2.0 / NDJSON over a Unix domain socket).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|