@ceralive/cerastream 2026.7.2 → 2026.7.4
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 +3 -1
- package/dist/client.d.ts +15 -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 +223 -2
- package/dist/events.js +56 -2
- package/dist/messages.d.ts +131 -0
- package/dist/messages.js +56 -1
- package/dist/types.d.ts +56 -0
- package/dist/types.js +47 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,14 @@ 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 nine 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
13
|
- Additive `client.getCapabilities()` discovery for platform, source, encoder, and
|
|
14
14
|
local preview availability
|
|
15
|
+
- Additive `client.changeConfig()` — reconfigure the live session (resolution,
|
|
16
|
+
framerate, codec, pipeline, source) as one transaction with typed rollback
|
|
15
17
|
|
|
16
18
|
The Zod schemas, types, and constants are the frozen wire contract; `connect()`
|
|
17
19
|
drives that contract over an NDJSON/UDS transport with no native dependencies.
|
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 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";
|
|
3
|
+
import { type ChangeConfigParams, type ChangeConfigResult, 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. */
|
|
@@ -82,6 +82,20 @@ export interface CerastreamClient {
|
|
|
82
82
|
* availability. This request intentionally sits outside the frozen v1 map.
|
|
83
83
|
*/
|
|
84
84
|
getCapabilities(): Promise<GetCapabilitiesResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Reconfigure the LIVE session as one transaction (resolution, framerate,
|
|
87
|
+
* codec, pipeline, source). Also sits outside the frozen v1 map.
|
|
88
|
+
*
|
|
89
|
+
* Resolves for every phase the transaction reached — including
|
|
90
|
+
* `rollback_failed`, an honest terminal outcome. Callers must branch on
|
|
91
|
+
* `result.phase`, not on whether the promise settled. The engine's declared
|
|
92
|
+
* worst-case transaction bound is 65 000 ms, so size any caller-side deadline
|
|
93
|
+
* from that, not from the default per-request timeout.
|
|
94
|
+
* @param params A delta of the live config; absent field ⇒ keep live value.
|
|
95
|
+
* @throws {CerastreamRpcError} ONLY when the transaction never started (not
|
|
96
|
+
* streaming, a concurrent change, or an empty/invalid delta).
|
|
97
|
+
*/
|
|
98
|
+
changeConfig(params: ChangeConfigParams): Promise<ChangeConfigResult>;
|
|
85
99
|
/**
|
|
86
100
|
* Subscribe to the live event stream; `handler` fires for each pushed event.
|
|
87
101
|
* @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 { getCapabilitiesResultSchema, requestSchemas, } from "./messages.js";
|
|
7
|
+
import { changeConfigParamsSchema, changeConfigResultSchema, 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
|
}
|
|
@@ -81,6 +82,9 @@ class ClientImpl {
|
|
|
81
82
|
async getCapabilities() {
|
|
82
83
|
return getCapabilitiesResultSchema.parse(await this.rawRequest("get-capabilities", undefined));
|
|
83
84
|
}
|
|
85
|
+
async changeConfig(params) {
|
|
86
|
+
return changeConfigResultSchema.parse(await this.rawRequest("change-config", changeConfigParamsSchema.parse(params)));
|
|
87
|
+
}
|
|
84
88
|
previewSession(params) {
|
|
85
89
|
return this.call("preview-session", params);
|
|
86
90
|
}
|
|
@@ -101,7 +105,7 @@ class ClientImpl {
|
|
|
101
105
|
this.intentionalClose = true;
|
|
102
106
|
for (const sub of this.subscriptions)
|
|
103
107
|
sub.close();
|
|
104
|
-
const conn = new CerastreamConnectionError("client closed");
|
|
108
|
+
const conn = new CerastreamConnectionError("client closed", undefined, "closed");
|
|
105
109
|
this.rejectAllPending(conn);
|
|
106
110
|
this.socket?.close();
|
|
107
111
|
this.socket = undefined;
|
|
@@ -117,7 +121,7 @@ class ClientImpl {
|
|
|
117
121
|
rawRequest(method, params) {
|
|
118
122
|
const socket = this.socket;
|
|
119
123
|
if (!socket) {
|
|
120
|
-
return Promise.reject(new CerastreamConnectionError("control connection is not open"));
|
|
124
|
+
return Promise.reject(new CerastreamConnectionError("control connection is not open", undefined, "closed"));
|
|
121
125
|
}
|
|
122
126
|
const id = this.nextId++;
|
|
123
127
|
const envelope = {
|
|
@@ -139,7 +143,7 @@ class ClientImpl {
|
|
|
139
143
|
catch (err) {
|
|
140
144
|
clearTimeout(timer);
|
|
141
145
|
this.pending.delete(id);
|
|
142
|
-
reject(new CerastreamConnectionError("failed to write request", err));
|
|
146
|
+
reject(new CerastreamConnectionError("failed to write request", err, "lost"));
|
|
143
147
|
}
|
|
144
148
|
});
|
|
145
149
|
}
|
|
@@ -206,7 +210,7 @@ class ClientImpl {
|
|
|
206
210
|
this.socket = undefined;
|
|
207
211
|
if (this.intentionalClose)
|
|
208
212
|
return;
|
|
209
|
-
const conn = new CerastreamConnectionError("control connection lost", err);
|
|
213
|
+
const conn = new CerastreamConnectionError("control connection lost", err, "lost");
|
|
210
214
|
this.rejectAllPending(conn);
|
|
211
215
|
if (this.autoReconnect)
|
|
212
216
|
void this.reconnectLoop();
|
|
@@ -263,6 +267,20 @@ function safeControlSocketPath() {
|
|
|
263
267
|
function isObject(value) {
|
|
264
268
|
return typeof value === "object" && value !== null;
|
|
265
269
|
}
|
|
270
|
+
// Classify a connect-time transport failure into a stable machine code. Bun's
|
|
271
|
+
// `connect` collapses several distinct errors onto ENOENT and does not reliably
|
|
272
|
+
// surface ECONNREFUSED for a stale AF_UNIX socket, so an explicit errno is
|
|
273
|
+
// honored when present, then the socket file's presence disambiguates: a path
|
|
274
|
+
// that is gone is `absent` (engine not up yet), a path that still exists but
|
|
275
|
+
// won't accept is `refused` (a crashed/stale listener).
|
|
276
|
+
function classifyConnectError(err, socketPath) {
|
|
277
|
+
const raw = isObject(err) ? err.code : undefined;
|
|
278
|
+
if (raw === "ECONNREFUSED")
|
|
279
|
+
return "refused";
|
|
280
|
+
if (raw === "EACCES" || raw === "EPERM")
|
|
281
|
+
return "unreachable";
|
|
282
|
+
return existsSync(socketPath) ? "refused" : "absent";
|
|
283
|
+
}
|
|
266
284
|
function sleep(ms) {
|
|
267
285
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
268
286
|
}
|
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.10.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.10.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,16 @@ 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>;
|
|
127
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
128
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
109
129
|
}, z.core.$strip>;
|
|
110
130
|
}, z.core.$strip>;
|
|
111
131
|
/** Payload of a {@link deviceEventSchema} event. */
|
|
@@ -162,6 +182,62 @@ export declare const previewEventSchema: z.ZodObject<{
|
|
|
162
182
|
}, z.core.$strip>;
|
|
163
183
|
/** Payload of a {@link previewEventSchema} event. */
|
|
164
184
|
export type PreviewEvent = z.infer<typeof previewEventSchema>;
|
|
185
|
+
/**
|
|
186
|
+
* `audio-level` event — the always-on audio level meter (ADR-0007, additive Todo
|
|
187
|
+
* 21). Either a real per-channel level from the current owner (the idle
|
|
188
|
+
* `sidecar` or the `streaming` audio leg), or `unavailable: true` + `reason` for
|
|
189
|
+
* a handoff gap or a degenerate `audio.mode` — never a fabricated level. A level
|
|
190
|
+
* carries `source`/`channels`/`rms_db`/`peak_db`/`floor_db`; an unavailable event
|
|
191
|
+
* carries `unavailable`/`reason` and omits the level fields. `floor_db` (`-1e6`)
|
|
192
|
+
* is the sentinel a consumer maps back to "silence" (JSON has no `-Infinity`).
|
|
193
|
+
* Independent of the preview-scoped `audio-level` WebSocket frame.
|
|
194
|
+
*/
|
|
195
|
+
export declare const audioLevelEventSchema: z.ZodObject<{
|
|
196
|
+
type: z.ZodLiteral<"audio-level">;
|
|
197
|
+
seq: z.ZodNumber;
|
|
198
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
199
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
200
|
+
owner: z.ZodEnum<{
|
|
201
|
+
streaming: "streaming";
|
|
202
|
+
sidecar: "sidecar";
|
|
203
|
+
}>;
|
|
204
|
+
}, z.core.$strip>>;
|
|
205
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
206
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
207
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
208
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
209
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
210
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
211
|
+
device_busy: "device_busy";
|
|
212
|
+
no_device: "no_device";
|
|
213
|
+
mode_none: "mode_none";
|
|
214
|
+
handoff: "handoff";
|
|
215
|
+
}>>;
|
|
216
|
+
}, z.core.$strip>;
|
|
217
|
+
/** Payload of a {@link audioLevelEventSchema} event. */
|
|
218
|
+
export type AudioLevelEvent = z.infer<typeof audioLevelEventSchema>;
|
|
219
|
+
/**
|
|
220
|
+
* `config-change` event — one phase of a `change-config` transaction (0.10.0,
|
|
221
|
+
* additive Todo 9). Exactly one `applying` at entry, then exactly ONE terminal
|
|
222
|
+
* phase for the same `attempt_id` (which the RPC result also echoes, so a caller
|
|
223
|
+
* that missed an event can still correlate). A terminal phase is published only
|
|
224
|
+
* after the outcome gate resolved, so `applied` means "PLAYING **and** frames
|
|
225
|
+
* actually advancing", never merely "PLAYING".
|
|
226
|
+
*/
|
|
227
|
+
export declare const configChangeEventSchema: z.ZodObject<{
|
|
228
|
+
type: z.ZodLiteral<"config-change">;
|
|
229
|
+
seq: z.ZodNumber;
|
|
230
|
+
attempt_id: z.ZodString;
|
|
231
|
+
phase: z.ZodEnum<{
|
|
232
|
+
applying: "applying";
|
|
233
|
+
applied: "applied";
|
|
234
|
+
reverted: "reverted";
|
|
235
|
+
rollback_failed: "rollback_failed";
|
|
236
|
+
}>;
|
|
237
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
238
|
+
}, z.core.$strip>;
|
|
239
|
+
/** Payload of a {@link configChangeEventSchema} event. */
|
|
240
|
+
export type ConfigChangeEvent = z.infer<typeof configChangeEventSchema>;
|
|
165
241
|
/** Discriminated union of every v1 event payload (the inner `params`). */
|
|
166
242
|
export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
167
243
|
type: z.ZodLiteral<"status">;
|
|
@@ -186,6 +262,11 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
186
262
|
active_input: z.ZodOptional<z.ZodString>;
|
|
187
263
|
decoder: z.ZodOptional<z.ZodString>;
|
|
188
264
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
265
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
266
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
267
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
268
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
269
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
189
270
|
}, z.core.$strip>>;
|
|
190
271
|
}, z.core.$strip>, z.ZodObject<{
|
|
191
272
|
type: z.ZodLiteral<"switch">;
|
|
@@ -232,6 +313,16 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
232
313
|
network: "network";
|
|
233
314
|
}>>;
|
|
234
315
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
316
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
317
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
318
|
+
hdmi: "hdmi";
|
|
319
|
+
usb: "usb";
|
|
320
|
+
bluetooth: "bluetooth";
|
|
321
|
+
onboard: "onboard";
|
|
322
|
+
}>>;
|
|
323
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
324
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
325
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
235
326
|
}, z.core.$strip>;
|
|
236
327
|
}, z.core.$strip>, z.ZodObject<{
|
|
237
328
|
type: z.ZodLiteral<"bitrate">;
|
|
@@ -266,6 +357,38 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
266
357
|
seq: z.ZodNumber;
|
|
267
358
|
session_id: z.ZodString;
|
|
268
359
|
phase: z.ZodString;
|
|
360
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
361
|
+
type: z.ZodLiteral<"audio-level">;
|
|
362
|
+
seq: z.ZodNumber;
|
|
363
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
364
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
365
|
+
owner: z.ZodEnum<{
|
|
366
|
+
streaming: "streaming";
|
|
367
|
+
sidecar: "sidecar";
|
|
368
|
+
}>;
|
|
369
|
+
}, z.core.$strip>>;
|
|
370
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
371
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
372
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
373
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
374
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
375
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
376
|
+
device_busy: "device_busy";
|
|
377
|
+
no_device: "no_device";
|
|
378
|
+
mode_none: "mode_none";
|
|
379
|
+
handoff: "handoff";
|
|
380
|
+
}>>;
|
|
381
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
382
|
+
type: z.ZodLiteral<"config-change">;
|
|
383
|
+
seq: z.ZodNumber;
|
|
384
|
+
attempt_id: z.ZodString;
|
|
385
|
+
phase: z.ZodEnum<{
|
|
386
|
+
applying: "applying";
|
|
387
|
+
applied: "applied";
|
|
388
|
+
reverted: "reverted";
|
|
389
|
+
rollback_failed: "rollback_failed";
|
|
390
|
+
}>;
|
|
391
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
269
392
|
}, z.core.$strip>], "type">;
|
|
270
393
|
export type EventParams = z.infer<typeof eventParamsSchema>;
|
|
271
394
|
/** Full event envelope: { jsonrpc, method:"event", params } with typed params. */
|
|
@@ -295,6 +418,11 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
295
418
|
active_input: z.ZodOptional<z.ZodString>;
|
|
296
419
|
decoder: z.ZodOptional<z.ZodString>;
|
|
297
420
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
421
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
422
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
423
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
424
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
425
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
298
426
|
}, z.core.$strip>>;
|
|
299
427
|
}, z.core.$strip>, z.ZodObject<{
|
|
300
428
|
type: z.ZodLiteral<"switch">;
|
|
@@ -341,6 +469,16 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
341
469
|
network: "network";
|
|
342
470
|
}>>;
|
|
343
471
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
472
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
473
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
474
|
+
hdmi: "hdmi";
|
|
475
|
+
usb: "usb";
|
|
476
|
+
bluetooth: "bluetooth";
|
|
477
|
+
onboard: "onboard";
|
|
478
|
+
}>>;
|
|
479
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
480
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
481
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
344
482
|
}, z.core.$strip>;
|
|
345
483
|
}, z.core.$strip>, z.ZodObject<{
|
|
346
484
|
type: z.ZodLiteral<"bitrate">;
|
|
@@ -375,11 +513,45 @@ export declare const cerastreamEventSchema: z.ZodObject<{
|
|
|
375
513
|
seq: z.ZodNumber;
|
|
376
514
|
session_id: z.ZodString;
|
|
377
515
|
phase: z.ZodString;
|
|
516
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
517
|
+
type: z.ZodLiteral<"audio-level">;
|
|
518
|
+
seq: z.ZodNumber;
|
|
519
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
520
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
521
|
+
owner: z.ZodEnum<{
|
|
522
|
+
streaming: "streaming";
|
|
523
|
+
sidecar: "sidecar";
|
|
524
|
+
}>;
|
|
525
|
+
}, z.core.$strip>>;
|
|
526
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
527
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
528
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
529
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
530
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
531
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
532
|
+
device_busy: "device_busy";
|
|
533
|
+
no_device: "no_device";
|
|
534
|
+
mode_none: "mode_none";
|
|
535
|
+
handoff: "handoff";
|
|
536
|
+
}>>;
|
|
537
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
538
|
+
type: z.ZodLiteral<"config-change">;
|
|
539
|
+
seq: z.ZodNumber;
|
|
540
|
+
attempt_id: z.ZodString;
|
|
541
|
+
phase: z.ZodEnum<{
|
|
542
|
+
applying: "applying";
|
|
543
|
+
applied: "applied";
|
|
544
|
+
reverted: "reverted";
|
|
545
|
+
rollback_failed: "rollback_failed";
|
|
546
|
+
}>;
|
|
547
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
378
548
|
}, z.core.$strip>], "type">;
|
|
379
549
|
}, z.core.$strip>;
|
|
380
550
|
export type CerastreamEvent = z.infer<typeof cerastreamEventSchema>;
|
|
381
|
-
/** The
|
|
382
|
-
|
|
551
|
+
/** The nine event topics (schema.md "Events" table) — count-assertion source.
|
|
552
|
+
* `audio-level` (Todo 21) and `config-change` (Todo 9) are additive and appended
|
|
553
|
+
* last, preserving the order of every prior topic. */
|
|
554
|
+
export declare const EVENT_TOPICS: readonly ["status", "switch", "device", "bitrate", "srt-stats", "error", "preview", "audio-level", "config-change"];
|
|
383
555
|
export type EventTopicName = (typeof EVENT_TOPICS)[number];
|
|
384
556
|
/** topic → payload Zod schema. Tests assert every topic is represented here. */
|
|
385
557
|
export declare const eventSchemas: {
|
|
@@ -406,6 +578,11 @@ export declare const eventSchemas: {
|
|
|
406
578
|
active_input: z.ZodOptional<z.ZodString>;
|
|
407
579
|
decoder: z.ZodOptional<z.ZodString>;
|
|
408
580
|
input_codec: z.ZodOptional<z.ZodString>;
|
|
581
|
+
passthrough: z.ZodOptional<z.ZodBoolean>;
|
|
582
|
+
gop_probe_outcome: z.ZodOptional<z.ZodString>;
|
|
583
|
+
gop_probe_ms: z.ZodOptional<z.ZodNumber>;
|
|
584
|
+
frames_emitted: z.ZodOptional<z.ZodNumber>;
|
|
585
|
+
pipeline_playing: z.ZodOptional<z.ZodBoolean>;
|
|
409
586
|
}, z.core.$strip>>;
|
|
410
587
|
}, z.core.$strip>;
|
|
411
588
|
readonly switch: z.ZodObject<{
|
|
@@ -454,6 +631,16 @@ export declare const eventSchemas: {
|
|
|
454
631
|
network: "network";
|
|
455
632
|
}>>;
|
|
456
633
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
634
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
635
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
636
|
+
hdmi: "hdmi";
|
|
637
|
+
usb: "usb";
|
|
638
|
+
bluetooth: "bluetooth";
|
|
639
|
+
onboard: "onboard";
|
|
640
|
+
}>>;
|
|
641
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
642
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
643
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
457
644
|
}, z.core.$strip>;
|
|
458
645
|
}, z.core.$strip>;
|
|
459
646
|
readonly bitrate: z.ZodObject<{
|
|
@@ -493,4 +680,38 @@ export declare const eventSchemas: {
|
|
|
493
680
|
session_id: z.ZodString;
|
|
494
681
|
phase: z.ZodString;
|
|
495
682
|
}, z.core.$strip>;
|
|
683
|
+
readonly "audio-level": z.ZodObject<{
|
|
684
|
+
type: z.ZodLiteral<"audio-level">;
|
|
685
|
+
seq: z.ZodNumber;
|
|
686
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
687
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
688
|
+
owner: z.ZodEnum<{
|
|
689
|
+
streaming: "streaming";
|
|
690
|
+
sidecar: "sidecar";
|
|
691
|
+
}>;
|
|
692
|
+
}, z.core.$strip>>;
|
|
693
|
+
channels: z.ZodOptional<z.ZodNumber>;
|
|
694
|
+
rms_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
695
|
+
peak_db: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
696
|
+
floor_db: z.ZodOptional<z.ZodNumber>;
|
|
697
|
+
unavailable: z.ZodOptional<z.ZodLiteral<true>>;
|
|
698
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
699
|
+
device_busy: "device_busy";
|
|
700
|
+
no_device: "no_device";
|
|
701
|
+
mode_none: "mode_none";
|
|
702
|
+
handoff: "handoff";
|
|
703
|
+
}>>;
|
|
704
|
+
}, z.core.$strip>;
|
|
705
|
+
readonly "config-change": z.ZodObject<{
|
|
706
|
+
type: z.ZodLiteral<"config-change">;
|
|
707
|
+
seq: z.ZodNumber;
|
|
708
|
+
attempt_id: z.ZodString;
|
|
709
|
+
phase: z.ZodEnum<{
|
|
710
|
+
applying: "applying";
|
|
711
|
+
applied: "applied";
|
|
712
|
+
reverted: "reverted";
|
|
713
|
+
rollback_failed: "rollback_failed";
|
|
714
|
+
}>;
|
|
715
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
716
|
+
}, z.core.$strip>;
|
|
496
717
|
};
|
package/dist/events.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { processErrorCodeSchema, processErrorSourceSchema, } from "./errors.js";
|
|
3
|
-
import { captureDeviceSchema, inputModeSchema, mediaClassSchema, streamStateSchema, } from "./types.js";
|
|
3
|
+
import { captureDeviceSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, streamStateSchema, } from "./types.js";
|
|
4
4
|
// Server → client `event` notifications, delivered after `subscribe-events`
|
|
5
5
|
// (schema.md "Events"). Each event is the inner `params` of an rpcEventSchema:
|
|
6
6
|
// { type, seq, … }. `type` discriminates; `seq` is a per-type monotonic counter.
|
|
@@ -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,47 @@ 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
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* `config-change` event — one phase of a `change-config` transaction (0.10.0,
|
|
132
|
+
* additive Todo 9). Exactly one `applying` at entry, then exactly ONE terminal
|
|
133
|
+
* phase for the same `attempt_id` (which the RPC result also echoes, so a caller
|
|
134
|
+
* that missed an event can still correlate). A terminal phase is published only
|
|
135
|
+
* after the outcome gate resolved, so `applied` means "PLAYING **and** frames
|
|
136
|
+
* actually advancing", never merely "PLAYING".
|
|
137
|
+
*/
|
|
138
|
+
export const configChangeEventSchema = z.object({
|
|
139
|
+
type: z.literal("config-change"),
|
|
140
|
+
seq,
|
|
141
|
+
attempt_id: z.string(),
|
|
142
|
+
phase: configChangePhaseSchema,
|
|
143
|
+
reason: z.string().optional(), // absent on applying/applied; "teardown_timeout" marks the supervisor escalation
|
|
144
|
+
});
|
|
99
145
|
/** Discriminated union of every v1 event payload (the inner `params`). */
|
|
100
146
|
export const eventParamsSchema = z.discriminatedUnion("type", [
|
|
101
147
|
statusEventSchema,
|
|
@@ -105,6 +151,8 @@ export const eventParamsSchema = z.discriminatedUnion("type", [
|
|
|
105
151
|
srtStatsEventSchema,
|
|
106
152
|
runtimeErrorEventSchema,
|
|
107
153
|
previewEventSchema,
|
|
154
|
+
audioLevelEventSchema,
|
|
155
|
+
configChangeEventSchema,
|
|
108
156
|
]);
|
|
109
157
|
/** Full event envelope: { jsonrpc, method:"event", params } with typed params. */
|
|
110
158
|
export const cerastreamEventSchema = z.object({
|
|
@@ -112,7 +160,9 @@ export const cerastreamEventSchema = z.object({
|
|
|
112
160
|
method: z.literal("event"),
|
|
113
161
|
params: eventParamsSchema,
|
|
114
162
|
});
|
|
115
|
-
/** The
|
|
163
|
+
/** The nine event topics (schema.md "Events" table) — count-assertion source.
|
|
164
|
+
* `audio-level` (Todo 21) and `config-change` (Todo 9) are additive and appended
|
|
165
|
+
* last, preserving the order of every prior topic. */
|
|
116
166
|
export const EVENT_TOPICS = [
|
|
117
167
|
"status",
|
|
118
168
|
"switch",
|
|
@@ -121,6 +171,8 @@ export const EVENT_TOPICS = [
|
|
|
121
171
|
"srt-stats",
|
|
122
172
|
"error",
|
|
123
173
|
"preview",
|
|
174
|
+
"audio-level",
|
|
175
|
+
"config-change",
|
|
124
176
|
];
|
|
125
177
|
/** topic → payload Zod schema. Tests assert every topic is represented here. */
|
|
126
178
|
export const eventSchemas = {
|
|
@@ -131,4 +183,6 @@ export const eventSchemas = {
|
|
|
131
183
|
"srt-stats": srtStatsEventSchema,
|
|
132
184
|
error: runtimeErrorEventSchema,
|
|
133
185
|
preview: previewEventSchema,
|
|
186
|
+
"audio-level": audioLevelEventSchema,
|
|
187
|
+
"config-change": configChangeEventSchema,
|
|
134
188
|
};
|
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<{
|
|
@@ -73,6 +83,7 @@ export declare const reloadConfigParamsSchema: z.ZodObject<{
|
|
|
73
83
|
audio: z.ZodOptional<z.ZodObject<{
|
|
74
84
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
75
85
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
86
|
+
meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
76
87
|
}, z.core.$strip>>;
|
|
77
88
|
}, z.core.$strip>;
|
|
78
89
|
export type ReloadConfigParams = z.infer<typeof reloadConfigParamsSchema>;
|
|
@@ -93,8 +104,14 @@ export declare const reloadConfigResultSchema: z.ZodObject<{
|
|
|
93
104
|
audio: z.ZodOptional<z.ZodObject<{
|
|
94
105
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
95
106
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
96
108
|
}, z.core.$strip>>;
|
|
97
109
|
}, z.core.$strip>;
|
|
110
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
111
|
+
encoder: "encoder";
|
|
112
|
+
"source-fixed": "source-fixed";
|
|
113
|
+
}>>;
|
|
114
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
98
115
|
}, z.core.$strip>;
|
|
99
116
|
export type ReloadConfigResult = z.infer<typeof reloadConfigResultSchema>;
|
|
100
117
|
export declare const setBitrateParamsSchema: z.ZodObject<{
|
|
@@ -105,6 +122,11 @@ export declare const setBitrateResultSchema: z.ZodObject<{
|
|
|
105
122
|
applied: z.ZodObject<{
|
|
106
123
|
max_bitrate: z.ZodNumber;
|
|
107
124
|
}, z.core.$strip>;
|
|
125
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
126
|
+
encoder: "encoder";
|
|
127
|
+
"source-fixed": "source-fixed";
|
|
128
|
+
}>>;
|
|
129
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
108
130
|
}, z.core.$strip>;
|
|
109
131
|
export type SetBitrateResult = z.infer<typeof setBitrateResultSchema>;
|
|
110
132
|
export declare const switchInputParamsSchema: z.ZodObject<{
|
|
@@ -172,6 +194,16 @@ export declare const listDevicesResultSchema: z.ZodObject<{
|
|
|
172
194
|
network: "network";
|
|
173
195
|
}>>;
|
|
174
196
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
197
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
198
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
199
|
+
hdmi: "hdmi";
|
|
200
|
+
usb: "usb";
|
|
201
|
+
bluetooth: "bluetooth";
|
|
202
|
+
onboard: "onboard";
|
|
203
|
+
}>>;
|
|
204
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
205
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
206
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
175
207
|
}, z.core.$strip>>;
|
|
176
208
|
}, z.core.$strip>;
|
|
177
209
|
export type ListDevicesResult = z.infer<typeof listDevicesResultSchema>;
|
|
@@ -183,6 +215,8 @@ export declare const eventTopicSchema: z.ZodEnum<{
|
|
|
183
215
|
switch: "switch";
|
|
184
216
|
"srt-stats": "srt-stats";
|
|
185
217
|
preview: "preview";
|
|
218
|
+
"audio-level": "audio-level";
|
|
219
|
+
"config-change": "config-change";
|
|
186
220
|
}>;
|
|
187
221
|
export type EventTopic = z.infer<typeof eventTopicSchema>;
|
|
188
222
|
export declare const subscribeEventsParamsSchema: z.ZodObject<{
|
|
@@ -194,6 +228,8 @@ export declare const subscribeEventsParamsSchema: z.ZodObject<{
|
|
|
194
228
|
switch: "switch";
|
|
195
229
|
"srt-stats": "srt-stats";
|
|
196
230
|
preview: "preview";
|
|
231
|
+
"audio-level": "audio-level";
|
|
232
|
+
"config-change": "config-change";
|
|
197
233
|
}>>>;
|
|
198
234
|
}, z.core.$strip>;
|
|
199
235
|
export type SubscribeEventsParams = z.infer<typeof subscribeEventsParamsSchema>;
|
|
@@ -206,6 +242,8 @@ export declare const subscribeEventsResultSchema: z.ZodObject<{
|
|
|
206
242
|
switch: "switch";
|
|
207
243
|
"srt-stats": "srt-stats";
|
|
208
244
|
preview: "preview";
|
|
245
|
+
"audio-level": "audio-level";
|
|
246
|
+
"config-change": "config-change";
|
|
209
247
|
}>>;
|
|
210
248
|
}, z.core.$strip>;
|
|
211
249
|
export type SubscribeEventsResult = z.infer<typeof subscribeEventsResultSchema>;
|
|
@@ -274,6 +312,16 @@ export declare const platformCapsSchema: z.ZodObject<{
|
|
|
274
312
|
supports_h265: z.ZodBoolean;
|
|
275
313
|
hardware_accelerated: z.ZodBoolean;
|
|
276
314
|
max_resolution: z.ZodString;
|
|
315
|
+
hardware_kind: z.ZodOptional<z.ZodEnum<{
|
|
316
|
+
rk3588: "rk3588";
|
|
317
|
+
jetson: "jetson";
|
|
318
|
+
n100: "n100";
|
|
319
|
+
generic: "generic";
|
|
320
|
+
}>>;
|
|
321
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
322
|
+
detected: "detected";
|
|
323
|
+
override: "override";
|
|
324
|
+
}>>;
|
|
277
325
|
}, z.core.$strip>;
|
|
278
326
|
export type PlatformCaps = z.infer<typeof platformCapsSchema>;
|
|
279
327
|
export declare const previewAvailabilitySchema: z.ZodObject<{
|
|
@@ -287,6 +335,16 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
|
|
|
287
335
|
supports_h265: z.ZodBoolean;
|
|
288
336
|
hardware_accelerated: z.ZodBoolean;
|
|
289
337
|
max_resolution: z.ZodString;
|
|
338
|
+
hardware_kind: z.ZodOptional<z.ZodEnum<{
|
|
339
|
+
rk3588: "rk3588";
|
|
340
|
+
jetson: "jetson";
|
|
341
|
+
n100: "n100";
|
|
342
|
+
generic: "generic";
|
|
343
|
+
}>>;
|
|
344
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
345
|
+
detected: "detected";
|
|
346
|
+
override: "override";
|
|
347
|
+
}>>;
|
|
290
348
|
}, z.core.$strip>;
|
|
291
349
|
encoder: z.ZodObject<{
|
|
292
350
|
codecs: z.ZodArray<z.ZodString>;
|
|
@@ -319,8 +377,45 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
|
|
|
319
377
|
bound: z.ZodBoolean;
|
|
320
378
|
}, z.core.$strip>>;
|
|
321
379
|
network_embedded_audio: z.ZodOptional<z.ZodBoolean>;
|
|
380
|
+
features: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
322
381
|
}, z.core.$strip>;
|
|
323
382
|
export type GetCapabilitiesResult = z.infer<typeof getCapabilitiesResultSchema>;
|
|
383
|
+
export declare const changeConfigParamsSchema: z.ZodObject<{
|
|
384
|
+
pipeline: z.ZodOptional<z.ZodString>;
|
|
385
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
386
|
+
framerate: z.ZodOptional<z.ZodNumber>;
|
|
387
|
+
codec: z.ZodOptional<z.ZodEnum<{
|
|
388
|
+
h264: "h264";
|
|
389
|
+
h265: "h265";
|
|
390
|
+
}>>;
|
|
391
|
+
input_id: z.ZodOptional<z.ZodString>;
|
|
392
|
+
}, z.core.$strip>;
|
|
393
|
+
export type ChangeConfigParams = z.infer<typeof changeConfigParamsSchema>;
|
|
394
|
+
export declare const changeConfigResultSchema: z.ZodObject<{
|
|
395
|
+
attempt_id: z.ZodString;
|
|
396
|
+
phase: z.ZodEnum<{
|
|
397
|
+
applying: "applying";
|
|
398
|
+
applied: "applied";
|
|
399
|
+
reverted: "reverted";
|
|
400
|
+
rollback_failed: "rollback_failed";
|
|
401
|
+
}>;
|
|
402
|
+
state: z.ZodEnum<{
|
|
403
|
+
idle: "idle";
|
|
404
|
+
starting: "starting";
|
|
405
|
+
streaming: "streaming";
|
|
406
|
+
stopping: "stopping";
|
|
407
|
+
}>;
|
|
408
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
409
|
+
}, z.core.$strip>;
|
|
410
|
+
export type ChangeConfigResult = z.infer<typeof changeConfigResultSchema>;
|
|
411
|
+
/**
|
|
412
|
+
* The `reason` a `rollback_failed` carries when a teardown deadline overran.
|
|
413
|
+
* A TERMINAL supervisor escalation, not an ordinary failure: the engine could not
|
|
414
|
+
* prove the old session released its capture devices within the bound, so it
|
|
415
|
+
* refuses to build a second session that would race it. Mirrors the Rust
|
|
416
|
+
* `REASON_TEARDOWN_TIMEOUT`; consumers render it distinctly.
|
|
417
|
+
*/
|
|
418
|
+
export declare const REASON_TEARDOWN_TIMEOUT: "teardown_timeout";
|
|
324
419
|
/** The eight v1 control methods (the literal JSON-RPC `method` strings). */
|
|
325
420
|
export declare const V1_METHODS: readonly ["start", "stop", "reload-config", "set-bitrate", "switch-input", "list-devices", "subscribe-events", "preview-session"];
|
|
326
421
|
export type V1Method = (typeof V1_METHODS)[number];
|
|
@@ -371,10 +466,20 @@ export declare const requestSchemas: {
|
|
|
371
466
|
resolution: z.ZodOptional<z.ZodString>;
|
|
372
467
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
373
468
|
audio: z.ZodOptional<z.ZodObject<{
|
|
469
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
470
|
+
default: "default";
|
|
471
|
+
none: "none";
|
|
472
|
+
device: "device";
|
|
473
|
+
}>>;
|
|
374
474
|
device: z.ZodOptional<z.ZodString>;
|
|
375
475
|
codec: z.ZodOptional<z.ZodString>;
|
|
376
476
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
377
477
|
}, z.core.$strip>>;
|
|
478
|
+
video_passthrough: z.ZodOptional<z.ZodEnum<{
|
|
479
|
+
auto: "auto";
|
|
480
|
+
force: "force";
|
|
481
|
+
off: "off";
|
|
482
|
+
}>>;
|
|
378
483
|
}, z.core.$strip>;
|
|
379
484
|
readonly result: z.ZodObject<{
|
|
380
485
|
session_id: z.ZodString;
|
|
@@ -416,6 +521,7 @@ export declare const requestSchemas: {
|
|
|
416
521
|
audio: z.ZodOptional<z.ZodObject<{
|
|
417
522
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
418
523
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
524
|
+
meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
419
525
|
}, z.core.$strip>>;
|
|
420
526
|
}, z.core.$strip>;
|
|
421
527
|
readonly result: z.ZodObject<{
|
|
@@ -435,8 +541,14 @@ export declare const requestSchemas: {
|
|
|
435
541
|
audio: z.ZodOptional<z.ZodObject<{
|
|
436
542
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
437
543
|
delay_ms_signed: z.ZodOptional<z.ZodNumber>;
|
|
544
|
+
meter_device: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
438
545
|
}, z.core.$strip>>;
|
|
439
546
|
}, z.core.$strip>;
|
|
547
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
548
|
+
encoder: "encoder";
|
|
549
|
+
"source-fixed": "source-fixed";
|
|
550
|
+
}>>;
|
|
551
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
440
552
|
}, z.core.$strip>;
|
|
441
553
|
};
|
|
442
554
|
readonly "set-bitrate": {
|
|
@@ -447,6 +559,11 @@ export declare const requestSchemas: {
|
|
|
447
559
|
applied: z.ZodObject<{
|
|
448
560
|
max_bitrate: z.ZodNumber;
|
|
449
561
|
}, z.core.$strip>;
|
|
562
|
+
bitrate_control: z.ZodOptional<z.ZodEnum<{
|
|
563
|
+
encoder: "encoder";
|
|
564
|
+
"source-fixed": "source-fixed";
|
|
565
|
+
}>>;
|
|
566
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
450
567
|
}, z.core.$strip>;
|
|
451
568
|
};
|
|
452
569
|
readonly "switch-input": {
|
|
@@ -498,6 +615,16 @@ export declare const requestSchemas: {
|
|
|
498
615
|
network: "network";
|
|
499
616
|
}>>;
|
|
500
617
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
618
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
619
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
620
|
+
hdmi: "hdmi";
|
|
621
|
+
usb: "usb";
|
|
622
|
+
bluetooth: "bluetooth";
|
|
623
|
+
onboard: "onboard";
|
|
624
|
+
}>>;
|
|
625
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
626
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
627
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
501
628
|
}, z.core.$strip>>;
|
|
502
629
|
}, z.core.$strip>;
|
|
503
630
|
};
|
|
@@ -511,6 +638,8 @@ export declare const requestSchemas: {
|
|
|
511
638
|
switch: "switch";
|
|
512
639
|
"srt-stats": "srt-stats";
|
|
513
640
|
preview: "preview";
|
|
641
|
+
"audio-level": "audio-level";
|
|
642
|
+
"config-change": "config-change";
|
|
514
643
|
}>>>;
|
|
515
644
|
}, z.core.$strip>;
|
|
516
645
|
readonly result: z.ZodObject<{
|
|
@@ -522,6 +651,8 @@ export declare const requestSchemas: {
|
|
|
522
651
|
switch: "switch";
|
|
523
652
|
"srt-stats": "srt-stats";
|
|
524
653
|
preview: "preview";
|
|
654
|
+
"audio-level": "audio-level";
|
|
655
|
+
"config-change": "config-change";
|
|
525
656
|
}>>;
|
|
526
657
|
}, z.core.$strip>;
|
|
527
658
|
};
|
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, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } 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 ----
|
|
@@ -39,11 +39,19 @@ export const reloadConfigParamsSchema = z.object({
|
|
|
39
39
|
.object({
|
|
40
40
|
delay_ms: z.number().int().min(0).max(AUDIO_DELAY_MAX_MS).optional(), // legacy unsigned; kept for 0.3.0 callers
|
|
41
41
|
delay_ms_signed: z.number().int().optional(), // signed sibling; clamped at apply, so unbounded
|
|
42
|
+
// additive (0.9.0): idle-meter card preference — absent leaves it unchanged,
|
|
43
|
+
// `null` restores the engine's auto-pick, `hw:CARD=…` prefers that card
|
|
44
|
+
// (a preference, not a pin: a card that never delivers is still demoted)
|
|
45
|
+
meter_device: z.string().nullable().optional(),
|
|
42
46
|
})
|
|
43
47
|
.optional(),
|
|
44
48
|
});
|
|
45
49
|
export const reloadConfigResultSchema = z.object({
|
|
46
50
|
applied: reloadConfigParamsSchema, // post-clamp values actually applied
|
|
51
|
+
// additive (0.5.0): a bitrate-bearing reload during passthrough is NOT applied —
|
|
52
|
+
// "source-fixed" + reason "passthrough" report that. Absent on a transcode reload.
|
|
53
|
+
bitrate_control: bitrateControlSchema.optional(),
|
|
54
|
+
reason: z.string().optional(),
|
|
47
55
|
});
|
|
48
56
|
// ---- 4. set-bitrate ----
|
|
49
57
|
export const setBitrateParamsSchema = z.object({
|
|
@@ -51,6 +59,10 @@ export const setBitrateParamsSchema = z.object({
|
|
|
51
59
|
});
|
|
52
60
|
export const setBitrateResultSchema = z.object({
|
|
53
61
|
applied: z.object({ max_bitrate: z.number().int() }),
|
|
62
|
+
// additive (0.5.0). On passthrough there is no encoder to drive, so a request is
|
|
63
|
+
// NOT applied: "source-fixed" with reason "passthrough". Absent/"encoder" ⇒ applied.
|
|
64
|
+
bitrate_control: bitrateControlSchema.optional(),
|
|
65
|
+
reason: z.string().optional(),
|
|
54
66
|
});
|
|
55
67
|
// ---- 5. switch-input ----
|
|
56
68
|
export const switchInputParamsSchema = z.object({
|
|
@@ -91,6 +103,8 @@ export const eventTopicSchema = z.enum([
|
|
|
91
103
|
"srt-stats",
|
|
92
104
|
"error",
|
|
93
105
|
"preview",
|
|
106
|
+
"audio-level",
|
|
107
|
+
"config-change",
|
|
94
108
|
]);
|
|
95
109
|
export const subscribeEventsParamsSchema = z.object({
|
|
96
110
|
topics: z.array(eventTopicSchema).optional(), // default: all topics
|
|
@@ -154,6 +168,10 @@ export const platformCapsSchema = z.object({
|
|
|
154
168
|
supports_h265: z.boolean(),
|
|
155
169
|
hardware_accelerated: z.boolean(),
|
|
156
170
|
max_resolution: z.string(),
|
|
171
|
+
// additive: the engine's resolved hardware kind and how it was resolved.
|
|
172
|
+
// Optional so a pre-field engine (which omits both) still parses.
|
|
173
|
+
hardware_kind: z.enum(["rk3588", "jetson", "n100", "generic"]).optional(),
|
|
174
|
+
source: z.enum(["detected", "override"]).optional(),
|
|
157
175
|
});
|
|
158
176
|
// Preview-server availability (0.4.0, additive). Lets the UI tell an unbound /
|
|
159
177
|
// port-conflicted preview (enabled:true, bound:false) from a down engine.
|
|
@@ -173,7 +191,44 @@ export const getCapabilitiesResultSchema = z.object({
|
|
|
173
191
|
profile_catalog_version: z.string().optional(), // semver of the supported_profiles catalog
|
|
174
192
|
preview: previewAvailabilitySchema.optional(), // preview-server availability (unbound vs down)
|
|
175
193
|
network_embedded_audio: z.boolean().optional(), // engine routes network-ingest embedded audio to the mux
|
|
194
|
+
features: z.array(z.string()).optional(), // named engine features (e.g. "video-passthrough") for fail-closed negotiation
|
|
195
|
+
});
|
|
196
|
+
// ---- 10. change-config (0.10.0, additive) ----
|
|
197
|
+
// Deliberately absent from V1_METHODS / requestSchemas, like get-capabilities and
|
|
198
|
+
// switch-audio, so the frozen eight-method contract count stays eight. Params are
|
|
199
|
+
// a DELTA of startParamsSchema: absent ⇒ keep the live value. An empty delta is
|
|
200
|
+
// refused with `cerastream.params.invalid` — a no-op that restarted the pipeline
|
|
201
|
+
// would be the worst possible outcome. `input_id` rides the transaction, so a
|
|
202
|
+
// source change renegotiates caps instead of going through `switch-input`.
|
|
203
|
+
export const changeConfigParamsSchema = z
|
|
204
|
+
.object({
|
|
205
|
+
pipeline: z.string().optional(),
|
|
206
|
+
resolution: z.string().optional(), // "WxH" pixels, e.g. "3840x2160"
|
|
207
|
+
framerate: z.number().optional(),
|
|
208
|
+
codec: videoCodecSchema.optional(),
|
|
209
|
+
input_id: z.string().optional(),
|
|
210
|
+
})
|
|
211
|
+
.refine((delta) => Object.keys(delta).length > 0, {
|
|
212
|
+
message: "change-config delta must carry at least one field",
|
|
213
|
+
});
|
|
214
|
+
// `Ok` for every phase the transaction actually reached — INCLUDING
|
|
215
|
+
// `rollback_failed`, an honest terminal outcome rather than an RPC fault. A
|
|
216
|
+
// Tier-1 error means the transaction never started, so a caller can tell
|
|
217
|
+
// "nothing happened" from "something happened and here is what".
|
|
218
|
+
export const changeConfigResultSchema = z.object({
|
|
219
|
+
attempt_id: z.string(), // correlates with every config-change event of the attempt
|
|
220
|
+
phase: configChangePhaseSchema, // the TERMINAL phase reached
|
|
221
|
+
state: streamStateSchema, // streaming for applied/reverted, idle for rollback_failed
|
|
222
|
+
reason: z.string().optional(), // machine-stable cause on a non-applied phase
|
|
176
223
|
});
|
|
224
|
+
/**
|
|
225
|
+
* The `reason` a `rollback_failed` carries when a teardown deadline overran.
|
|
226
|
+
* A TERMINAL supervisor escalation, not an ordinary failure: the engine could not
|
|
227
|
+
* prove the old session released its capture devices within the bound, so it
|
|
228
|
+
* refuses to build a second session that would race it. Mirrors the Rust
|
|
229
|
+
* `REASON_TEARDOWN_TIMEOUT`; consumers render it distinctly.
|
|
230
|
+
*/
|
|
231
|
+
export const REASON_TEARDOWN_TIMEOUT = "teardown_timeout";
|
|
177
232
|
// ---- method registry (count-assertion source of truth) ----
|
|
178
233
|
/** The eight v1 control methods (the literal JSON-RPC `method` strings). */
|
|
179
234
|
export const V1_METHODS = [
|
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,20 @@ 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>;
|
|
64
|
+
export declare const configChangePhaseSchema: z.ZodEnum<{
|
|
65
|
+
applying: "applying";
|
|
66
|
+
applied: "applied";
|
|
67
|
+
reverted: "reverted";
|
|
68
|
+
rollback_failed: "rollback_failed";
|
|
69
|
+
}>;
|
|
70
|
+
export type ConfigChangePhase = z.infer<typeof configChangePhaseSchema>;
|
|
46
71
|
/** SRT transport config. Mirrors schema.md `start.srt` exactly. */
|
|
47
72
|
export declare const srtConfigSchema: z.ZodObject<{
|
|
48
73
|
host: z.ZodString;
|
|
@@ -73,7 +98,18 @@ export type BitrateConfig = z.infer<typeof bitrateConfigSchema>;
|
|
|
73
98
|
* later, negative = earlier); it is clamped to ±AUDIO_DELAY_MAX_MS when applied,
|
|
74
99
|
* never rejected — so the schema does NOT bound it.
|
|
75
100
|
*/
|
|
101
|
+
export declare const audioModeSchema: z.ZodEnum<{
|
|
102
|
+
default: "default";
|
|
103
|
+
none: "none";
|
|
104
|
+
device: "device";
|
|
105
|
+
}>;
|
|
106
|
+
export type AudioMode = z.infer<typeof audioModeSchema>;
|
|
76
107
|
export declare const audioConfigSchema: z.ZodObject<{
|
|
108
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
109
|
+
default: "default";
|
|
110
|
+
none: "none";
|
|
111
|
+
device: "device";
|
|
112
|
+
}>>;
|
|
77
113
|
device: z.ZodOptional<z.ZodString>;
|
|
78
114
|
codec: z.ZodOptional<z.ZodString>;
|
|
79
115
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
@@ -114,10 +150,20 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
|
|
|
114
150
|
resolution: z.ZodOptional<z.ZodString>;
|
|
115
151
|
framerate: z.ZodOptional<z.ZodNumber>;
|
|
116
152
|
audio: z.ZodOptional<z.ZodObject<{
|
|
153
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
154
|
+
default: "default";
|
|
155
|
+
none: "none";
|
|
156
|
+
device: "device";
|
|
157
|
+
}>>;
|
|
117
158
|
device: z.ZodOptional<z.ZodString>;
|
|
118
159
|
codec: z.ZodOptional<z.ZodString>;
|
|
119
160
|
delay_ms: z.ZodOptional<z.ZodNumber>;
|
|
120
161
|
}, z.core.$strip>>;
|
|
162
|
+
video_passthrough: z.ZodOptional<z.ZodEnum<{
|
|
163
|
+
auto: "auto";
|
|
164
|
+
force: "force";
|
|
165
|
+
off: "off";
|
|
166
|
+
}>>;
|
|
121
167
|
}, z.core.$strip>;
|
|
122
168
|
export type CerastreamConfig = z.infer<typeof cerastreamConfigSchema>;
|
|
123
169
|
export type PartialCerastreamConfig = z.input<typeof cerastreamConfigSchema>;
|
|
@@ -159,6 +205,16 @@ export declare const captureDeviceSchema: z.ZodObject<{
|
|
|
159
205
|
network: "network";
|
|
160
206
|
}>>;
|
|
161
207
|
alsa_card_id: z.ZodOptional<z.ZodString>;
|
|
208
|
+
product_name: z.ZodOptional<z.ZodString>;
|
|
209
|
+
transport: z.ZodOptional<z.ZodEnum<{
|
|
210
|
+
hdmi: "hdmi";
|
|
211
|
+
usb: "usb";
|
|
212
|
+
bluetooth: "bluetooth";
|
|
213
|
+
onboard: "onboard";
|
|
214
|
+
}>>;
|
|
215
|
+
stable_id: z.ZodOptional<z.ZodString>;
|
|
216
|
+
physical_group_id: z.ZodOptional<z.ZodString>;
|
|
217
|
+
hardware_serial: z.ZodOptional<z.ZodString>;
|
|
162
218
|
}, z.core.$strip>;
|
|
163
219
|
export type CaptureDevice = z.infer<typeof captureDeviceSchema>;
|
|
164
220
|
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,29 @@ 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
|
+
]);
|
|
55
|
+
// Phase of one `change-config` transaction (0.10.0, additive). `applying` is
|
|
56
|
+
// published once at entry, then exactly ONE terminal phase for the same
|
|
57
|
+
// attempt_id: `applied` (the new config satisfied the outcome gate),
|
|
58
|
+
// `reverted` (it did not and the single known-good rollback attempt did), or
|
|
59
|
+
// `rollback_failed` (no rollback was possible, or the one attempt also failed —
|
|
60
|
+
// the engine is idle, which is the truthful terminal state).
|
|
61
|
+
export const configChangePhaseSchema = z.enum([
|
|
62
|
+
"applying",
|
|
63
|
+
"applied",
|
|
64
|
+
"reverted",
|
|
65
|
+
"rollback_failed",
|
|
66
|
+
]);
|
|
34
67
|
// ---- config sub-schemas (canonical; reused by `start` + the unified config) ----
|
|
35
68
|
/** SRT transport config. Mirrors schema.md `start.srt` exactly. */
|
|
36
69
|
export const srtConfigSchema = z.object({
|
|
@@ -67,7 +100,15 @@ export const bitrateConfigSchema = z
|
|
|
67
100
|
* later, negative = earlier); it is clamped to ±AUDIO_DELAY_MAX_MS when applied,
|
|
68
101
|
* never rejected — so the schema does NOT bound it.
|
|
69
102
|
*/
|
|
103
|
+
// How the program audio is sourced (0.6.0, additive). "none" = no audio branch
|
|
104
|
+
// (video-only TS); "default" = embedded audio for a network source, the selected
|
|
105
|
+
// device or a silent track for a capture source; "device" = the selected ALSA
|
|
106
|
+
// leg (requires `device`). Absent ⇒ legacy inference from `device`/source kind,
|
|
107
|
+
// so a pre-0.6.0 caller keeps working. Replaces leaking pseudo-source strings
|
|
108
|
+
// (e.g. "No audio") into `device`.
|
|
109
|
+
export const audioModeSchema = z.enum(["none", "default", "device"]);
|
|
70
110
|
export const audioConfigSchema = z.object({
|
|
111
|
+
mode: audioModeSchema.optional(),
|
|
71
112
|
device: z.string().optional(), // ALSA capture device id; absent ⇒ test-tone fallback
|
|
72
113
|
codec: z.string().optional(), // audio encoder codec id (e.g. "aac", "opus")
|
|
73
114
|
delay_ms: z.number().int().optional(), // signed A/V-sync delay (clamped at apply)
|
|
@@ -87,6 +128,7 @@ export const cerastreamConfigSchema = z.object({
|
|
|
87
128
|
resolution: z.string().optional(), // additive (0.4.0): "WxH" pixel form (never a UI token)
|
|
88
129
|
framerate: z.number().optional(), // additive (0.4.0): fps as a number, e.g. 29.97
|
|
89
130
|
audio: audioConfigSchema.optional(), // additive (0.4.0): audio device/codec/signed delay
|
|
131
|
+
video_passthrough: videoPassthroughSchema.optional(), // additive (0.5.0): auto|force|off; absent ⇒ auto
|
|
90
132
|
});
|
|
91
133
|
// convenience defaults for building a config client-side
|
|
92
134
|
export const DEFAULT_BITRATE_CONFIG = {
|
|
@@ -110,6 +152,11 @@ export const captureDeviceSchema = z.object({
|
|
|
110
152
|
caps: z.array(captureCapSchema).optional(),
|
|
111
153
|
kind: captureDeviceKindSchema.optional(), // additive (0.4.0): engine-typed device family; absent on legacy producers
|
|
112
154
|
alsa_card_id: z.string().optional(), // additive: ALSA card id for media_class:audio devices only; absent on video + legacy producers
|
|
155
|
+
product_name: z.string().optional(), // additive (Todo 20): real product name, deduped with a #N suffix when shared; absent ⇒ use display_name
|
|
156
|
+
transport: deviceTransportSchema.optional(), // additive (Todo 20): how the device is attached; absent on legacy producers
|
|
157
|
+
stable_id: z.string().optional(), // additive (Todo 20): reboot-stable hardware identity, distinct from input_id/device_path
|
|
158
|
+
physical_group_id: z.string().optional(), // additive (0.10.0, ADR-0008): `usb:<topology-token>` shared by one physical device's rows; absent on non-USB + legacy producers, and an absent group NEVER matches
|
|
159
|
+
hardware_serial: z.string().optional(), // additive (0.10.0, ADR-0008): USB serial, DIAGNOSTIC ONLY — vendors ship placeholder serials, so never match/group/select on it
|
|
113
160
|
});
|
|
114
161
|
// ---- transport telemetry (srt-stats event payload) ----
|
|
115
162
|
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.4",
|
|
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",
|