@rstreamlabs/tunnels 1.0.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 +108 -0
- package/dist/index.d.mts +4780 -0
- package/dist/index.d.ts +4780 -0
- package/dist/index.js +1365 -0
- package/dist/index.mjs +1287 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
RstreamAuthResource: () => RstreamAuthResource,
|
|
34
|
+
RstreamAuthRessource: () => RstreamAuthResource,
|
|
35
|
+
RstreamClient: () => RstreamTunnelsClient,
|
|
36
|
+
RstreamClientsResource: () => RstreamClientsResource,
|
|
37
|
+
RstreamClientsRessource: () => RstreamClientsResource,
|
|
38
|
+
RstreamTURNResource: () => RstreamTURNResource,
|
|
39
|
+
RstreamTunnelsClient: () => RstreamTunnelsClient,
|
|
40
|
+
RstreamTunnelsResource: () => RstreamTunnelsResource,
|
|
41
|
+
RstreamTunnelsRessource: () => RstreamTunnelsResource,
|
|
42
|
+
RstreamWebhookResource: () => RstreamWebhookResource,
|
|
43
|
+
RstreamWebhookRessource: () => RstreamWebhookResource,
|
|
44
|
+
Tunnels: () => RstreamTunnelsClient,
|
|
45
|
+
Watch: () => Watch,
|
|
46
|
+
authTokenPermissionsSchema: () => authTokenPermissionsSchema,
|
|
47
|
+
authTokenSchema: () => authTokenSchema,
|
|
48
|
+
authTokenScopesSchema: () => authTokenScopesSchema,
|
|
49
|
+
authTokenTunnelsScopesSchema: () => authTokenTunnelsScopesSchema,
|
|
50
|
+
clientSchema: () => clientSchema,
|
|
51
|
+
createAPITURNCredentials: () => createAPITURNCredentials,
|
|
52
|
+
createAPPTURNCredentials: () => createAPPTURNCredentials,
|
|
53
|
+
createAuthTokenFromClientCredentials: () => createAuthTokenFromClientCredentials,
|
|
54
|
+
createAuthTokenParamsSchema: () => createAuthTokenParamsSchema,
|
|
55
|
+
createAuthTokenResponseSchema: () => createAuthTokenResponseSchema,
|
|
56
|
+
createPATTURNCredentials: () => createPATTURNCredentials,
|
|
57
|
+
createTURNCredentials: () => createTURNCredentials,
|
|
58
|
+
eventSchema: () => eventSchema,
|
|
59
|
+
listClientsParamsSchema: () => listClientsParamsSchema,
|
|
60
|
+
listClientsResponseSchema: () => listClientsResponseSchema,
|
|
61
|
+
listTunnelsParamsSchema: () => listTunnelsParamsSchema,
|
|
62
|
+
listTunnelsResponseSchema: () => listTunnelsResponseSchema,
|
|
63
|
+
parseWebTTYServers: () => parseWebTTYServers,
|
|
64
|
+
resolveControlPlaneCredentials: () => resolveControlPlaneCredentials,
|
|
65
|
+
resolveTunnelsAPIURL: () => resolveTunnelsAPIURL,
|
|
66
|
+
resolveTunnelsCredentials: () => resolveTunnelsCredentials,
|
|
67
|
+
resolveTunnelsEngine: () => resolveTunnelsEngine,
|
|
68
|
+
streamSummarySchema: () => streamSummarySchema,
|
|
69
|
+
tokenSchema: () => tokenSchema,
|
|
70
|
+
tunnelSchema: () => tunnelSchema,
|
|
71
|
+
turnCredentialModeSchema: () => turnCredentialModeSchema,
|
|
72
|
+
webhookEventsSchema: () => webhookEventsSchema,
|
|
73
|
+
webttyServerSchema: () => webttyServerSchema,
|
|
74
|
+
wsEventsSchema: () => wsEventsSchema
|
|
75
|
+
});
|
|
76
|
+
module.exports = __toCommonJS(index_exports);
|
|
77
|
+
|
|
78
|
+
// src/auth-resource.ts
|
|
79
|
+
var import_rstream = require("@rstreamlabs/rstream");
|
|
80
|
+
var import_rstream2 = require("@rstreamlabs/rstream");
|
|
81
|
+
var RstreamAuthResource = class {
|
|
82
|
+
client;
|
|
83
|
+
constructor(client) {
|
|
84
|
+
this.client = client;
|
|
85
|
+
}
|
|
86
|
+
async createAuthToken(params, options) {
|
|
87
|
+
const credentials = options?.credentials || this.client.credentials;
|
|
88
|
+
if (!(0, import_rstream2.isClientCredentials)(credentials)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Application credentials (client id, client secret) are required to create an auth token."
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const engine = options?.engine ?? await this.client.getEngine().catch(() => void 0);
|
|
94
|
+
return createAuthTokenFromClientCredentials(credentials, params, {
|
|
95
|
+
engine
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
function createAuthTokenFromClientCredentials(credentials, params, options) {
|
|
100
|
+
const payload = {
|
|
101
|
+
metadata: {
|
|
102
|
+
engine: options?.engine,
|
|
103
|
+
scopes: params?.scopes
|
|
104
|
+
},
|
|
105
|
+
permissions: null,
|
|
106
|
+
project_id: params?.project_id,
|
|
107
|
+
workspace_id: params?.workspace_id
|
|
108
|
+
};
|
|
109
|
+
const { token } = (0, import_rstream.createClientCredentialsToken)(credentials, {
|
|
110
|
+
claims: payload,
|
|
111
|
+
expiresInSeconds: params?.expires_in
|
|
112
|
+
});
|
|
113
|
+
return { token };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/zod.ts
|
|
117
|
+
var z = __toESM(require("zod"));
|
|
118
|
+
var StringFilter = z.union([
|
|
119
|
+
z.string(),
|
|
120
|
+
z.object({ exact: z.string() }),
|
|
121
|
+
z.object({ oneof: z.array(z.string()) }),
|
|
122
|
+
z.object({ regex: z.string() })
|
|
123
|
+
]);
|
|
124
|
+
function transform(field) {
|
|
125
|
+
if (field instanceof z.ZodOptional) {
|
|
126
|
+
return z.optional(transform(field.unwrap()));
|
|
127
|
+
}
|
|
128
|
+
if (field instanceof z.ZodString) {
|
|
129
|
+
return z.optional(StringFilter);
|
|
130
|
+
}
|
|
131
|
+
if (field instanceof z.ZodRecord) {
|
|
132
|
+
return z.optional(z.record(field.keyType, transform(field.valueType)));
|
|
133
|
+
}
|
|
134
|
+
return z.optional(field);
|
|
135
|
+
}
|
|
136
|
+
function filters(base) {
|
|
137
|
+
const entries = Object.entries(base.shape).map(([key, field]) => [
|
|
138
|
+
key,
|
|
139
|
+
transform(field)
|
|
140
|
+
]);
|
|
141
|
+
const shape = Object.fromEntries(entries);
|
|
142
|
+
const node = z.lazy(
|
|
143
|
+
() => z.union([
|
|
144
|
+
z.object(shape),
|
|
145
|
+
z.object({ AND: z.array(node) }),
|
|
146
|
+
z.object({ OR: z.array(node) })
|
|
147
|
+
])
|
|
148
|
+
);
|
|
149
|
+
return node;
|
|
150
|
+
}
|
|
151
|
+
function select(base) {
|
|
152
|
+
const entries = Object.entries(base.shape).map(([key]) => {
|
|
153
|
+
return [key, z.boolean().optional()];
|
|
154
|
+
});
|
|
155
|
+
const shape = Object.fromEntries(entries);
|
|
156
|
+
return z.object(shape);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/tunnel.ts
|
|
160
|
+
var z2 = __toESM(require("zod"));
|
|
161
|
+
var tunnelSchema = z2.object({
|
|
162
|
+
// common properties
|
|
163
|
+
client_id: z2.string(),
|
|
164
|
+
user_id: z2.string().optional(),
|
|
165
|
+
workspace_id: z2.string().optional(),
|
|
166
|
+
project_id: z2.string().optional(),
|
|
167
|
+
cluster_id: z2.string().optional(),
|
|
168
|
+
plan: z2.string().optional(),
|
|
169
|
+
provider: z2.string().optional(),
|
|
170
|
+
region: z2.string().optional(),
|
|
171
|
+
status: z2.enum(["online", "offline"]),
|
|
172
|
+
// tunnel properties
|
|
173
|
+
id: z2.string(),
|
|
174
|
+
creation_date: z2.union([z2.string().datetime(), z2.date()]).optional(),
|
|
175
|
+
name: z2.string().optional(),
|
|
176
|
+
type: z2.enum(["bytestream", "datagram"]).optional(),
|
|
177
|
+
publish: z2.boolean().optional(),
|
|
178
|
+
protocol: z2.enum(["tls", "dtls", "quic", "http"]).optional(),
|
|
179
|
+
labels: z2.record(z2.string(), z2.string()).optional(),
|
|
180
|
+
geo_ip: z2.array(z2.string()).optional(),
|
|
181
|
+
trusted_ips: z2.array(z2.string()).optional(),
|
|
182
|
+
host: z2.string().optional(),
|
|
183
|
+
tls_mode: z2.enum(["passthrough", "terminated"]).optional(),
|
|
184
|
+
tls_alpns: z2.array(z2.string()).optional(),
|
|
185
|
+
tls_min_version: z2.string().optional(),
|
|
186
|
+
tls_ciphers: z2.array(z2.string()).optional(),
|
|
187
|
+
mtls: z2.boolean().optional(),
|
|
188
|
+
mtls_ca_cert_pem: z2.string().optional(),
|
|
189
|
+
http_version: z2.enum(["http/1.1", "h2", "h2c", "h3"]).optional(),
|
|
190
|
+
http_use_tls: z2.boolean().optional(),
|
|
191
|
+
token_auth: z2.boolean().optional(),
|
|
192
|
+
rstream_auth: z2.boolean().optional(),
|
|
193
|
+
challenge_mode: z2.boolean().optional()
|
|
194
|
+
});
|
|
195
|
+
var listTunnelsParamsSchema = z2.object({
|
|
196
|
+
limit: z2.number().optional(),
|
|
197
|
+
filters: tunnelSchema.pick({
|
|
198
|
+
status: true,
|
|
199
|
+
client_id: true,
|
|
200
|
+
protocol: true,
|
|
201
|
+
publish: true,
|
|
202
|
+
labels: true
|
|
203
|
+
}).partial().optional()
|
|
204
|
+
});
|
|
205
|
+
var listTunnelsResponseSchema = z2.array(tunnelSchema);
|
|
206
|
+
|
|
207
|
+
// src/auth.ts
|
|
208
|
+
var z3 = __toESM(require("zod"));
|
|
209
|
+
var authTokenPermissionsSchema = z3.array(z3.string());
|
|
210
|
+
var authTokenTunnelsScopesSchema = z3.object({
|
|
211
|
+
// Scopes for creating tunnels
|
|
212
|
+
create: z3.union([
|
|
213
|
+
z3.boolean(),
|
|
214
|
+
z3.object({
|
|
215
|
+
filters: filters(
|
|
216
|
+
tunnelSchema.omit({
|
|
217
|
+
client_id: true,
|
|
218
|
+
user_id: true,
|
|
219
|
+
workspace_id: true,
|
|
220
|
+
project_id: true,
|
|
221
|
+
cluster_id: true,
|
|
222
|
+
plan: true,
|
|
223
|
+
provider: true,
|
|
224
|
+
region: true,
|
|
225
|
+
status: true,
|
|
226
|
+
id: true,
|
|
227
|
+
creation_date: true
|
|
228
|
+
})
|
|
229
|
+
).optional()
|
|
230
|
+
})
|
|
231
|
+
]).optional(),
|
|
232
|
+
// Scopes for connecting to tunnels
|
|
233
|
+
connect: z3.union([
|
|
234
|
+
z3.boolean(),
|
|
235
|
+
z3.object({
|
|
236
|
+
filters: filters(tunnelSchema).optional(),
|
|
237
|
+
params: filters(
|
|
238
|
+
z3.object({
|
|
239
|
+
path: z3.string().optional()
|
|
240
|
+
})
|
|
241
|
+
).optional()
|
|
242
|
+
})
|
|
243
|
+
]).optional(),
|
|
244
|
+
// Scopes for listing tunnels
|
|
245
|
+
list: z3.union([
|
|
246
|
+
z3.boolean(),
|
|
247
|
+
z3.object({
|
|
248
|
+
filters: filters(tunnelSchema).optional(),
|
|
249
|
+
select: select(tunnelSchema).optional()
|
|
250
|
+
})
|
|
251
|
+
]).optional()
|
|
252
|
+
});
|
|
253
|
+
var authTokenScopesSchema = z3.object({
|
|
254
|
+
tunnels: authTokenTunnelsScopesSchema.optional()
|
|
255
|
+
// Scopes related to tunnels
|
|
256
|
+
});
|
|
257
|
+
var authTokenSchema = z3.union([
|
|
258
|
+
z3.object({
|
|
259
|
+
type: z3.literal("auth"),
|
|
260
|
+
userId: z3.string(),
|
|
261
|
+
permissions: authTokenPermissionsSchema.nullable()
|
|
262
|
+
}),
|
|
263
|
+
z3.object({
|
|
264
|
+
type: z3.literal("pat"),
|
|
265
|
+
token_endpoint: z3.string().optional()
|
|
266
|
+
}),
|
|
267
|
+
z3.object({
|
|
268
|
+
type: z3.literal("app"),
|
|
269
|
+
clientId: z3.string(),
|
|
270
|
+
permissions: authTokenPermissionsSchema.nullable()
|
|
271
|
+
})
|
|
272
|
+
]).and(
|
|
273
|
+
z3.object({
|
|
274
|
+
workspace_id: z3.string().optional(),
|
|
275
|
+
project_id: z3.string().optional(),
|
|
276
|
+
metadata: z3.object({
|
|
277
|
+
engine: z3.string().optional(),
|
|
278
|
+
scopes: authTokenScopesSchema.optional()
|
|
279
|
+
}).optional()
|
|
280
|
+
})
|
|
281
|
+
);
|
|
282
|
+
var tokenSchema = authTokenSchema;
|
|
283
|
+
var createAuthTokenParamsSchema = z3.object({
|
|
284
|
+
expires_in: z3.number().default(60),
|
|
285
|
+
// 1 minute
|
|
286
|
+
workspace_id: z3.string().optional(),
|
|
287
|
+
project_id: z3.string().optional(),
|
|
288
|
+
scopes: authTokenScopesSchema.optional(),
|
|
289
|
+
metadata: z3.unknown().optional()
|
|
290
|
+
// Additional metadata
|
|
291
|
+
});
|
|
292
|
+
var createAuthTokenResponseSchema = z3.object({
|
|
293
|
+
token: z3.string()
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// src/client.ts
|
|
297
|
+
var z4 = __toESM(require("zod"));
|
|
298
|
+
var clientSchema = z4.object({
|
|
299
|
+
id: z4.string(),
|
|
300
|
+
status: z4.enum(["online", "offline"]),
|
|
301
|
+
user_id: z4.string().optional(),
|
|
302
|
+
workspace_id: z4.string().optional(),
|
|
303
|
+
project_id: z4.string().optional(),
|
|
304
|
+
cluster_id: z4.string().optional(),
|
|
305
|
+
plan: z4.string().optional(),
|
|
306
|
+
provider: z4.string().optional(),
|
|
307
|
+
region: z4.string().optional(),
|
|
308
|
+
agent: z4.string().optional(),
|
|
309
|
+
channel: z4.string().optional(),
|
|
310
|
+
version: z4.string().optional(),
|
|
311
|
+
os: z4.string().optional(),
|
|
312
|
+
arch: z4.string().optional(),
|
|
313
|
+
protocol_version: z4.string().optional(),
|
|
314
|
+
labels: z4.record(z4.string(), z4.string()).optional()
|
|
315
|
+
});
|
|
316
|
+
var listClientsParamsSchema = z4.object({
|
|
317
|
+
limit: z4.number().optional(),
|
|
318
|
+
filters: clientSchema.pick({
|
|
319
|
+
labels: true
|
|
320
|
+
}).partial().optional()
|
|
321
|
+
});
|
|
322
|
+
var listClientsResponseSchema = z4.array(clientSchema);
|
|
323
|
+
|
|
324
|
+
// src/clients-resource.ts
|
|
325
|
+
var RstreamClientsResource = class {
|
|
326
|
+
client;
|
|
327
|
+
constructor(client) {
|
|
328
|
+
this.client = client;
|
|
329
|
+
}
|
|
330
|
+
async list(params) {
|
|
331
|
+
const path = params === void 0 ? "/clients" : `/clients?params=${encodeURIComponent(JSON.stringify(params))}`;
|
|
332
|
+
const response = await this.client.request(path, {
|
|
333
|
+
method: "GET"
|
|
334
|
+
});
|
|
335
|
+
return listClientsResponseSchema.parse(response);
|
|
336
|
+
}
|
|
337
|
+
async get(id) {
|
|
338
|
+
const response = await this.client.request(`/clients/${id}`, {
|
|
339
|
+
method: "GET"
|
|
340
|
+
});
|
|
341
|
+
return clientSchema.parse(response);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/stream.ts
|
|
346
|
+
var z5 = __toESM(require("zod"));
|
|
347
|
+
var nonEmptyStringSchema = z5.string().min(1);
|
|
348
|
+
var isoDateTimeSchema = z5.string().datetime();
|
|
349
|
+
var ipAddressSchema = z5.string();
|
|
350
|
+
var intNonNegSchema = z5.number().int().nonnegative();
|
|
351
|
+
var numNonNegSchema = z5.number().nonnegative();
|
|
352
|
+
var geolocationSchema = z5.object({
|
|
353
|
+
country: z5.string().optional(),
|
|
354
|
+
region: z5.string().optional()
|
|
355
|
+
});
|
|
356
|
+
var endpointSchema = z5.object({
|
|
357
|
+
ip: ipAddressSchema.optional(),
|
|
358
|
+
geolocation: geolocationSchema.optional()
|
|
359
|
+
});
|
|
360
|
+
var httpHeadersSchema = z5.record(
|
|
361
|
+
z5.string(),
|
|
362
|
+
z5.union([z5.string(), z5.array(z5.string())])
|
|
363
|
+
);
|
|
364
|
+
var httpProtocolSchema = z5.enum(["http/1.1", "h2", "h2c", "h3"]);
|
|
365
|
+
var httpRequestSchema = z5.object({
|
|
366
|
+
method: nonEmptyStringSchema,
|
|
367
|
+
scheme: z5.string().min(1).optional(),
|
|
368
|
+
host: nonEmptyStringSchema.optional(),
|
|
369
|
+
path: nonEmptyStringSchema.optional(),
|
|
370
|
+
query: z5.string().optional(),
|
|
371
|
+
url: nonEmptyStringSchema.optional(),
|
|
372
|
+
headers: httpHeadersSchema.optional(),
|
|
373
|
+
userAgent: z5.string().optional()
|
|
374
|
+
});
|
|
375
|
+
var httpResponseSchema = z5.object({
|
|
376
|
+
status: z5.number().int().min(100).max(999),
|
|
377
|
+
headers: httpHeadersSchema.optional()
|
|
378
|
+
});
|
|
379
|
+
var httpRequestStageSchema = z5.object({
|
|
380
|
+
protocol: httpProtocolSchema.optional(),
|
|
381
|
+
request: httpRequestSchema
|
|
382
|
+
});
|
|
383
|
+
var httpResponseStageSchema = z5.object({
|
|
384
|
+
protocol: httpProtocolSchema.optional(),
|
|
385
|
+
response: httpResponseSchema
|
|
386
|
+
});
|
|
387
|
+
var tokenSourceSchema = z5.enum([
|
|
388
|
+
"authorization",
|
|
389
|
+
"query",
|
|
390
|
+
"client",
|
|
391
|
+
"unknown"
|
|
392
|
+
]);
|
|
393
|
+
var tokenTypeSchema = z5.enum(["auth", "pat", "app", "unknown"]);
|
|
394
|
+
var firewallRuleSchema = z5.discriminatedUnion("kind", [
|
|
395
|
+
z5.object({ kind: z5.literal("none") }),
|
|
396
|
+
z5.object({ kind: z5.literal("mtls") }),
|
|
397
|
+
z5.object({
|
|
398
|
+
kind: z5.literal("rstream_auth"),
|
|
399
|
+
source: z5.literal("rstream_auth")
|
|
400
|
+
}),
|
|
401
|
+
z5.object({
|
|
402
|
+
kind: z5.literal("token"),
|
|
403
|
+
source: tokenSourceSchema.optional(),
|
|
404
|
+
tokenType: tokenTypeSchema.optional()
|
|
405
|
+
})
|
|
406
|
+
]);
|
|
407
|
+
var unauthorizedSourceSchema = z5.object({
|
|
408
|
+
source: tokenSourceSchema
|
|
409
|
+
});
|
|
410
|
+
var unauthorizedReasonSchema = (shape) => z5.object(shape).merge(unauthorizedSourceSchema);
|
|
411
|
+
var unauthorizedSchema = z5.discriminatedUnion("reason", [
|
|
412
|
+
unauthorizedReasonSchema({
|
|
413
|
+
reason: z5.literal("missing_token")
|
|
414
|
+
}),
|
|
415
|
+
unauthorizedReasonSchema({
|
|
416
|
+
reason: z5.literal("invalid_token")
|
|
417
|
+
}),
|
|
418
|
+
unauthorizedReasonSchema({
|
|
419
|
+
reason: z5.literal("expired_token")
|
|
420
|
+
}),
|
|
421
|
+
unauthorizedReasonSchema({
|
|
422
|
+
reason: z5.literal("invalid_secret")
|
|
423
|
+
}),
|
|
424
|
+
unauthorizedReasonSchema({
|
|
425
|
+
reason: z5.literal("user_mismatch")
|
|
426
|
+
}),
|
|
427
|
+
unauthorizedReasonSchema({
|
|
428
|
+
reason: z5.literal("policy_denied"),
|
|
429
|
+
policy: z5.enum(["ip", "geo", "mtls", "challenge", "other"]),
|
|
430
|
+
value: z5.string().optional()
|
|
431
|
+
})
|
|
432
|
+
]);
|
|
433
|
+
var failureSchema = z5.discriminatedUnion("kind", [
|
|
434
|
+
z5.object({
|
|
435
|
+
kind: z5.literal("timeout"),
|
|
436
|
+
timeoutMs: intNonNegSchema.optional(),
|
|
437
|
+
message: z5.string().optional()
|
|
438
|
+
}),
|
|
439
|
+
z5.object({
|
|
440
|
+
kind: z5.literal("unauthorized"),
|
|
441
|
+
why: unauthorizedSchema,
|
|
442
|
+
message: z5.string().optional()
|
|
443
|
+
}),
|
|
444
|
+
z5.object({
|
|
445
|
+
kind: z5.literal("not_found"),
|
|
446
|
+
resource: z5.enum(["tunnel", "client", "route"]),
|
|
447
|
+
message: z5.string().optional()
|
|
448
|
+
}),
|
|
449
|
+
z5.object({
|
|
450
|
+
kind: z5.literal("refused"),
|
|
451
|
+
reason: z5.enum(["capacity", "policy", "unsupported", "other"]),
|
|
452
|
+
message: z5.string().optional()
|
|
453
|
+
}),
|
|
454
|
+
z5.object({ kind: z5.literal("io"), message: z5.string().optional() }),
|
|
455
|
+
z5.object({ kind: z5.literal("internal"), message: z5.string().optional() })
|
|
456
|
+
]);
|
|
457
|
+
var firewallSchema = z5.discriminatedUnion("decision", [
|
|
458
|
+
z5.object({ decision: z5.literal("allowed"), rule: firewallRuleSchema }),
|
|
459
|
+
z5.object({
|
|
460
|
+
decision: z5.literal("denied"),
|
|
461
|
+
rule: firewallRuleSchema,
|
|
462
|
+
failure: failureSchema
|
|
463
|
+
})
|
|
464
|
+
]);
|
|
465
|
+
var routedTunnelSchema = z5.object({
|
|
466
|
+
tunnelId: nonEmptyStringSchema.optional(),
|
|
467
|
+
tunnelName: z5.string().optional(),
|
|
468
|
+
protocol: z5.string().optional(),
|
|
469
|
+
published: z5.boolean().optional(),
|
|
470
|
+
type: z5.string().optional(),
|
|
471
|
+
tlsMode: z5.string().optional(),
|
|
472
|
+
httpVersion: z5.string().optional(),
|
|
473
|
+
httpUseTls: z5.boolean().optional(),
|
|
474
|
+
tokenAuth: z5.boolean().optional(),
|
|
475
|
+
mtls: z5.boolean().optional()
|
|
476
|
+
});
|
|
477
|
+
var routingSchema = z5.discriminatedUnion("decision", [
|
|
478
|
+
z5.object({ decision: z5.literal("routed"), tunnel: routedTunnelSchema }),
|
|
479
|
+
z5.object({ decision: z5.literal("not_routed"), failure: failureSchema })
|
|
480
|
+
]);
|
|
481
|
+
var streamConnectedMetricsSchema = z5.object({
|
|
482
|
+
establish_ms: numNonNegSchema,
|
|
483
|
+
duration_ms: numNonNegSchema,
|
|
484
|
+
upstream_bytes: intNonNegSchema,
|
|
485
|
+
downstream_bytes: intNonNegSchema
|
|
486
|
+
});
|
|
487
|
+
var streamFailedMetricsSchema = z5.object({
|
|
488
|
+
time_to_fail_ms: numNonNegSchema.optional(),
|
|
489
|
+
establish_ms: numNonNegSchema.optional()
|
|
490
|
+
});
|
|
491
|
+
var terminationSchema = z5.object({
|
|
492
|
+
by: z5.enum(["downstream", "upstream", "server", "unknown"]),
|
|
493
|
+
reason: z5.enum(["closed", "reset", "timeout", "error", "unknown"]),
|
|
494
|
+
message: z5.string().optional()
|
|
495
|
+
});
|
|
496
|
+
var responseConnectedSchema = z5.object({
|
|
497
|
+
outcome: z5.literal("connected"),
|
|
498
|
+
established_at: isoDateTimeSchema,
|
|
499
|
+
upstream: endpointSchema.optional(),
|
|
500
|
+
metrics: streamConnectedMetricsSchema,
|
|
501
|
+
termination: terminationSchema.optional(),
|
|
502
|
+
http: httpResponseStageSchema.optional()
|
|
503
|
+
});
|
|
504
|
+
var responseFailedSchema = z5.object({
|
|
505
|
+
outcome: z5.literal("failed"),
|
|
506
|
+
failure: failureSchema,
|
|
507
|
+
upstream: endpointSchema.optional(),
|
|
508
|
+
metrics: streamFailedMetricsSchema.optional(),
|
|
509
|
+
termination: terminationSchema.optional(),
|
|
510
|
+
http: httpResponseStageSchema.optional()
|
|
511
|
+
});
|
|
512
|
+
var responseSchema = z5.discriminatedUnion("outcome", [
|
|
513
|
+
responseConnectedSchema,
|
|
514
|
+
responseFailedSchema
|
|
515
|
+
]);
|
|
516
|
+
var streamSummarySchema = z5.object({
|
|
517
|
+
workspace_id: nonEmptyStringSchema.optional(),
|
|
518
|
+
project_id: nonEmptyStringSchema.optional(),
|
|
519
|
+
cluster_id: nonEmptyStringSchema.optional(),
|
|
520
|
+
user_id: nonEmptyStringSchema.optional(),
|
|
521
|
+
client_id: nonEmptyStringSchema.optional(),
|
|
522
|
+
tunnel_id: nonEmptyStringSchema.optional(),
|
|
523
|
+
stream_id: nonEmptyStringSchema,
|
|
524
|
+
created_at: isoDateTimeSchema,
|
|
525
|
+
terminated_at: isoDateTimeSchema,
|
|
526
|
+
request: z5.object({
|
|
527
|
+
downstream: endpointSchema,
|
|
528
|
+
http: httpRequestStageSchema.optional()
|
|
529
|
+
}),
|
|
530
|
+
firewall: firewallSchema,
|
|
531
|
+
routing: routingSchema,
|
|
532
|
+
response: responseSchema
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// src/event.ts
|
|
536
|
+
var z6 = __toESM(require("zod"));
|
|
537
|
+
var isoDateTimeSchema2 = z6.string().datetime();
|
|
538
|
+
var initialStateSchema = z6.object({
|
|
539
|
+
snapshot_at: isoDateTimeSchema2,
|
|
540
|
+
clients: z6.array(clientSchema),
|
|
541
|
+
tunnels: z6.array(tunnelSchema)
|
|
542
|
+
});
|
|
543
|
+
var eventBaseSchema = z6.object({
|
|
544
|
+
id: z6.string().optional(),
|
|
545
|
+
created_at: isoDateTimeSchema2.optional(),
|
|
546
|
+
user_id: z6.string().optional(),
|
|
547
|
+
workspace_id: z6.string().optional(),
|
|
548
|
+
project_id: z6.string().optional(),
|
|
549
|
+
cluster_id: z6.string().optional()
|
|
550
|
+
});
|
|
551
|
+
var withEventBase = (shape) => z6.object(shape).merge(eventBaseSchema).passthrough();
|
|
552
|
+
var stateInitialEventSchema = withEventBase({
|
|
553
|
+
type: z6.literal("state.initial"),
|
|
554
|
+
object: initialStateSchema
|
|
555
|
+
});
|
|
556
|
+
var streamSummaryEventSchema = withEventBase({
|
|
557
|
+
type: z6.literal("stream.summary"),
|
|
558
|
+
object: streamSummarySchema
|
|
559
|
+
});
|
|
560
|
+
var commonEventsSchema = [
|
|
561
|
+
withEventBase({
|
|
562
|
+
type: z6.literal("client.created"),
|
|
563
|
+
object: clientSchema
|
|
564
|
+
}),
|
|
565
|
+
withEventBase({
|
|
566
|
+
type: z6.literal("client.updated"),
|
|
567
|
+
object: clientSchema
|
|
568
|
+
}),
|
|
569
|
+
withEventBase({
|
|
570
|
+
type: z6.literal("client.deleted"),
|
|
571
|
+
object: clientSchema
|
|
572
|
+
}),
|
|
573
|
+
withEventBase({
|
|
574
|
+
type: z6.literal("tunnel.created"),
|
|
575
|
+
object: tunnelSchema
|
|
576
|
+
}),
|
|
577
|
+
withEventBase({
|
|
578
|
+
type: z6.literal("tunnel.updated"),
|
|
579
|
+
object: tunnelSchema
|
|
580
|
+
}),
|
|
581
|
+
withEventBase({
|
|
582
|
+
type: z6.literal("tunnel.deleted"),
|
|
583
|
+
object: tunnelSchema
|
|
584
|
+
})
|
|
585
|
+
];
|
|
586
|
+
var wsEventsSchema = z6.union([
|
|
587
|
+
stateInitialEventSchema,
|
|
588
|
+
...commonEventsSchema
|
|
589
|
+
]);
|
|
590
|
+
var webhookEventsSchema = z6.union([
|
|
591
|
+
...commonEventsSchema,
|
|
592
|
+
streamSummaryEventSchema
|
|
593
|
+
]);
|
|
594
|
+
var eventSchema = z6.union([wsEventsSchema, webhookEventsSchema]);
|
|
595
|
+
|
|
596
|
+
// src/resolution.ts
|
|
597
|
+
var import_rstream3 = require("@rstreamlabs/rstream");
|
|
598
|
+
var import_rstream4 = require("@rstreamlabs/rstream");
|
|
599
|
+
var import_rstream5 = require("@rstreamlabs/rstream");
|
|
600
|
+
var import_rstream6 = require("@rstreamlabs/rstream");
|
|
601
|
+
var import_rstream7 = require("@rstreamlabs/rstream");
|
|
602
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
603
|
+
function trimOptionalString(value) {
|
|
604
|
+
const normalized = value?.trim();
|
|
605
|
+
return normalized ? normalized : void 0;
|
|
606
|
+
}
|
|
607
|
+
function getEngineFromToken(token) {
|
|
608
|
+
if (!token) {
|
|
609
|
+
return void 0;
|
|
610
|
+
}
|
|
611
|
+
const parsed = authTokenSchema.safeParse(
|
|
612
|
+
import_jsonwebtoken.default.decode(token, { complete: false })
|
|
613
|
+
);
|
|
614
|
+
if (!parsed.success) {
|
|
615
|
+
return void 0;
|
|
616
|
+
}
|
|
617
|
+
return trimOptionalString(parsed.data.metadata?.engine);
|
|
618
|
+
}
|
|
619
|
+
function resolveTunnelsAPIURL(apiUrl) {
|
|
620
|
+
return trimOptionalString(apiUrl) ?? (0, import_rstream6.readEnvironment)().apiUrl ?? import_rstream3.DEFAULT_API_URL;
|
|
621
|
+
}
|
|
622
|
+
function resolveTunnelsCredentials(credentials) {
|
|
623
|
+
if (credentials !== void 0) {
|
|
624
|
+
return credentials;
|
|
625
|
+
}
|
|
626
|
+
const token = (0, import_rstream6.readEnvironment)().token;
|
|
627
|
+
if (!token) {
|
|
628
|
+
return void 0;
|
|
629
|
+
}
|
|
630
|
+
return { token };
|
|
631
|
+
}
|
|
632
|
+
function resolveControlPlaneCredentials(controlPlaneCredentials, credentials, authToken) {
|
|
633
|
+
if (controlPlaneCredentials !== void 0) {
|
|
634
|
+
return controlPlaneCredentials;
|
|
635
|
+
}
|
|
636
|
+
if (credentials !== void 0) {
|
|
637
|
+
return credentials;
|
|
638
|
+
}
|
|
639
|
+
if (authToken) {
|
|
640
|
+
return { token: authToken };
|
|
641
|
+
}
|
|
642
|
+
const environmentToken = (0, import_rstream6.readEnvironment)().token;
|
|
643
|
+
if (!environmentToken) {
|
|
644
|
+
return void 0;
|
|
645
|
+
}
|
|
646
|
+
return { token: environmentToken };
|
|
647
|
+
}
|
|
648
|
+
async function resolveTunnelsEngine(config) {
|
|
649
|
+
const explicitEngine = trimOptionalString(config.engine);
|
|
650
|
+
if (explicitEngine) {
|
|
651
|
+
return explicitEngine;
|
|
652
|
+
}
|
|
653
|
+
const environmentEngine = (0, import_rstream6.readEnvironment)().engine;
|
|
654
|
+
if (environmentEngine) {
|
|
655
|
+
return environmentEngine;
|
|
656
|
+
}
|
|
657
|
+
const token = trimOptionalString(config.token) ?? ((0, import_rstream5.isTokenCredentials)(config.credentials) ? config.credentials.token : void 0);
|
|
658
|
+
const tokenEngine = getEngineFromToken(token);
|
|
659
|
+
if (tokenEngine) {
|
|
660
|
+
return tokenEngine;
|
|
661
|
+
}
|
|
662
|
+
const projectEndpoint = trimOptionalString(config.projectEndpoint);
|
|
663
|
+
if (projectEndpoint) {
|
|
664
|
+
const credentials = resolveControlPlaneCredentials(
|
|
665
|
+
config.controlPlaneCredentials,
|
|
666
|
+
config.credentials,
|
|
667
|
+
token
|
|
668
|
+
);
|
|
669
|
+
if (credentials === void 0) {
|
|
670
|
+
throw new Error(
|
|
671
|
+
"Control-plane credentials are required to resolve a managed project endpoint."
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
const controlPlane = new import_rstream7.RstreamClient({
|
|
675
|
+
apiUrl: resolveTunnelsAPIURL(config.apiUrl),
|
|
676
|
+
credentials
|
|
677
|
+
});
|
|
678
|
+
const project = await controlPlane.tunnels.projects.resolveByEndpoint(projectEndpoint);
|
|
679
|
+
const engine = (0, import_rstream4.getTunnelsProjectEngine)(project);
|
|
680
|
+
if (engine) {
|
|
681
|
+
return engine;
|
|
682
|
+
}
|
|
683
|
+
throw new Error(
|
|
684
|
+
"Failed to resolve the engine address from the managed tunnels project."
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
throw new Error(
|
|
688
|
+
"Engine URL is not defined. Provide an engine, use RSTREAM_ENGINE, or configure a managed project endpoint with control-plane access."
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/tunnels-resource.ts
|
|
693
|
+
var RstreamTunnelsResource = class {
|
|
694
|
+
client;
|
|
695
|
+
constructor(client) {
|
|
696
|
+
this.client = client;
|
|
697
|
+
}
|
|
698
|
+
async list(params) {
|
|
699
|
+
const path = params === void 0 ? "/tunnels" : `/tunnels?params=${encodeURIComponent(JSON.stringify(params))}`;
|
|
700
|
+
const response = await this.client.request(path, {
|
|
701
|
+
method: "GET"
|
|
702
|
+
});
|
|
703
|
+
return listTunnelsResponseSchema.parse(response);
|
|
704
|
+
}
|
|
705
|
+
async get(id) {
|
|
706
|
+
const response = await this.client.request(`/tunnels/${id}`, {
|
|
707
|
+
method: "GET"
|
|
708
|
+
});
|
|
709
|
+
return tunnelSchema.parse(response);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// src/tunnels.ts
|
|
714
|
+
var import_rstream12 = require("@rstreamlabs/rstream");
|
|
715
|
+
|
|
716
|
+
// src/turn.ts
|
|
717
|
+
var import_rstream8 = require("@rstreamlabs/rstream");
|
|
718
|
+
var import_rstream9 = require("@rstreamlabs/rstream");
|
|
719
|
+
var import_rstream10 = require("@rstreamlabs/rstream");
|
|
720
|
+
var import_rstream11 = require("@rstreamlabs/rstream");
|
|
721
|
+
var z7 = __toESM(require("zod"));
|
|
722
|
+
var import_crypto = __toESM(require("crypto"));
|
|
723
|
+
var defaultTURNPort = 3478;
|
|
724
|
+
var defaultTURNSPort = 5349;
|
|
725
|
+
var defaultTURNCredentialTTLSeconds = 24 * 60 * 60;
|
|
726
|
+
var turnTokenClaimsSchema = z7.object({
|
|
727
|
+
token_endpoint: z7.string().optional(),
|
|
728
|
+
type: z7.string()
|
|
729
|
+
});
|
|
730
|
+
var turnCredentialModeSchema = z7.enum(["api", "app", "pat"]);
|
|
731
|
+
function normalizeOptionalString(value) {
|
|
732
|
+
const normalized = value?.trim();
|
|
733
|
+
return normalized ? normalized : void 0;
|
|
734
|
+
}
|
|
735
|
+
function normalizeClusterDomain(clusterDomain) {
|
|
736
|
+
const normalized = normalizeOptionalString(clusterDomain);
|
|
737
|
+
return normalized?.toLowerCase();
|
|
738
|
+
}
|
|
739
|
+
function normalizeNow(now) {
|
|
740
|
+
if (now instanceof Date) {
|
|
741
|
+
return Math.floor(now.getTime() / 1e3);
|
|
742
|
+
}
|
|
743
|
+
if (typeof now === "number" && Number.isFinite(now)) {
|
|
744
|
+
return Math.floor(now);
|
|
745
|
+
}
|
|
746
|
+
return Math.floor(Date.now() / 1e3);
|
|
747
|
+
}
|
|
748
|
+
function normalizeTTLSeconds(ttlSeconds) {
|
|
749
|
+
if (typeof ttlSeconds === "number" && Number.isFinite(ttlSeconds) && ttlSeconds > 0) {
|
|
750
|
+
return Math.floor(ttlSeconds);
|
|
751
|
+
}
|
|
752
|
+
return defaultTURNCredentialTTLSeconds;
|
|
753
|
+
}
|
|
754
|
+
function normalizeTURNPort(port) {
|
|
755
|
+
if (typeof port === "number" && Number.isFinite(port) && port > 0) {
|
|
756
|
+
return Math.floor(port);
|
|
757
|
+
}
|
|
758
|
+
return defaultTURNPort;
|
|
759
|
+
}
|
|
760
|
+
function normalizeTURNSPort(port) {
|
|
761
|
+
if (typeof port === "number" && Number.isFinite(port) && port > 0) {
|
|
762
|
+
return Math.floor(port);
|
|
763
|
+
}
|
|
764
|
+
return defaultTURNSPort;
|
|
765
|
+
}
|
|
766
|
+
function createTURNURLs(clusterDomain, turnPort, turnsPort) {
|
|
767
|
+
const normalizedTURNPort = normalizeTURNPort(turnPort);
|
|
768
|
+
const normalizedTURNSPort = normalizeTURNSPort(turnsPort);
|
|
769
|
+
return [
|
|
770
|
+
`turn:${clusterDomain}:${normalizedTURNPort}?transport=udp`,
|
|
771
|
+
`turn:${clusterDomain}:${normalizedTURNPort}?transport=tcp`,
|
|
772
|
+
`turns:${clusterDomain}:${normalizedTURNSPort}?transport=udp`,
|
|
773
|
+
`turns:${clusterDomain}:${normalizedTURNSPort}?transport=tcp`
|
|
774
|
+
];
|
|
775
|
+
}
|
|
776
|
+
function parseTURNTokenClaims(token) {
|
|
777
|
+
const parts = token.split(".");
|
|
778
|
+
const payloadPart = parts.at(1);
|
|
779
|
+
if (!payloadPart) {
|
|
780
|
+
throw new Error("Invalid token format.");
|
|
781
|
+
}
|
|
782
|
+
const payload = Buffer.from(payloadPart, "base64url").toString("utf8");
|
|
783
|
+
const parsed = turnTokenClaimsSchema.safeParse(JSON.parse(payload));
|
|
784
|
+
if (!parsed.success) {
|
|
785
|
+
throw new Error("Invalid token format.");
|
|
786
|
+
}
|
|
787
|
+
return parsed.data;
|
|
788
|
+
}
|
|
789
|
+
function deriveClusterDomainFromEngine(projectEndpoint, engine) {
|
|
790
|
+
const normalizedProjectEndpoint = normalizeOptionalString(projectEndpoint);
|
|
791
|
+
const normalizedEngine = normalizeOptionalString(engine);
|
|
792
|
+
if (!normalizedProjectEndpoint || !normalizedEngine) {
|
|
793
|
+
throw new Error(
|
|
794
|
+
"Failed to derive the cluster domain from the engine address."
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
const url = new URL(`https://${normalizedEngine}`);
|
|
798
|
+
const prefix = `${normalizedProjectEndpoint}.`;
|
|
799
|
+
if (!url.hostname.startsWith(prefix)) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
"Failed to derive the cluster domain from the engine address."
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
const clusterDomain = url.hostname.slice(prefix.length);
|
|
805
|
+
if (!clusterDomain) {
|
|
806
|
+
throw new Error(
|
|
807
|
+
"Failed to derive the cluster domain from the engine address."
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
return clusterDomain;
|
|
811
|
+
}
|
|
812
|
+
function requireOption(value, message) {
|
|
813
|
+
if (value) {
|
|
814
|
+
return value;
|
|
815
|
+
}
|
|
816
|
+
throw new Error(message);
|
|
817
|
+
}
|
|
818
|
+
function resolveMode(credentials, mode) {
|
|
819
|
+
if (mode !== void 0) {
|
|
820
|
+
return turnCredentialModeSchema.parse(mode);
|
|
821
|
+
}
|
|
822
|
+
if ((0, import_rstream8.isClientCredentials)(credentials)) {
|
|
823
|
+
return "app";
|
|
824
|
+
}
|
|
825
|
+
if ((0, import_rstream9.isTokenCredentials)(credentials)) {
|
|
826
|
+
const claims = parseTURNTokenClaims(credentials.token);
|
|
827
|
+
if (claims.type === "pat" && normalizeOptionalString(claims.token_endpoint)) {
|
|
828
|
+
return "pat";
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return "api";
|
|
832
|
+
}
|
|
833
|
+
async function loadTURNServerPublicKeyHex(clusterDomain, options) {
|
|
834
|
+
const configuredKey = normalizeOptionalString(options.keyringBaseUrl);
|
|
835
|
+
const keyringBaseURL = configuredKey ?? normalizeOptionalString(options.apiUrl) ?? "https://rstream.io";
|
|
836
|
+
const url = new URL(
|
|
837
|
+
`/keyrings/turn/${clusterDomain}.spki.der.hex`,
|
|
838
|
+
keyringBaseURL
|
|
839
|
+
);
|
|
840
|
+
const response = await fetch(url);
|
|
841
|
+
if (!response.ok) {
|
|
842
|
+
throw new Error("Failed to load TURN keyring.");
|
|
843
|
+
}
|
|
844
|
+
return (await response.text()).trim();
|
|
845
|
+
}
|
|
846
|
+
async function createAPITURNCredentials(options) {
|
|
847
|
+
const projectId = normalizeOptionalString(options.projectId);
|
|
848
|
+
const projectEndpoint = normalizeOptionalString(options.projectEndpoint);
|
|
849
|
+
if (!projectId && !projectEndpoint) {
|
|
850
|
+
throw new Error(
|
|
851
|
+
"Project ID or project endpoint is required for TURN API mode."
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
const client = new import_rstream10.RstreamClient({
|
|
855
|
+
apiUrl: options.apiUrl,
|
|
856
|
+
credentials: options.credentials
|
|
857
|
+
});
|
|
858
|
+
if (projectId) {
|
|
859
|
+
return await client.tunnels.projects.createTurnCredentials(projectId);
|
|
860
|
+
}
|
|
861
|
+
if (!projectEndpoint) {
|
|
862
|
+
throw new Error(
|
|
863
|
+
"Project ID or project endpoint is required for TURN API mode."
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
return await client.tunnels.projects.createTurnCredentialsByEndpoint(
|
|
867
|
+
projectEndpoint
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
function createPATTURNCredentials(options) {
|
|
871
|
+
const clusterDomain = normalizeClusterDomain(options.clusterDomain);
|
|
872
|
+
if (!clusterDomain) {
|
|
873
|
+
throw new Error("Cluster domain is required for TURN PAT mode.");
|
|
874
|
+
}
|
|
875
|
+
const projectEndpoint = normalizeOptionalString(options.projectEndpoint);
|
|
876
|
+
if (!projectEndpoint) {
|
|
877
|
+
throw new Error("Project endpoint is required for TURN PAT mode.");
|
|
878
|
+
}
|
|
879
|
+
const token = normalizeOptionalString(options.token);
|
|
880
|
+
if (!token) {
|
|
881
|
+
throw new Error("Token is required for TURN PAT mode.");
|
|
882
|
+
}
|
|
883
|
+
const claims = parseTURNTokenClaims(token);
|
|
884
|
+
if (claims.type !== "pat") {
|
|
885
|
+
throw new Error("TURN PAT mode requires a PAT token.");
|
|
886
|
+
}
|
|
887
|
+
const tokenEndpoint = normalizeOptionalString(claims.token_endpoint);
|
|
888
|
+
if (!tokenEndpoint) {
|
|
889
|
+
throw new Error(
|
|
890
|
+
"TURN PAT mode requires a PAT token carrying a token endpoint."
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
const ttlSeconds = normalizeTTLSeconds(options.ttlSeconds);
|
|
894
|
+
const exp = normalizeNow(options.now) + ttlSeconds;
|
|
895
|
+
const username = `v1:${exp}:pat:${projectEndpoint}:${tokenEndpoint}`;
|
|
896
|
+
const tokenHash = import_crypto.default.createHash("sha256").update(token, "utf8").digest();
|
|
897
|
+
const key = Buffer.from(
|
|
898
|
+
import_crypto.default.hkdfSync(
|
|
899
|
+
"sha256",
|
|
900
|
+
tokenHash,
|
|
901
|
+
Buffer.from(clusterDomain, "utf8"),
|
|
902
|
+
Buffer.from("turn-pat-v1", "utf8"),
|
|
903
|
+
32
|
|
904
|
+
)
|
|
905
|
+
);
|
|
906
|
+
const credential = import_crypto.default.createHmac("sha256", key).update(username, "utf8").digest("base64");
|
|
907
|
+
return import_rstream11.turnCredentialsSchema.parse({
|
|
908
|
+
credential,
|
|
909
|
+
ttl: ttlSeconds,
|
|
910
|
+
urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
|
|
911
|
+
username
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
async function createAPPTURNCredentials(options) {
|
|
915
|
+
const clusterDomain = normalizeClusterDomain(options.clusterDomain);
|
|
916
|
+
if (!clusterDomain) {
|
|
917
|
+
throw new Error("Cluster domain is required for TURN APP mode.");
|
|
918
|
+
}
|
|
919
|
+
const projectEndpoint = normalizeOptionalString(options.projectEndpoint);
|
|
920
|
+
if (!projectEndpoint) {
|
|
921
|
+
throw new Error("Project endpoint is required for TURN APP mode.");
|
|
922
|
+
}
|
|
923
|
+
const clientId = normalizeOptionalString(options.clientId);
|
|
924
|
+
if (!clientId) {
|
|
925
|
+
throw new Error("Client ID is required for TURN APP mode.");
|
|
926
|
+
}
|
|
927
|
+
const clientSecret = normalizeOptionalString(options.clientSecret);
|
|
928
|
+
if (!clientSecret) {
|
|
929
|
+
throw new Error("Client secret is required for TURN APP mode.");
|
|
930
|
+
}
|
|
931
|
+
const ttlSeconds = normalizeTTLSeconds(options.ttlSeconds);
|
|
932
|
+
const exp = normalizeNow(options.now) + ttlSeconds;
|
|
933
|
+
const username = `v1:${exp}:app:${projectEndpoint}:${clientId}`;
|
|
934
|
+
const clientPrivateKey = import_crypto.default.createPrivateKey({
|
|
935
|
+
key: Buffer.from(clientSecret, "hex"),
|
|
936
|
+
format: "der",
|
|
937
|
+
type: "pkcs8"
|
|
938
|
+
});
|
|
939
|
+
const serverPublicKeyHex = normalizeOptionalString(options.serverPublicKeyHex) ?? await loadTURNServerPublicKeyHex(clusterDomain, options);
|
|
940
|
+
const serverPublicKey = import_crypto.default.createPublicKey({
|
|
941
|
+
key: Buffer.from(serverPublicKeyHex, "hex"),
|
|
942
|
+
format: "der",
|
|
943
|
+
type: "spki"
|
|
944
|
+
});
|
|
945
|
+
const sharedSecret = import_crypto.default.diffieHellman({
|
|
946
|
+
privateKey: clientPrivateKey,
|
|
947
|
+
publicKey: serverPublicKey
|
|
948
|
+
});
|
|
949
|
+
const key = Buffer.from(
|
|
950
|
+
import_crypto.default.hkdfSync(
|
|
951
|
+
"sha256",
|
|
952
|
+
sharedSecret,
|
|
953
|
+
Buffer.from(clusterDomain, "utf8"),
|
|
954
|
+
Buffer.from("turn-app-v1", "utf8"),
|
|
955
|
+
32
|
|
956
|
+
)
|
|
957
|
+
);
|
|
958
|
+
const credential = import_crypto.default.createHmac("sha256", key).update(username, "utf8").digest("base64");
|
|
959
|
+
return import_rstream11.turnCredentialsSchema.parse({
|
|
960
|
+
credential,
|
|
961
|
+
ttl: ttlSeconds,
|
|
962
|
+
urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
|
|
963
|
+
username
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
async function createTURNCredentials(options) {
|
|
967
|
+
const credentials = options.credentials;
|
|
968
|
+
const mode = resolveMode(credentials, options.mode);
|
|
969
|
+
const projectEndpoint = normalizeOptionalString(options.projectEndpoint);
|
|
970
|
+
if (mode === "api") {
|
|
971
|
+
const controlPlaneCredentials = options.controlPlaneCredentials ?? credentials;
|
|
972
|
+
if (!controlPlaneCredentials) {
|
|
973
|
+
throw new Error(
|
|
974
|
+
"Control-plane credentials are required for TURN API mode."
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
return await createAPITURNCredentials({
|
|
978
|
+
apiUrl: options.apiUrl,
|
|
979
|
+
credentials: controlPlaneCredentials,
|
|
980
|
+
projectEndpoint,
|
|
981
|
+
projectId: options.projectId
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
const clusterDomain = normalizeClusterDomain(options.clusterDomain) ?? (projectEndpoint && options.engine ? deriveClusterDomainFromEngine(projectEndpoint, options.engine) : void 0);
|
|
985
|
+
if (mode === "pat") {
|
|
986
|
+
if (!(0, import_rstream9.isTokenCredentials)(credentials)) {
|
|
987
|
+
throw new Error("A PAT token is required for TURN PAT mode.");
|
|
988
|
+
}
|
|
989
|
+
return createPATTURNCredentials({
|
|
990
|
+
clusterDomain: requireOption(
|
|
991
|
+
clusterDomain,
|
|
992
|
+
"Cluster domain is required for TURN PAT mode."
|
|
993
|
+
),
|
|
994
|
+
now: options.now,
|
|
995
|
+
projectEndpoint: requireOption(
|
|
996
|
+
projectEndpoint,
|
|
997
|
+
"Project endpoint is required for TURN PAT mode."
|
|
998
|
+
),
|
|
999
|
+
token: credentials.token,
|
|
1000
|
+
ttlSeconds: options.ttlSeconds,
|
|
1001
|
+
turnPort: options.turnPort,
|
|
1002
|
+
turnsPort: options.turnsPort
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (!(0, import_rstream8.isClientCredentials)(credentials)) {
|
|
1006
|
+
throw new Error("Client credentials are required for TURN APP mode.");
|
|
1007
|
+
}
|
|
1008
|
+
return await createAPPTURNCredentials({
|
|
1009
|
+
apiUrl: options.apiUrl,
|
|
1010
|
+
clientId: credentials.clientId,
|
|
1011
|
+
clientSecret: credentials.clientSecret,
|
|
1012
|
+
clusterDomain: requireOption(
|
|
1013
|
+
clusterDomain,
|
|
1014
|
+
"Cluster domain is required for TURN APP mode."
|
|
1015
|
+
),
|
|
1016
|
+
keyringBaseUrl: options.keyringBaseUrl,
|
|
1017
|
+
now: options.now,
|
|
1018
|
+
projectEndpoint: requireOption(
|
|
1019
|
+
projectEndpoint,
|
|
1020
|
+
"Project endpoint is required for TURN APP mode."
|
|
1021
|
+
),
|
|
1022
|
+
serverPublicKeyHex: options.serverPublicKeyHex,
|
|
1023
|
+
ttlSeconds: options.ttlSeconds,
|
|
1024
|
+
turnPort: options.turnPort,
|
|
1025
|
+
turnsPort: options.turnsPort
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// src/turn-resource.ts
|
|
1030
|
+
var RstreamTURNResource = class {
|
|
1031
|
+
client;
|
|
1032
|
+
constructor(client) {
|
|
1033
|
+
this.client = client;
|
|
1034
|
+
}
|
|
1035
|
+
async createCredentials(options = {}) {
|
|
1036
|
+
const engine = options.engine ?? (options.clusterDomain === void 0 ? await this.client.getEngine().catch(() => void 0) : void 0);
|
|
1037
|
+
return await createTURNCredentials({
|
|
1038
|
+
apiUrl: options.apiUrl ?? this.client.apiUrl,
|
|
1039
|
+
clusterDomain: options.clusterDomain,
|
|
1040
|
+
controlPlaneCredentials: options.controlPlaneCredentials ?? this.client.controlPlaneCredentials,
|
|
1041
|
+
credentials: options.credentials ?? this.client.credentials,
|
|
1042
|
+
engine,
|
|
1043
|
+
keyringBaseUrl: options.keyringBaseUrl,
|
|
1044
|
+
mode: options.mode,
|
|
1045
|
+
now: options.now,
|
|
1046
|
+
projectEndpoint: options.projectEndpoint ?? this.client.projectEndpoint,
|
|
1047
|
+
projectId: options.projectId,
|
|
1048
|
+
serverPublicKeyHex: options.serverPublicKeyHex,
|
|
1049
|
+
ttlSeconds: options.ttlSeconds,
|
|
1050
|
+
turnPort: options.turnPort,
|
|
1051
|
+
turnsPort: options.turnsPort
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
// src/webhooks-resource.ts
|
|
1057
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
1058
|
+
var RstreamWebhookResource = class {
|
|
1059
|
+
async event(payload, header, secret, tolerance = 300, receivedAt = Date.now()) {
|
|
1060
|
+
const payloadBuffer = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
|
|
1061
|
+
const signatureHeader = Array.isArray(header) ? header[0] : header.toString();
|
|
1062
|
+
if (!signatureHeader) {
|
|
1063
|
+
throw new Error("No signature header");
|
|
1064
|
+
}
|
|
1065
|
+
const elements = signatureHeader.split(",").map((element) => element.trim());
|
|
1066
|
+
const timestamp = elements.find((element) => element.startsWith("t="))?.substring(2);
|
|
1067
|
+
const signatures = elements.filter((element) => element.startsWith("v1=")).map((element) => element.substring(3));
|
|
1068
|
+
if (!timestamp || signatures.length === 0) {
|
|
1069
|
+
throw new Error("Invalid signature header format");
|
|
1070
|
+
}
|
|
1071
|
+
const timestampNum = parseInt(timestamp, 10);
|
|
1072
|
+
const now = Math.floor(receivedAt / 1e3);
|
|
1073
|
+
if (Math.abs(now - timestampNum) > tolerance) {
|
|
1074
|
+
throw new Error(
|
|
1075
|
+
`Webhook signature timestamp outside tolerance: ${now}, ${timestampNum}`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
const signedPayload = `${timestamp}.${payloadBuffer.toString("utf8")}`;
|
|
1079
|
+
const expectedSignature = import_crypto2.default.createHmac("sha256", secret).update(signedPayload).digest("hex");
|
|
1080
|
+
const signatureMatched = signatures.some((signature) => {
|
|
1081
|
+
try {
|
|
1082
|
+
return import_crypto2.default.timingSafeEqual(
|
|
1083
|
+
Buffer.from(signature),
|
|
1084
|
+
Buffer.from(expectedSignature)
|
|
1085
|
+
);
|
|
1086
|
+
} catch {
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
if (!signatureMatched) {
|
|
1091
|
+
throw new Error("Signature verification failed");
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
return webhookEventsSchema.parse(
|
|
1095
|
+
JSON.parse(payloadBuffer.toString("utf8"))
|
|
1096
|
+
);
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
throw new Error(
|
|
1099
|
+
`Failed to parse webhook payload: ${error instanceof Error ? error.message : String(error)}`,
|
|
1100
|
+
{ cause: error }
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
// src/tunnels.ts
|
|
1107
|
+
var RstreamTunnelsClient = class {
|
|
1108
|
+
config;
|
|
1109
|
+
constructor(config) {
|
|
1110
|
+
this.config = config;
|
|
1111
|
+
}
|
|
1112
|
+
get apiUrl() {
|
|
1113
|
+
return resolveTunnelsAPIURL(this.config?.apiUrl);
|
|
1114
|
+
}
|
|
1115
|
+
get credentials() {
|
|
1116
|
+
return resolveTunnelsCredentials(this.config?.credentials);
|
|
1117
|
+
}
|
|
1118
|
+
get controlPlaneCredentials() {
|
|
1119
|
+
return resolveControlPlaneCredentials(
|
|
1120
|
+
this.config?.controlPlaneCredentials,
|
|
1121
|
+
this.credentials
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
get projectEndpoint() {
|
|
1125
|
+
return this.config?.projectEndpoint;
|
|
1126
|
+
}
|
|
1127
|
+
async getEngine() {
|
|
1128
|
+
return await resolveTunnelsEngine({
|
|
1129
|
+
apiUrl: this.config?.apiUrl,
|
|
1130
|
+
controlPlaneCredentials: this.controlPlaneCredentials,
|
|
1131
|
+
credentials: this.credentials,
|
|
1132
|
+
engine: this.config?.engine,
|
|
1133
|
+
projectEndpoint: this.config?.projectEndpoint
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
get auth() {
|
|
1137
|
+
return new RstreamAuthResource(this);
|
|
1138
|
+
}
|
|
1139
|
+
get clients() {
|
|
1140
|
+
return new RstreamClientsResource(this);
|
|
1141
|
+
}
|
|
1142
|
+
get tunnels() {
|
|
1143
|
+
return new RstreamTunnelsResource(this);
|
|
1144
|
+
}
|
|
1145
|
+
get webhooks() {
|
|
1146
|
+
return new RstreamWebhookResource();
|
|
1147
|
+
}
|
|
1148
|
+
get turn() {
|
|
1149
|
+
return new RstreamTURNResource(this);
|
|
1150
|
+
}
|
|
1151
|
+
async getToken(engine) {
|
|
1152
|
+
const credentials = this.credentials;
|
|
1153
|
+
if (!credentials) {
|
|
1154
|
+
return void 0;
|
|
1155
|
+
}
|
|
1156
|
+
if (!(0, import_rstream12.isClientCredentials)(credentials)) {
|
|
1157
|
+
return credentials.token;
|
|
1158
|
+
}
|
|
1159
|
+
return (await this.auth.createAuthToken(void 0, {
|
|
1160
|
+
credentials,
|
|
1161
|
+
engine
|
|
1162
|
+
})).token;
|
|
1163
|
+
}
|
|
1164
|
+
async request(path, options) {
|
|
1165
|
+
const engine = await this.getEngine();
|
|
1166
|
+
const url = `https://${engine}/api${path}`;
|
|
1167
|
+
const headers = new Headers(options?.headers || {});
|
|
1168
|
+
const token = await this.getToken(engine);
|
|
1169
|
+
if (token) {
|
|
1170
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1171
|
+
}
|
|
1172
|
+
const response = await fetch(url, {
|
|
1173
|
+
...options,
|
|
1174
|
+
headers
|
|
1175
|
+
});
|
|
1176
|
+
if (!response.ok) {
|
|
1177
|
+
const errorText = await response.text();
|
|
1178
|
+
throw new Error(`HTTP error ${response.status}: ${errorText}`);
|
|
1179
|
+
}
|
|
1180
|
+
return await response.json();
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
// src/watch.ts
|
|
1185
|
+
var Watch = class {
|
|
1186
|
+
connection = null;
|
|
1187
|
+
connectionState = "preparing";
|
|
1188
|
+
config;
|
|
1189
|
+
events;
|
|
1190
|
+
constructor(config, events) {
|
|
1191
|
+
this.config = config;
|
|
1192
|
+
this.events = events;
|
|
1193
|
+
}
|
|
1194
|
+
async connect() {
|
|
1195
|
+
if (this.connectionState !== "preparing") {
|
|
1196
|
+
throw new Error("Watch: Connection already started or closed.");
|
|
1197
|
+
}
|
|
1198
|
+
this.connectionState = "connecting";
|
|
1199
|
+
const token = typeof this.config.auth === "function" ? await this.config.auth() : this.config.auth;
|
|
1200
|
+
const engine = await resolveTunnelsEngine({
|
|
1201
|
+
apiUrl: this.config.apiUrl,
|
|
1202
|
+
controlPlaneCredentials: this.config.controlPlaneCredentials,
|
|
1203
|
+
engine: this.config.engine,
|
|
1204
|
+
projectEndpoint: this.config.projectEndpoint,
|
|
1205
|
+
token
|
|
1206
|
+
});
|
|
1207
|
+
const transport = this.config.transport ?? "sse";
|
|
1208
|
+
const base = `https://${engine}`;
|
|
1209
|
+
if (transport === "sse") {
|
|
1210
|
+
const url = new URL(`/api/sse`, base);
|
|
1211
|
+
url.searchParams.set("rstream.token", token);
|
|
1212
|
+
this.connection = new EventSource(url.toString());
|
|
1213
|
+
} else {
|
|
1214
|
+
const url = new URL(`/api/websocket`, base);
|
|
1215
|
+
url.searchParams.set("rstream.token", token);
|
|
1216
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
1217
|
+
this.connection = new WebSocket(url.toString());
|
|
1218
|
+
}
|
|
1219
|
+
this.connection.onopen = () => {
|
|
1220
|
+
this.connectionState = "connected";
|
|
1221
|
+
this.events.onConnect?.();
|
|
1222
|
+
};
|
|
1223
|
+
this.connection.onmessage = (msg) => {
|
|
1224
|
+
const parsed = wsEventsSchema.parse(JSON.parse(msg.data));
|
|
1225
|
+
this.events.onEvent?.(parsed);
|
|
1226
|
+
};
|
|
1227
|
+
this.connection.onerror = () => {
|
|
1228
|
+
if (this.connectionState !== "closed") {
|
|
1229
|
+
this.disconnect();
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
if (this.connection instanceof WebSocket) {
|
|
1233
|
+
this.connection.onclose = () => {
|
|
1234
|
+
if (this.connectionState !== "closed") {
|
|
1235
|
+
this.disconnect();
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
disconnect() {
|
|
1241
|
+
if (this.connection) {
|
|
1242
|
+
this.connection.onerror = null;
|
|
1243
|
+
this.connection.onmessage = null;
|
|
1244
|
+
this.connection.onopen = null;
|
|
1245
|
+
this.connection.close();
|
|
1246
|
+
this.connection = null;
|
|
1247
|
+
}
|
|
1248
|
+
if (this.connectionState !== "closed") {
|
|
1249
|
+
this.connectionState = "closed";
|
|
1250
|
+
this.events.onClose?.();
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
// src/webtty.ts
|
|
1256
|
+
var z8 = __toESM(require("zod"));
|
|
1257
|
+
var osFamilies = [
|
|
1258
|
+
"linux",
|
|
1259
|
+
"macos",
|
|
1260
|
+
"windows",
|
|
1261
|
+
"netbsd",
|
|
1262
|
+
"openbsd",
|
|
1263
|
+
"freebsd"
|
|
1264
|
+
];
|
|
1265
|
+
var webttyServerSchema = z8.object({
|
|
1266
|
+
tunnel_id: z8.string(),
|
|
1267
|
+
host: z8.string(),
|
|
1268
|
+
token_auth: z8.boolean(),
|
|
1269
|
+
os_family: z8.enum(osFamilies).optional(),
|
|
1270
|
+
arch: z8.string().optional(),
|
|
1271
|
+
os_id: z8.string().optional(),
|
|
1272
|
+
// /etc/os-release::ID (ubuntu, debian, rocky, etc.)
|
|
1273
|
+
os_version_id: z8.string().optional(),
|
|
1274
|
+
// /etc/os-release::VERSION_ID (24.04, 22.04, 11, 10.0.19045, ...)
|
|
1275
|
+
os_version_codename: z8.string().optional(),
|
|
1276
|
+
// /etc/os-release::VERSION_CODENAME (jammy, noble...)
|
|
1277
|
+
os_pretty_name: z8.string().optional(),
|
|
1278
|
+
// /etc/os-release::PRETTY_NAME
|
|
1279
|
+
kernel_release: z8.string().optional(),
|
|
1280
|
+
// uname -r
|
|
1281
|
+
hostname: z8.string().optional(),
|
|
1282
|
+
// uname -n
|
|
1283
|
+
labels: z8.record(z8.string(), z8.string().optional()).optional()
|
|
1284
|
+
});
|
|
1285
|
+
function parser(tunnel) {
|
|
1286
|
+
if (tunnel.status !== "online") return null;
|
|
1287
|
+
if (tunnel.publish !== true) return null;
|
|
1288
|
+
if (tunnel.protocol !== "http") return null;
|
|
1289
|
+
const tunnelLabels = tunnel.labels ?? {};
|
|
1290
|
+
if (tunnelLabels["application-protocol"] !== "rstream.webtty") {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
const labels = {};
|
|
1294
|
+
for (const [key, value] of Object.entries(tunnelLabels)) {
|
|
1295
|
+
if (!key.startsWith("rstream.webtty.label.")) continue;
|
|
1296
|
+
const labelKey = key.slice("rstream.webtty.label.".length);
|
|
1297
|
+
if (labelKey.length === 0) continue;
|
|
1298
|
+
labels[labelKey] = value;
|
|
1299
|
+
}
|
|
1300
|
+
const candidate = {
|
|
1301
|
+
tunnel_id: tunnel.id,
|
|
1302
|
+
host: tunnel.host,
|
|
1303
|
+
token_auth: tunnel.token_auth === true,
|
|
1304
|
+
os_family: tunnelLabels["rstream.webtty.os_family"],
|
|
1305
|
+
arch: tunnelLabels["rstream.webtty.arch"],
|
|
1306
|
+
os_id: tunnelLabels["rstream.webtty.os_id"],
|
|
1307
|
+
os_version_id: tunnelLabels["rstream.webtty.os_version_id"],
|
|
1308
|
+
os_version_codename: tunnelLabels["rstream.webtty.os_version_codename"],
|
|
1309
|
+
os_pretty_name: tunnelLabels["rstream.webtty.os_pretty_name"],
|
|
1310
|
+
kernel_release: tunnelLabels["rstream.webtty.kernel_release"],
|
|
1311
|
+
hostname: tunnelLabels["rstream.webtty.hostname"],
|
|
1312
|
+
labels: Object.keys(labels).length > 0 ? labels : void 0
|
|
1313
|
+
};
|
|
1314
|
+
const parsed = webttyServerSchema.safeParse(candidate);
|
|
1315
|
+
if (!parsed.success) return null;
|
|
1316
|
+
return parsed.data;
|
|
1317
|
+
}
|
|
1318
|
+
function parseWebTTYServers(tunnels) {
|
|
1319
|
+
return tunnels.map((tunnel) => parser(tunnel)).filter((server) => server !== null);
|
|
1320
|
+
}
|
|
1321
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1322
|
+
0 && (module.exports = {
|
|
1323
|
+
RstreamAuthResource,
|
|
1324
|
+
RstreamAuthRessource,
|
|
1325
|
+
RstreamClient,
|
|
1326
|
+
RstreamClientsResource,
|
|
1327
|
+
RstreamClientsRessource,
|
|
1328
|
+
RstreamTURNResource,
|
|
1329
|
+
RstreamTunnelsClient,
|
|
1330
|
+
RstreamTunnelsResource,
|
|
1331
|
+
RstreamTunnelsRessource,
|
|
1332
|
+
RstreamWebhookResource,
|
|
1333
|
+
RstreamWebhookRessource,
|
|
1334
|
+
Tunnels,
|
|
1335
|
+
Watch,
|
|
1336
|
+
authTokenPermissionsSchema,
|
|
1337
|
+
authTokenSchema,
|
|
1338
|
+
authTokenScopesSchema,
|
|
1339
|
+
authTokenTunnelsScopesSchema,
|
|
1340
|
+
clientSchema,
|
|
1341
|
+
createAPITURNCredentials,
|
|
1342
|
+
createAPPTURNCredentials,
|
|
1343
|
+
createAuthTokenFromClientCredentials,
|
|
1344
|
+
createAuthTokenParamsSchema,
|
|
1345
|
+
createAuthTokenResponseSchema,
|
|
1346
|
+
createPATTURNCredentials,
|
|
1347
|
+
createTURNCredentials,
|
|
1348
|
+
eventSchema,
|
|
1349
|
+
listClientsParamsSchema,
|
|
1350
|
+
listClientsResponseSchema,
|
|
1351
|
+
listTunnelsParamsSchema,
|
|
1352
|
+
listTunnelsResponseSchema,
|
|
1353
|
+
parseWebTTYServers,
|
|
1354
|
+
resolveControlPlaneCredentials,
|
|
1355
|
+
resolveTunnelsAPIURL,
|
|
1356
|
+
resolveTunnelsCredentials,
|
|
1357
|
+
resolveTunnelsEngine,
|
|
1358
|
+
streamSummarySchema,
|
|
1359
|
+
tokenSchema,
|
|
1360
|
+
tunnelSchema,
|
|
1361
|
+
turnCredentialModeSchema,
|
|
1362
|
+
webhookEventsSchema,
|
|
1363
|
+
webttyServerSchema,
|
|
1364
|
+
wsEventsSchema
|
|
1365
|
+
});
|