@ondewo/sip-client-nodejs 5.4.1 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,3 +70,97 @@ npm
70
70
  └── README.md
71
71
  ```
72
72
 
73
+ ## TLS, mutual TLS and certificates
74
+
75
+ gRPC encrypts with **TLS** ("SSL" in names such as `credentials.createSsl` or `grpc.ssl_target_name_override` is legacy naming). The package ships a channel helper, `auth/grpcChannel` (exported from the package root), that builds the `@grpc/grpc-js` credentials and channel options for every generated client:
76
+
77
+ | Mode | `useSecureChannel` | Config fields |
78
+ |-----------------------------------------|--------------------|-----------------------------------------------------------------|
79
+ | Plaintext (not for production) | `false` | none |
80
+ | TLS, server verified by the trust store | `true` (default) | none |
81
+ | TLS, server verified by your CA | `true` (default) | `grpcCert` = PEM of the CA that signed the server certificate |
82
+ | Mutual TLS | `true` (default) | `grpcCert` (optional) plus `grpcClientCert` and `grpcClientKey` |
83
+
84
+ Rules the code enforces:
85
+
86
+ - The three fields hold **PEM content** (`string` or `Buffer`), **not file paths**. Read the files yourself; a value without a PEM header line is refused.
87
+ - `grpcClientCert` and `grpcClientKey` go together: setting only one throws when the `GrpcClientConfig` is built, and `createChannelCredentials` refuses half a pair again before anything reaches gRPC. Empty strings on both mean plain server-authenticated TLS.
88
+ - `useSecureChannel: false` with a client certificate throws instead of silently dropping the identity. A plaintext channel otherwise logs a warning naming `host:port` (through `console.warn`, or the `logger` you pass).
89
+ - Without `grpcCert` the server is verified against Node's default CA store (Node's bundled Mozilla roots plus `NODE_EXTRA_CA_CERTS`; start Node with `--use-system-ca` to use the operating system's store instead).
90
+ - The host you connect to must match a subject alternative name (SAN) of the server certificate. When you connect by IP and the certificate has no IP SAN, pass the name to check as the channel option `'grpc.ssl_target_name_override'`.
91
+ - A bare IPv6 host is bracketed for you (`::1` becomes `[::1]:50051`).
92
+
93
+ ```ts
94
+ import { readFileSync } from 'fs';
95
+
96
+ import * as grpc from '@grpc/grpc-js';
97
+ import { createChannelCredentials, createGrpcClient, GrpcClientConfig, SipClient } from '@ondewo/sip-client-nodejs';
98
+
99
+ const config = new GrpcClientConfig({
100
+ host: '10.0.0.5',
101
+ port: 50051,
102
+ grpcCert: readFileSync('certs/ca.pem'),
103
+ grpcClientCert: readFileSync('certs/client.pem'), // leave both out for server-authenticated TLS
104
+ grpcClientKey: readFileSync('certs/client.key')
105
+ });
106
+
107
+ const client: SipClient = createGrpcClient(SipClient, config, {
108
+ channelOptions: { 'grpc.ssl_target_name_override': 'sip.example.internal' } // only when connecting by IP
109
+ });
110
+ ```
111
+
112
+ `createGrpcClient` starts from `DEFAULT_GRPC_CHANNEL_OPTIONS` (maximum message size 2³¹-1 bytes in both directions, reconnect backoff capped at 5 s) and applies your `channelOptions` on top. To build credentials only, call `createChannelCredentials(config, { useSecureChannel })` and pass the result to any generated client constructor.
113
+
114
+ **One connection for several services.** Clients built with the **same credentials object** (and the same target and options) share one connection, so the TLS handshake happens once:
115
+
116
+ ```ts
117
+ const credentials: grpc.ChannelCredentials = createChannelCredentials(config);
118
+ const first = createGrpcClient(SipClient, config, { credentials });
119
+ const second = createGrpcClient(SipClient, config, { credentials }); // any other generated client works the same way
120
+ ```
121
+
122
+ **Keepalive is off by default.** `@grpc/grpc-js` has no `grpc.http2.max_pings_without_data`, so with `grpc.keepalive_time_ms` set it keeps pinging a stream that carries no data, and a grpc-core server (as every ONDEWO server is) answers with `GOAWAY too_many_pings`: the call fails with `RESOURCE_EXHAUSTED` (measured: after 150 s at a 30 s keepalive). The Python SDKs' 30 s keepalive therefore is not copied; set `grpc.keepalive_time_ms` yourself only against a server whose `grpc.http2.min_ping_interval_without_data_ms` allows it. `grpc.keepalive_timeout_ms` is preset to 20 s for that case.
123
+
124
+ ### A test PKI with openssl
125
+
126
+ A CA, a server certificate with SANs and a client certificate with the `clientAuth` extended key usage. For tests only: the keys are unencrypted.
127
+
128
+ ```bash
129
+ openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -days 365 \
130
+ -subj "/CN=Test CA" -keyout ca.key -out ca.pem
131
+
132
+ printf 'subjectAltName=DNS:localhost,IP:127.0.0.1\nextendedKeyUsage=serverAuth\n' > server.ext
133
+ openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
134
+ -subj "/CN=localhost" -keyout server.key -out server.csr
135
+ openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 \
136
+ -extfile server.ext -out server.pem
137
+
138
+ printf 'extendedKeyUsage=clientAuth\n' > client.ext
139
+ openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
140
+ -subj "/CN=my-client" -keyout client.key -out client.csr
141
+ openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 \
142
+ -extfile client.ext -out client.pem
143
+
144
+ chmod 600 *.key
145
+ openssl verify -CAfile ca.pem server.pem client.pem
146
+ ```
147
+
148
+ The client then uses `ca.pem` / `client.pem` / `client.key`; a server that requires client certificates uses `server.pem` / `server.key` and trusts `ca.pem` for its clients.
149
+
150
+ ### TLS security notes
151
+
152
+ - Keep the client key out of source control and out of serialized configs: load it from a file (mode `0600`) or a secret store at startup.
153
+ - `GrpcClientConfig` renders the key as `***REDACTED***` in `toString()`, `util.inspect()` / `console.log()` and `JSON.stringify()` (an empty key renders empty). There is no serializer that writes the key, so a config cannot be persisted with it. A plain object you pass instead of a `GrpcClientConfig` has none of this protection: do not log it.
154
+ - No error message of the helper contains a PEM, a key or a whole config; they name the field and `host:port`.
155
+
156
+ ### TLS troubleshooting
157
+
158
+ A failed handshake surfaces as gRPC status `UNAVAILABLE` (14); the cause is in the error's `details`:
159
+
160
+ - **`unable to verify the first certificate`**: the server certificate is not signed by `grpcCert` (or, without `grpcCert`, not by a CA Node trusts). Pass the CA that signed the server certificate.
161
+ - **`ERR_TLS_CERT_ALTNAME_INVALID: Hostname/IP does not match certificate's altnames`**: the host you dial is not a SAN of the server certificate. Dial a name in the certificate, or set `'grpc.ssl_target_name_override'`. Some Node releases (seen on 22.23 and 24.18) also reject an IPv6 literal against a matching IP SAN; the override to a DNS SAN works around it.
162
+ - **`tlsv13 alert certificate required`**: the server requires mutual TLS and the client sent no certificate. Set `grpcClientCert` and `grpcClientKey`.
163
+ - **`Failed to connect`** right after the handshake with a client certificate: the server does not trust the CA that signed `grpcClientCert`.
164
+ - **`... grpcCert is not PEM content (no PEM header line)`** (thrown when the config is built): a field holds a file path or other text instead of the file's content. Pass `readFileSync(path)`.
165
+ - **`createChannelCredentials for host:port: gRPC rejected the TLS material: ... key values mismatch`** (thrown, not a call error): `grpcClientKey` is not the key of `grpcClientCert`. Other OpenSSL reasons there mean a damaged PEM block.
166
+
@@ -21,6 +21,9 @@ interface ISipService extends grpc.ServiceDefinition<grpc.UntypedServiceImplemen
21
21
  sipPlayWavFiles: ISipService_ISipPlayWavFiles;
22
22
  sipMute: ISipService_ISipMute;
23
23
  sipUnMute: ISipService_ISipUnMute;
24
+ sipReportAnsweringMachineDetected: ISipService_ISipReportAnsweringMachineDetected;
25
+ sipSetCallMediaControl: ISipService_ISipSetCallMediaControl;
26
+ sipStreamCallAudio: ISipService_ISipStreamCallAudio;
24
27
  }
25
28
 
26
29
  interface ISipService_ISipStartSession extends grpc.MethodDefinition<ondewo_sip_sip_pb.SipStartSessionRequest, ondewo_sip_sip_pb.SipStatus> {
@@ -122,6 +125,33 @@ interface ISipService_ISipUnMute extends grpc.MethodDefinition<google_protobuf_e
122
125
  responseSerialize: grpc.serialize<ondewo_sip_sip_pb.SipStatus>;
123
126
  responseDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipStatus>;
124
127
  }
128
+ interface ISipService_ISipReportAnsweringMachineDetected extends grpc.MethodDefinition<ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, ondewo_sip_sip_pb.SipStatus> {
129
+ path: "/ondewo.sip.Sip/SipReportAnsweringMachineDetected";
130
+ requestStream: false;
131
+ responseStream: false;
132
+ requestSerialize: grpc.serialize<ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest>;
133
+ requestDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest>;
134
+ responseSerialize: grpc.serialize<ondewo_sip_sip_pb.SipStatus>;
135
+ responseDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipStatus>;
136
+ }
137
+ interface ISipService_ISipSetCallMediaControl extends grpc.MethodDefinition<ondewo_sip_sip_pb.SipSetCallMediaControlRequest, ondewo_sip_sip_pb.SipStatus> {
138
+ path: "/ondewo.sip.Sip/SipSetCallMediaControl";
139
+ requestStream: false;
140
+ responseStream: false;
141
+ requestSerialize: grpc.serialize<ondewo_sip_sip_pb.SipSetCallMediaControlRequest>;
142
+ requestDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipSetCallMediaControlRequest>;
143
+ responseSerialize: grpc.serialize<ondewo_sip_sip_pb.SipStatus>;
144
+ responseDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipStatus>;
145
+ }
146
+ interface ISipService_ISipStreamCallAudio extends grpc.MethodDefinition<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse> {
147
+ path: "/ondewo.sip.Sip/SipStreamCallAudio";
148
+ requestStream: true;
149
+ responseStream: true;
150
+ requestSerialize: grpc.serialize<ondewo_sip_sip_pb.SipCallAudioRequest>;
151
+ requestDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipCallAudioRequest>;
152
+ responseSerialize: grpc.serialize<ondewo_sip_sip_pb.SipCallAudioResponse>;
153
+ responseDeserialize: grpc.deserialize<ondewo_sip_sip_pb.SipCallAudioResponse>;
154
+ }
125
155
 
126
156
  export const SipService: ISipService;
127
157
 
@@ -137,6 +167,9 @@ export interface ISipServer {
137
167
  sipPlayWavFiles: grpc.handleUnaryCall<ondewo_sip_sip_pb.SipPlayWavFilesRequest, ondewo_sip_sip_pb.SipStatus>;
138
168
  sipMute: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, ondewo_sip_sip_pb.SipStatus>;
139
169
  sipUnMute: grpc.handleUnaryCall<google_protobuf_empty_pb.Empty, ondewo_sip_sip_pb.SipStatus>;
170
+ sipReportAnsweringMachineDetected: grpc.handleUnaryCall<ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, ondewo_sip_sip_pb.SipStatus>;
171
+ sipSetCallMediaControl: grpc.handleUnaryCall<ondewo_sip_sip_pb.SipSetCallMediaControlRequest, ondewo_sip_sip_pb.SipStatus>;
172
+ sipStreamCallAudio: grpc.handleBidiStreamingCall<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
140
173
  }
141
174
 
142
175
  export interface ISipClient {
@@ -173,6 +206,15 @@ export interface ISipClient {
173
206
  sipUnMute(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
174
207
  sipUnMute(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
175
208
  sipUnMute(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
209
+ sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
210
+ sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
211
+ sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
212
+ sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
213
+ sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
214
+ sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
215
+ sipStreamCallAudio(): grpc.ClientDuplexStream<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
216
+ sipStreamCallAudio(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
217
+ sipStreamCallAudio(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
176
218
  }
177
219
 
178
220
  export class SipClient extends grpc.Client implements ISipClient {
@@ -210,4 +252,12 @@ export class SipClient extends grpc.Client implements ISipClient {
210
252
  public sipUnMute(request: google_protobuf_empty_pb.Empty, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
211
253
  public sipUnMute(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
212
254
  public sipUnMute(request: google_protobuf_empty_pb.Empty, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
255
+ public sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
256
+ public sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
257
+ public sipReportAnsweringMachineDetected(request: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
258
+ public sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
259
+ public sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
260
+ public sipSetCallMediaControl(request: ondewo_sip_sip_pb.SipSetCallMediaControlRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ondewo_sip_sip_pb.SipStatus) => void): grpc.ClientUnaryCall;
261
+ public sipStreamCallAudio(options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
262
+ public sipStreamCallAudio(metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ondewo_sip_sip_pb.SipCallAudioRequest, ondewo_sip_sip_pb.SipCallAudioResponse>;
213
263
  }
@@ -32,6 +32,28 @@ function deserialize_google_protobuf_Empty(buffer_arg) {
32
32
  return google_protobuf_empty_pb.Empty.deserializeBinary(new Uint8Array(buffer_arg));
33
33
  }
34
34
 
35
+ function serialize_ondewo_sip_SipCallAudioRequest(arg) {
36
+ if (!(arg instanceof ondewo_sip_sip_pb.SipCallAudioRequest)) {
37
+ throw new Error('Expected argument of type ondewo.sip.SipCallAudioRequest');
38
+ }
39
+ return Buffer.from(arg.serializeBinary());
40
+ }
41
+
42
+ function deserialize_ondewo_sip_SipCallAudioRequest(buffer_arg) {
43
+ return ondewo_sip_sip_pb.SipCallAudioRequest.deserializeBinary(new Uint8Array(buffer_arg));
44
+ }
45
+
46
+ function serialize_ondewo_sip_SipCallAudioResponse(arg) {
47
+ if (!(arg instanceof ondewo_sip_sip_pb.SipCallAudioResponse)) {
48
+ throw new Error('Expected argument of type ondewo.sip.SipCallAudioResponse');
49
+ }
50
+ return Buffer.from(arg.serializeBinary());
51
+ }
52
+
53
+ function deserialize_ondewo_sip_SipCallAudioResponse(buffer_arg) {
54
+ return ondewo_sip_sip_pb.SipCallAudioResponse.deserializeBinary(new Uint8Array(buffer_arg));
55
+ }
56
+
35
57
  function serialize_ondewo_sip_SipEndCallRequest(arg) {
36
58
  if (!(arg instanceof ondewo_sip_sip_pb.SipEndCallRequest)) {
37
59
  throw new Error('Expected argument of type ondewo.sip.SipEndCallRequest');
@@ -65,6 +87,28 @@ function deserialize_ondewo_sip_SipRegisterAccountRequest(buffer_arg) {
65
87
  return ondewo_sip_sip_pb.SipRegisterAccountRequest.deserializeBinary(new Uint8Array(buffer_arg));
66
88
  }
67
89
 
90
+ function serialize_ondewo_sip_SipReportAnsweringMachineDetectedRequest(arg) {
91
+ if (!(arg instanceof ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest)) {
92
+ throw new Error('Expected argument of type ondewo.sip.SipReportAnsweringMachineDetectedRequest');
93
+ }
94
+ return Buffer.from(arg.serializeBinary());
95
+ }
96
+
97
+ function deserialize_ondewo_sip_SipReportAnsweringMachineDetectedRequest(buffer_arg) {
98
+ return ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest.deserializeBinary(new Uint8Array(buffer_arg));
99
+ }
100
+
101
+ function serialize_ondewo_sip_SipSetCallMediaControlRequest(arg) {
102
+ if (!(arg instanceof ondewo_sip_sip_pb.SipSetCallMediaControlRequest)) {
103
+ throw new Error('Expected argument of type ondewo.sip.SipSetCallMediaControlRequest');
104
+ }
105
+ return Buffer.from(arg.serializeBinary());
106
+ }
107
+
108
+ function deserialize_ondewo_sip_SipSetCallMediaControlRequest(buffer_arg) {
109
+ return ondewo_sip_sip_pb.SipSetCallMediaControlRequest.deserializeBinary(new Uint8Array(buffer_arg));
110
+ }
111
+
68
112
  function serialize_ondewo_sip_SipStartCallRequest(arg) {
69
113
  if (!(arg instanceof ondewo_sip_sip_pb.SipStartCallRequest)) {
70
114
  throw new Error('Expected argument of type ondewo.sip.SipStartCallRequest');
@@ -135,7 +179,8 @@ sipStartSession: {
135
179
  responseSerialize: serialize_ondewo_sip_SipStatus,
136
180
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
137
181
  },
138
- // <p>Ends a SIP session for an account registered at a SIP server</p>
182
+ // Not idempotent (no idempotency_level): (re)creates the SIP session and registration.
183
+ // <p>Ends a SIP session for an account registered at a SIP server</p>
139
184
  sipEndSession: {
140
185
  path: '/ondewo.sip.Sip/SipEndSession',
141
186
  requestStream: false,
@@ -147,7 +192,8 @@ sipEndSession: {
147
192
  responseSerialize: serialize_ondewo_sip_SipStatus,
148
193
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
149
194
  },
150
- // <p>Starts a call in an active SIP session for an account registered at a SIP server</p>
195
+ // Not idempotent (no idempotency_level): tears down the session; a repeat records a new status.
196
+ // <p>Starts a call in an active SIP session for an account registered at a SIP server</p>
151
197
  sipStartCall: {
152
198
  path: '/ondewo.sip.Sip/SipStartCall',
153
199
  requestStream: false,
@@ -159,7 +205,8 @@ sipStartCall: {
159
205
  responseSerialize: serialize_ondewo_sip_SipStatus,
160
206
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
161
207
  },
162
- // <p>Ends a call in an active SIP session for an account registered at a SIP server</p>
208
+ // Not idempotent (no idempotency_level): a repeat dials a second call.
209
+ // <p>Ends a call in an active SIP session for an account registered at a SIP server</p>
163
210
  sipEndCall: {
164
211
  path: '/ondewo.sip.Sip/SipEndCall',
165
212
  requestStream: false,
@@ -171,7 +218,20 @@ sipEndCall: {
171
218
  responseSerialize: serialize_ondewo_sip_SipStatus,
172
219
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
173
220
  },
174
- // <p>Transfers a call in an active SIP session for an account registered at a SIP server to another SIP account or phone number specified by <code>transfer_id</code></p>
221
+ // Not idempotent (no idempotency_level): a repeat without a call appends its refusal to the history and ends
222
+ // a one-shot caller container; unscoped it can end the next call.
223
+ // <p>Transfers a call in an active SIP session for an account registered at a SIP server to another SIP account or phone number specified by <code>transfer_id</code></p>
224
+ // <p>Call scoping: when the gRPC metadatum <code>x-ondewo-expected-call-id</code> is present it must equal
225
+ // <code>SipStatus.call_id</code> of the ongoing call, otherwise the request is refused with
226
+ // <code>exception_name=CallScopeMismatch</code> and nothing is assigned to the status. When it is absent the request is
227
+ // accepted for backward compatibility (unless the server requires call scoping).</p>
228
+ // <p>With <code>outcome_timeout_ms = 0</code> the call is transferred as before (REFER, then an immediate hangup).
229
+ // With <code>outcome_timeout_ms &gt; 0</code> see <code>SipTransferCallRequest.outcome_timeout_ms</code>.</p>
230
+ // <p>Refused while invited participants are present (see
231
+ // <code>SipSetCallMediaControlRequest.participants_present</code>): a REFER into a conference bridge transfers every
232
+ // party in it, the invited participant included. The refusal is RETURNED as <code>TRANSFER_CALL_FAILED</code> with
233
+ // <code>exception_name=ParticipantsPresent</code> and <code>description = reason=participants-present</code>; nothing
234
+ // is sent and the call is kept.</p>
175
235
  sipTransferCall: {
176
236
  path: '/ondewo.sip.Sip/SipTransferCall',
177
237
  requestStream: false,
@@ -183,7 +243,8 @@ sipTransferCall: {
183
243
  responseSerialize: serialize_ondewo_sip_SipStatus,
184
244
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
185
245
  },
186
- // <p>Registers s SIP account at a SIP server</p>
246
+ // Not idempotent (no idempotency_level): a repeat sends another REFER.
247
+ // <p>Registers s SIP account at a SIP server</p>
187
248
  sipRegisterAccount: {
188
249
  path: '/ondewo.sip.Sip/SipRegisterAccount',
189
250
  requestStream: false,
@@ -195,7 +256,8 @@ sipRegisterAccount: {
195
256
  responseSerialize: serialize_ondewo_sip_SipStatus,
196
257
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
197
258
  },
198
- // <p>Gets the current SIP status</p>
259
+ // Not idempotent (no idempotency_level): re-registers the account at the SIP server.
260
+ // <p>Gets the current SIP status</p>
199
261
  sipGetSipStatus: {
200
262
  path: '/ondewo.sip.Sip/SipGetSipStatus',
201
263
  requestStream: false,
@@ -220,6 +282,8 @@ sipGetSipStatusHistory: {
220
282
  responseDeserialize: deserialize_ondewo_sip_SipStatusHistoryResponse,
221
283
  },
222
284
  // <p>Plays wav files during an ongoing call of an active SIP session</p>
285
+ // <p>Call scoping as for <code>SipTransferCall</code>: a present <code>x-ondewo-expected-call-id</code> metadatum must
286
+ // match <code>SipStatus.call_id</code>.</p>
223
287
  sipPlayWavFiles: {
224
288
  path: '/ondewo.sip.Sip/SipPlayWavFiles',
225
289
  requestStream: false,
@@ -231,7 +295,11 @@ sipPlayWavFiles: {
231
295
  responseSerialize: serialize_ondewo_sip_SipStatus,
232
296
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
233
297
  },
234
- // <p>Mutes the microphone in an ongoing call of an active SIP session</p>
298
+ // Not idempotent (no idempotency_level): a repeat plays the files again.
299
+ // <p>Mutes the microphone in an ongoing call of an active SIP session</p>
300
+ // <p>Call scoping as for <code>SipTransferCall</code>. Sent by the in-container speech-to-speech pipeline it mutes only
301
+ // the bot's own mixer slot; sent by a remote client it sets the operator mute of
302
+ // <code>SipSetCallMediaControl</code>, which the pipeline cannot undo.</p>
235
303
  sipMute: {
236
304
  path: '/ondewo.sip.Sip/SipMute',
237
305
  requestStream: false,
@@ -243,7 +311,9 @@ sipMute: {
243
311
  responseSerialize: serialize_ondewo_sip_SipStatus,
244
312
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
245
313
  },
246
- // <p>Un-mutes the microphone in an ongoing call of an active SIP session</p>
314
+ // Not idempotent (no idempotency_level): without a call it assigns NO_ONGOING_CALL and appends to the history.
315
+ // <p>Un-mutes the microphone in an ongoing call of an active SIP session</p>
316
+ // <p>Call scoping and the split between the pipeline's own mute and the operator mute as for <code>SipMute</code>.</p>
247
317
  sipUnMute: {
248
318
  path: '/ondewo.sip.Sip/SipUnMute',
249
319
  requestStream: false,
@@ -255,6 +325,71 @@ sipUnMute: {
255
325
  responseSerialize: serialize_ondewo_sip_SipStatus,
256
326
  responseDeserialize: deserialize_ondewo_sip_SipStatus,
257
327
  },
328
+ // Not idempotent (no idempotency_level): without a call it assigns NO_ONGOING_CALL and appends to the history.
329
+ // <p>Reports that answering machine detection reached a verdict on the ongoing outgoing call. Sets the status
330
+ // <code>OUTGOING_CALL_ANSWERING_MACHINE_DETECTED</code> carrying <code>amd_result</code>; the call stays up.</p>
331
+ // <p>Called by the speech-to-speech pipeline (ONDEWO-CSI) inside the same container, i.e. over loopback only.
332
+ // Refused, and the current status left untouched, when no outgoing call is connected: the returned
333
+ // <code>SipStatus</code> then carries the refusal in <code>exception_name</code> and <code>description</code></p>
334
+ sipReportAnsweringMachineDetected: {
335
+ path: '/ondewo.sip.Sip/SipReportAnsweringMachineDetected',
336
+ requestStream: false,
337
+ responseStream: false,
338
+ requestType: ondewo_sip_sip_pb.SipReportAnsweringMachineDetectedRequest,
339
+ responseType: ondewo_sip_sip_pb.SipStatus,
340
+ requestSerialize: serialize_ondewo_sip_SipReportAnsweringMachineDetectedRequest,
341
+ requestDeserialize: deserialize_ondewo_sip_SipReportAnsweringMachineDetectedRequest,
342
+ responseSerialize: serialize_ondewo_sip_SipStatus,
343
+ responseDeserialize: deserialize_ondewo_sip_SipStatus,
344
+ },
345
+ // Not idempotent (no idempotency_level): assigns a status and records answering machine detection telemetry.
346
+ // <p>Call-scoped operator media control of the ongoing call: mute the bot and/or pause its listening.</p>
347
+ // <p>Metadata REQUIRED: <code>x-ondewo-expected-call-id</code> (must equal <code>SipStatus.call_id</code> of the ongoing
348
+ // call) and <code>x-ondewo-sip-call-control-token</code> (the per-container call-control token).</p>
349
+ // <p>Every request sets a desired level per owner and never toggles; a repeat leaves the level unchanged. The bot is
350
+ // muted while ANY owner holds a mute, and its listening is paused while ANY owner holds a pause.</p>
351
+ // <p>Returns the live status with <code>call_id</code>, <code>bot_muted</code>, <code>listening_paused</code> and
352
+ // <code>call_audio_streams</code> filled. Refusals are RETURNED in <code>exception_name</code> /
353
+ // <code>description</code> (<code>CallScopeMismatch</code>, <code>CallControlUnauthenticated</code>,
354
+ // <code>NoOngoingCall</code>, <code>AmdInProgress</code>, <code>CsiMediaControlFailed</code>) and never assigned to
355
+ // the shared status. When the pipeline refuses or fails, a requested pause is rolled back and a requested mute is
356
+ // kept (the safe direction); the returned fields carry the actual level.</p>
357
+ sipSetCallMediaControl: {
358
+ path: '/ondewo.sip.Sip/SipSetCallMediaControl',
359
+ requestStream: false,
360
+ responseStream: false,
361
+ requestType: ondewo_sip_sip_pb.SipSetCallMediaControlRequest,
362
+ responseType: ondewo_sip_sip_pb.SipStatus,
363
+ requestSerialize: serialize_ondewo_sip_SipSetCallMediaControlRequest,
364
+ requestDeserialize: deserialize_ondewo_sip_SipSetCallMediaControlRequest,
365
+ responseSerialize: serialize_ondewo_sip_SipStatus,
366
+ responseDeserialize: deserialize_ondewo_sip_SipStatus,
367
+ },
368
+ // Deliberately unmarked although a repeat leaves the level unchanged: a retried attempt can land after a newer
369
+ // request of the same owner and restore a stale mute or pause.
370
+ // <p>Bidirectional live audio of the ongoing call.</p>
371
+ // <p>The first request MUST be <code>config</code> and must arrive within 2 seconds. Metadata as for
372
+ // <code>SipSetCallMediaControl</code>.</p>
373
+ // <p>LISTEN receives the caller (plus any conference participants) mixed with the bot. TALK sends the agent's audio to
374
+ // the caller; it REQUIRES <code>take_over</code>, i.e. the bot is muted and does not listen while the stream is
375
+ // connected, and in TALK the agent hears the caller only. Audio is LINEAR16 little-endian mono in 20 ms frames.</p>
376
+ // <p>gRPC status codes: <code>UNAUTHENTICATED</code> (token), <code>FAILED_PRECONDITION</code> (call id mismatch, no
377
+ // connected call, answering machine detection in progress, bot still speaking at TALK start),
378
+ // <code>INVALID_ARGUMENT</code> (missing or invalid <code>config</code>, wrong frame size),
379
+ // <code>RESOURCE_EXHAUSTED</code> (stream cap reached, a second TALK). A normal end sends one <code>ended</code>
380
+ // message and then OK.</p>
381
+ sipStreamCallAudio: {
382
+ path: '/ondewo.sip.Sip/SipStreamCallAudio',
383
+ requestStream: true,
384
+ responseStream: true,
385
+ requestType: ondewo_sip_sip_pb.SipCallAudioRequest,
386
+ responseType: ondewo_sip_sip_pb.SipCallAudioResponse,
387
+ requestSerialize: serialize_ondewo_sip_SipCallAudioRequest,
388
+ requestDeserialize: deserialize_ondewo_sip_SipCallAudioRequest,
389
+ responseSerialize: serialize_ondewo_sip_SipCallAudioResponse,
390
+ responseDeserialize: deserialize_ondewo_sip_SipCallAudioResponse,
391
+ },
392
+ // Not idempotent (no idempotency_level): a stream takes a slot and, in TALK, takes over the call.
258
393
  };
259
394
 
260
395
  exports.SipClient = grpc.makeGenericClientConstructor(SipService, 'Sip');