@elizaos/plugin-streaming 2.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/api/stream-persistence.d.ts +68 -0
- package/dist/api/stream-persistence.js +181 -0
- package/dist/api/stream-persistence.js.map +1 -0
- package/dist/api/stream-route-state.d.ts +59 -0
- package/dist/api/stream-route-state.js +1 -0
- package/dist/api/stream-route-state.js.map +1 -0
- package/dist/api/stream-routes.d.ts +43 -0
- package/dist/api/stream-routes.js +609 -0
- package/dist/api/stream-routes.js.map +1 -0
- package/dist/api/streaming-text.d.ts +10 -0
- package/dist/api/streaming-text.js +88 -0
- package/dist/api/streaming-text.js.map +1 -0
- package/dist/api/streaming-types.d.ts +2 -0
- package/dist/api/streaming-types.js +1 -0
- package/dist/api/streaming-types.js.map +1 -0
- package/dist/api/tts-routes.d.ts +25 -0
- package/dist/api/tts-routes.js +158 -0
- package/dist/api/tts-routes.js.map +1 -0
- package/dist/core.d.ts +164 -0
- package/dist/core.js +495 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/services/stream-manager.d.ts +124 -0
- package/dist/services/stream-manager.js +531 -0
- package/dist/services/stream-manager.js.map +1 -0
- package/dist/services/tts-stream-bridge.d.ts +103 -0
- package/dist/services/tts-stream-bridge.js +327 -0
- package/dist/services/tts-stream-bridge.js.map +1 -0
- package/package.json +106 -0
package/dist/core.js
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { isCloudConnected } from "@elizaos/cloud-routing";
|
|
2
|
+
const WIDGET_DEFAULTS = {
|
|
3
|
+
"thought-bubble": {
|
|
4
|
+
position: { x: 2, y: 2, width: 30, height: 20 },
|
|
5
|
+
zIndex: 10
|
|
6
|
+
},
|
|
7
|
+
"action-ticker": {
|
|
8
|
+
position: { x: 0, y: 85, width: 100, height: 15 },
|
|
9
|
+
zIndex: 5
|
|
10
|
+
},
|
|
11
|
+
"alert-popup": {
|
|
12
|
+
position: { x: 30, y: 10, width: 40, height: 20 },
|
|
13
|
+
zIndex: 20
|
|
14
|
+
},
|
|
15
|
+
"viewer-count": {
|
|
16
|
+
position: { x: 88, y: 2, width: 10, height: 6 },
|
|
17
|
+
zIndex: 15
|
|
18
|
+
},
|
|
19
|
+
branding: { position: { x: 2, y: 90, width: 20, height: 8 }, zIndex: 2 },
|
|
20
|
+
"custom-html": {
|
|
21
|
+
position: { x: 50, y: 50, width: 30, height: 20 },
|
|
22
|
+
zIndex: 1
|
|
23
|
+
},
|
|
24
|
+
"peon-hud": {
|
|
25
|
+
position: { x: 82, y: 10, width: 16, height: 30 },
|
|
26
|
+
zIndex: 12
|
|
27
|
+
},
|
|
28
|
+
"peon-glass": {
|
|
29
|
+
position: { x: 2, y: 2, width: 32, height: 40 },
|
|
30
|
+
zIndex: 16
|
|
31
|
+
},
|
|
32
|
+
"peon-sakura": {
|
|
33
|
+
position: { x: 0, y: 0, width: 25, height: 50 },
|
|
34
|
+
zIndex: 3
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
let _presetCounter = 0;
|
|
38
|
+
function buildPresetLayout(name, enabledTypes) {
|
|
39
|
+
const enabledSet = new Set(enabledTypes);
|
|
40
|
+
const widgets = Object.entries(WIDGET_DEFAULTS).map(
|
|
41
|
+
([type, defaults]) => {
|
|
42
|
+
_presetCounter += 1;
|
|
43
|
+
return {
|
|
44
|
+
id: `preset${_presetCounter.toString(36)}`,
|
|
45
|
+
type,
|
|
46
|
+
enabled: enabledSet.has(type),
|
|
47
|
+
position: { ...defaults.position },
|
|
48
|
+
zIndex: defaults.zIndex,
|
|
49
|
+
config: {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return { version: 1, name, widgets };
|
|
54
|
+
}
|
|
55
|
+
function createNamedRtmpDestination(params) {
|
|
56
|
+
const trimmedId = params.id.trim();
|
|
57
|
+
const label = (params.name ?? trimmedId).trim() || trimmedId;
|
|
58
|
+
return {
|
|
59
|
+
id: trimmedId,
|
|
60
|
+
name: label,
|
|
61
|
+
async getCredentials() {
|
|
62
|
+
const rtmpUrl = params.rtmpUrl.trim();
|
|
63
|
+
const rtmpKey = params.rtmpKey.trim();
|
|
64
|
+
if (!rtmpUrl || !rtmpKey) {
|
|
65
|
+
throw new Error(`${label}: RTMP URL and stream key are required`);
|
|
66
|
+
}
|
|
67
|
+
return { rtmpUrl, rtmpKey };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function createCustomRtmpDestination(config) {
|
|
72
|
+
return {
|
|
73
|
+
id: "custom-rtmp",
|
|
74
|
+
name: "Custom RTMP",
|
|
75
|
+
async getCredentials() {
|
|
76
|
+
const rtmpUrl = (config?.rtmpUrl ?? process.env.CUSTOM_RTMP_URL ?? "").trim();
|
|
77
|
+
const rtmpKey = (config?.rtmpKey ?? process.env.CUSTOM_RTMP_KEY ?? "").trim();
|
|
78
|
+
if (!rtmpUrl || !rtmpKey) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"Custom RTMP requires rtmpUrl and rtmpKey in streaming.customRtmp config or CUSTOM_RTMP_* env"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return { rtmpUrl, rtmpKey };
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function createStreamingDestination(cfg, overrides) {
|
|
88
|
+
return {
|
|
89
|
+
id: cfg.platformId,
|
|
90
|
+
name: cfg.platformName,
|
|
91
|
+
defaultOverlayLayout: cfg.defaultOverlayLayout,
|
|
92
|
+
async getCredentials() {
|
|
93
|
+
const streamKey = (overrides?.streamKey ?? process.env[cfg.streamKeyEnvVar] ?? "").trim();
|
|
94
|
+
if (!streamKey) {
|
|
95
|
+
throw new Error(`${cfg.platformName} stream key not configured`);
|
|
96
|
+
}
|
|
97
|
+
const rtmpUrl = (overrides?.rtmpUrl ?? (cfg.rtmpUrlEnvVar ? process.env[cfg.rtmpUrlEnvVar] : void 0) ?? cfg.defaultRtmpUrl).trim();
|
|
98
|
+
if (!rtmpUrl) {
|
|
99
|
+
throw new Error(`${cfg.platformName} RTMP URL not configured`);
|
|
100
|
+
}
|
|
101
|
+
return { rtmpUrl, rtmpKey: streamKey };
|
|
102
|
+
}
|
|
103
|
+
// Platforms detect stream automatically via RTMP ingest -- no API calls needed
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const CLOUD_BASE_FALLBACK = "https://www.elizacloud.ai/api/v1";
|
|
107
|
+
function readSetting(runtime, key) {
|
|
108
|
+
const raw = runtime.getSetting(key);
|
|
109
|
+
if (raw === null || raw === void 0) return null;
|
|
110
|
+
const str = String(raw).trim();
|
|
111
|
+
return str.length > 0 ? str : null;
|
|
112
|
+
}
|
|
113
|
+
function getCloudBaseUrl(runtime) {
|
|
114
|
+
const override = readSetting(runtime, "ELIZAOS_CLOUD_BASE_URL");
|
|
115
|
+
return (override ?? CLOUD_BASE_FALLBACK).replace(/\/+$/, "");
|
|
116
|
+
}
|
|
117
|
+
function getCloudApiKey(runtime) {
|
|
118
|
+
const apiKey = readSetting(runtime, "ELIZAOS_CLOUD_API_KEY");
|
|
119
|
+
if (apiKey === null) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"Eliza Cloud relay requested but ELIZAOS_CLOUD_API_KEY is not set"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return apiKey;
|
|
125
|
+
}
|
|
126
|
+
function createCloudRelayDestination(cfg) {
|
|
127
|
+
if (!isCloudConnected(cfg.runtime)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Cloud relay requested for ${cfg.platformName} but Eliza Cloud is not connected (ELIZAOS_CLOUD_API_KEY missing or ELIZAOS_CLOUD_ENABLED falsy)`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
let activeSessionId = null;
|
|
133
|
+
return {
|
|
134
|
+
id: cfg.platformId,
|
|
135
|
+
name: cfg.platformName,
|
|
136
|
+
defaultOverlayLayout: cfg.defaultOverlayLayout,
|
|
137
|
+
async getCredentials() {
|
|
138
|
+
const baseUrl = getCloudBaseUrl(cfg.runtime);
|
|
139
|
+
const apiKey = getCloudApiKey(cfg.runtime);
|
|
140
|
+
const res = await fetch(`${baseUrl}/apis/streaming/sessions`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: {
|
|
143
|
+
"Content-Type": "application/json",
|
|
144
|
+
Authorization: `Bearer ${apiKey}`
|
|
145
|
+
},
|
|
146
|
+
body: JSON.stringify({ destinations: [cfg.platformId] }),
|
|
147
|
+
signal: AbortSignal.timeout(2e4)
|
|
148
|
+
});
|
|
149
|
+
if (!res.ok) {
|
|
150
|
+
const text = await res.text().catch(() => "");
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Cloud relay session create failed: ${res.status} ${text}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const body = await res.json();
|
|
156
|
+
if (!body.sessionId || !body.streamKey || !body.ingestUrl) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
"Cloud relay session create returned malformed response"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
activeSessionId = body.sessionId;
|
|
162
|
+
return { rtmpUrl: body.ingestUrl, rtmpKey: body.streamKey };
|
|
163
|
+
},
|
|
164
|
+
async onStreamStop() {
|
|
165
|
+
if (!activeSessionId) return;
|
|
166
|
+
const baseUrl = getCloudBaseUrl(cfg.runtime);
|
|
167
|
+
const apiKey = getCloudApiKey(cfg.runtime);
|
|
168
|
+
const sessionId = activeSessionId;
|
|
169
|
+
activeSessionId = null;
|
|
170
|
+
const res = await fetch(
|
|
171
|
+
`${baseUrl}/apis/streaming/sessions/${encodeURIComponent(sessionId)}`,
|
|
172
|
+
{
|
|
173
|
+
method: "DELETE",
|
|
174
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
175
|
+
signal: AbortSignal.timeout(15e3)
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
if (!res.ok && res.status !== 404) {
|
|
179
|
+
const text = await res.text().catch(() => "");
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Cloud relay session close failed: ${res.status} ${text}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function readBackendSetting(runtime, envVar) {
|
|
188
|
+
const raw = readSetting(runtime, envVar);
|
|
189
|
+
if (raw === null) return "auto";
|
|
190
|
+
const lower = raw.toLowerCase();
|
|
191
|
+
if (lower === "direct" || lower === "cloud" || lower === "auto") return lower;
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Invalid ${envVar}="${raw}" (expected "direct" | "cloud" | "auto")`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
function resolveStreamingBackend(runtime, cfg) {
|
|
197
|
+
const upper = cfg.platformId.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
198
|
+
const setting = readBackendSetting(runtime, `${upper}_STREAMING_BACKEND`);
|
|
199
|
+
if (setting === "direct" || setting === "cloud") return setting;
|
|
200
|
+
const localKey = readSetting(runtime, cfg.streamKeyEnvVar);
|
|
201
|
+
if (localKey !== null) return "direct";
|
|
202
|
+
return isCloudConnected(runtime) ? "cloud" : "direct";
|
|
203
|
+
}
|
|
204
|
+
function streamingPipelineLocalPort() {
|
|
205
|
+
return Number(process.env.SERVER_PORT || process.env.PORT || "2138");
|
|
206
|
+
}
|
|
207
|
+
const STREAMING_PLATFORMS = [
|
|
208
|
+
"twitch",
|
|
209
|
+
"youtube",
|
|
210
|
+
"x",
|
|
211
|
+
"pumpfun"
|
|
212
|
+
];
|
|
213
|
+
const PLATFORM_LABELS = {
|
|
214
|
+
twitch: "Twitch",
|
|
215
|
+
youtube: "YouTube",
|
|
216
|
+
x: "X (Twitter)",
|
|
217
|
+
pumpfun: "pump.fun"
|
|
218
|
+
};
|
|
219
|
+
function isStreamingPlatform(value) {
|
|
220
|
+
return typeof value === "string" && STREAMING_PLATFORMS.includes(value);
|
|
221
|
+
}
|
|
222
|
+
function isStreamingOp(value) {
|
|
223
|
+
return value === "start" || value === "stop" || value === "status";
|
|
224
|
+
}
|
|
225
|
+
function readParam(options, key) {
|
|
226
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
const handler = options;
|
|
230
|
+
const params = handler.parameters;
|
|
231
|
+
if (params && key in params) {
|
|
232
|
+
const v2 = params[key];
|
|
233
|
+
if (typeof v2 === "string" || typeof v2 === "number" || typeof v2 === "boolean" || v2 === null) {
|
|
234
|
+
return v2;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const flat = options;
|
|
238
|
+
const v = flat[key];
|
|
239
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null) {
|
|
240
|
+
return v;
|
|
241
|
+
}
|
|
242
|
+
return void 0;
|
|
243
|
+
}
|
|
244
|
+
async function fetchStreamStatus(platform) {
|
|
245
|
+
const port = streamingPipelineLocalPort();
|
|
246
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/stream/status`, {
|
|
247
|
+
signal: AbortSignal.timeout(1e4)
|
|
248
|
+
});
|
|
249
|
+
const data = await res.json();
|
|
250
|
+
const uptime = typeof data.uptime === "number" ? Number(data.uptime) : null;
|
|
251
|
+
const frames = typeof data.frames === "number" ? Number(data.frames) : null;
|
|
252
|
+
return {
|
|
253
|
+
platform,
|
|
254
|
+
running: !!data.running,
|
|
255
|
+
uptimeSeconds: uptime,
|
|
256
|
+
frames,
|
|
257
|
+
destination: PLATFORM_LABELS[platform]
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function buildStreamOpAction(params = {}) {
|
|
261
|
+
const validate = params.validate ?? (async () => true);
|
|
262
|
+
const platformParam = {
|
|
263
|
+
name: "platform",
|
|
264
|
+
description: "Streaming destination platform: twitch, youtube, x, or pumpfun.",
|
|
265
|
+
descriptionCompressed: "Platform: twitch|youtube|x|pumpfun.",
|
|
266
|
+
required: true,
|
|
267
|
+
schema: {
|
|
268
|
+
type: "string",
|
|
269
|
+
enum: [...STREAMING_PLATFORMS]
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const opParam = {
|
|
273
|
+
name: "subaction",
|
|
274
|
+
description: "Operation to perform: start (go live), stop (go offline), or status.",
|
|
275
|
+
descriptionCompressed: "Op: start|stop|status.",
|
|
276
|
+
required: true,
|
|
277
|
+
schema: {
|
|
278
|
+
type: "string",
|
|
279
|
+
enum: ["start", "stop", "status"]
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
return {
|
|
283
|
+
name: "STREAM",
|
|
284
|
+
contexts: ["media", "automation", "connectors"],
|
|
285
|
+
contextGate: { anyOf: ["media", "automation", "connectors"] },
|
|
286
|
+
roleGate: { minRole: "ADMIN" },
|
|
287
|
+
description: "Control the local RTMP streaming pipeline for a target platform. Dispatches start, stop, and status calls to the dashboard stream API for twitch, youtube, x, or pumpfun.",
|
|
288
|
+
descriptionCompressed: "Stream ops: start, stop, status; platforms: twitch, youtube, x, pumpfun.",
|
|
289
|
+
similes: [
|
|
290
|
+
"START_STREAM",
|
|
291
|
+
"STOP_STREAM",
|
|
292
|
+
"GET_STREAM_STATUS",
|
|
293
|
+
"GO_LIVE",
|
|
294
|
+
"GO_OFFLINE",
|
|
295
|
+
"STREAM_STATUS",
|
|
296
|
+
"IS_LIVE"
|
|
297
|
+
],
|
|
298
|
+
parameters: [platformParam, opParam],
|
|
299
|
+
validate,
|
|
300
|
+
handler: async (_runtime, _message, _state, options, callback) => {
|
|
301
|
+
const platformRaw = readParam(options, "platform");
|
|
302
|
+
const opRaw = readParam(options, "op");
|
|
303
|
+
if (!isStreamingPlatform(platformRaw)) {
|
|
304
|
+
const text = `STREAM_OP requires platform in {${STREAMING_PLATFORMS.join(", ")}}, got ${String(platformRaw)}`;
|
|
305
|
+
if (callback) await callback({ text, actions: [] });
|
|
306
|
+
return { success: false, error: text };
|
|
307
|
+
}
|
|
308
|
+
if (!isStreamingOp(opRaw)) {
|
|
309
|
+
const text = `STREAM_OP requires op in {start, stop, status}, got ${String(opRaw)}`;
|
|
310
|
+
if (callback) await callback({ text, actions: [] });
|
|
311
|
+
return { success: false, error: text };
|
|
312
|
+
}
|
|
313
|
+
const platform = platformRaw;
|
|
314
|
+
const op = opRaw;
|
|
315
|
+
const label = PLATFORM_LABELS[platform];
|
|
316
|
+
const port = streamingPipelineLocalPort();
|
|
317
|
+
try {
|
|
318
|
+
if (op === "start") {
|
|
319
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/stream/live`, {
|
|
320
|
+
method: "POST",
|
|
321
|
+
signal: AbortSignal.timeout(3e4)
|
|
322
|
+
});
|
|
323
|
+
const data = await res.json();
|
|
324
|
+
const ok = !!data.ok;
|
|
325
|
+
const text2 = ok ? `${label} stream started successfully! We're live.` : `Failed to start ${label} stream: ${data.error ?? "unknown error"}`;
|
|
326
|
+
if (callback) await callback({ text: text2, actions: [] });
|
|
327
|
+
return { success: ok, text: text2 };
|
|
328
|
+
}
|
|
329
|
+
if (op === "stop") {
|
|
330
|
+
const res = await fetch(
|
|
331
|
+
`http://127.0.0.1:${port}/api/stream/offline`,
|
|
332
|
+
{
|
|
333
|
+
method: "POST",
|
|
334
|
+
signal: AbortSignal.timeout(15e3)
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
const data = await res.json();
|
|
338
|
+
const ok = !!data.ok;
|
|
339
|
+
const text2 = ok ? `${label} stream stopped. We're offline now.` : `Failed to stop ${label} stream: ${data.error ?? "unknown error"}`;
|
|
340
|
+
if (callback) await callback({ text: text2, actions: [] });
|
|
341
|
+
return { success: ok, text: text2 };
|
|
342
|
+
}
|
|
343
|
+
const snapshot = await fetchStreamStatus(platform);
|
|
344
|
+
const status = snapshot.running ? "LIVE" : "OFFLINE";
|
|
345
|
+
const uptime = snapshot.uptimeSeconds === null ? "n/a" : `${Math.floor(snapshot.uptimeSeconds / 60)}m`;
|
|
346
|
+
const text = `${label} stream status: ${status} | Uptime: ${uptime} | Destination: ${label}`;
|
|
347
|
+
if (callback) await callback({ text, actions: [] });
|
|
348
|
+
return { success: true, text, data: { snapshot } };
|
|
349
|
+
} catch (err) {
|
|
350
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
351
|
+
const text = `Error running STREAM_OP ${op} for ${label}: ${msg}`;
|
|
352
|
+
if (callback) await callback({ text, actions: [] });
|
|
353
|
+
return { success: false, error: msg, text };
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
examples: [
|
|
357
|
+
[
|
|
358
|
+
{
|
|
359
|
+
name: "{{user1}}",
|
|
360
|
+
content: { text: "Go live on Twitch" }
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "{{agent}}",
|
|
364
|
+
content: {
|
|
365
|
+
text: "Starting the Twitch stream now.",
|
|
366
|
+
actions: ["STREAM"]
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
[
|
|
371
|
+
{
|
|
372
|
+
name: "{{user1}}",
|
|
373
|
+
content: { text: "Stop the YouTube stream" }
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "{{agent}}",
|
|
377
|
+
content: {
|
|
378
|
+
text: "Stopping the stream now.",
|
|
379
|
+
actions: ["STREAM"]
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
[
|
|
384
|
+
{
|
|
385
|
+
name: "{{user1}}",
|
|
386
|
+
content: { text: "Is the X stream live?" }
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "{{agent}}",
|
|
390
|
+
content: {
|
|
391
|
+
text: "Let me check the stream status.",
|
|
392
|
+
actions: ["STREAM"]
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
]
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const streamStatusProvider = {
|
|
400
|
+
name: "streamStatus",
|
|
401
|
+
description: "Live RTMP pipeline status per supported platform (twitch, youtube, x, pumpfun) rendered as JSON.",
|
|
402
|
+
descriptionCompressed: "RTMP status per platform.",
|
|
403
|
+
dynamic: true,
|
|
404
|
+
contexts: ["media", "automation"],
|
|
405
|
+
contextGate: { anyOf: ["media", "automation"] },
|
|
406
|
+
cacheStable: false,
|
|
407
|
+
cacheScope: "turn",
|
|
408
|
+
get: async (_runtime, _message, _state) => {
|
|
409
|
+
const rows = await Promise.all(
|
|
410
|
+
STREAMING_PLATFORMS.map(async (platform) => {
|
|
411
|
+
try {
|
|
412
|
+
const snap = await fetchStreamStatus(platform);
|
|
413
|
+
return {
|
|
414
|
+
platform: snap.platform,
|
|
415
|
+
running: snap.running,
|
|
416
|
+
uptimeSeconds: snap.uptimeSeconds ?? 0,
|
|
417
|
+
frames: snap.frames ?? 0,
|
|
418
|
+
destination: snap.destination,
|
|
419
|
+
error: null
|
|
420
|
+
};
|
|
421
|
+
} catch (err) {
|
|
422
|
+
return {
|
|
423
|
+
platform,
|
|
424
|
+
running: false,
|
|
425
|
+
uptimeSeconds: 0,
|
|
426
|
+
frames: 0,
|
|
427
|
+
destination: PLATFORM_LABELS[platform],
|
|
428
|
+
error: err instanceof Error ? err.message : String(err)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
})
|
|
432
|
+
);
|
|
433
|
+
return {
|
|
434
|
+
text: JSON.stringify({
|
|
435
|
+
stream_status: {
|
|
436
|
+
count: rows.length,
|
|
437
|
+
platforms: rows
|
|
438
|
+
}
|
|
439
|
+
}),
|
|
440
|
+
data: { stream_status: rows }
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
function createStreamingPlugin(cfg) {
|
|
445
|
+
const NAME = cfg.platformName;
|
|
446
|
+
const configEntries = {
|
|
447
|
+
[cfg.streamKeyEnvVar]: process.env[cfg.streamKeyEnvVar] ?? null
|
|
448
|
+
};
|
|
449
|
+
if (cfg.rtmpUrlEnvVar) {
|
|
450
|
+
configEntries[cfg.rtmpUrlEnvVar] = process.env[cfg.rtmpUrlEnvVar] ?? null;
|
|
451
|
+
}
|
|
452
|
+
const plugin = {
|
|
453
|
+
name: cfg.pluginName ?? `${cfg.platformId}-streaming`,
|
|
454
|
+
description: `${NAME} RTMP streaming destination \u2014 credentials and overlay layout. Stream control actions live on the unified streaming plugin.`,
|
|
455
|
+
get config() {
|
|
456
|
+
return configEntries;
|
|
457
|
+
},
|
|
458
|
+
actions: [],
|
|
459
|
+
async init(_config, _runtime) {
|
|
460
|
+
const streamKey = (_config[cfg.streamKeyEnvVar] ?? process.env[cfg.streamKeyEnvVar] ?? "").trim();
|
|
461
|
+
if (!streamKey) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const createDestination = (runtime, overrides) => {
|
|
467
|
+
if (cfg.cloudRelay && runtime) {
|
|
468
|
+
const backend = resolveStreamingBackend(runtime, cfg);
|
|
469
|
+
if (backend === "cloud") {
|
|
470
|
+
return createCloudRelayDestination({
|
|
471
|
+
platformId: cfg.platformId,
|
|
472
|
+
platformName: cfg.platformName,
|
|
473
|
+
runtime,
|
|
474
|
+
defaultOverlayLayout: cfg.defaultOverlayLayout
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return createStreamingDestination(cfg, overrides);
|
|
479
|
+
};
|
|
480
|
+
return { plugin, createDestination };
|
|
481
|
+
}
|
|
482
|
+
export {
|
|
483
|
+
STREAMING_PLATFORMS,
|
|
484
|
+
buildPresetLayout,
|
|
485
|
+
buildStreamOpAction,
|
|
486
|
+
createCloudRelayDestination,
|
|
487
|
+
createCustomRtmpDestination,
|
|
488
|
+
createNamedRtmpDestination,
|
|
489
|
+
createStreamingDestination,
|
|
490
|
+
createStreamingPlugin,
|
|
491
|
+
resolveStreamingBackend,
|
|
492
|
+
streamStatusProvider,
|
|
493
|
+
streamingPipelineLocalPort
|
|
494
|
+
};
|
|
495
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * Shared RTMP streaming utilities: destinations, cloud relay, overlay presets,\n * and pipeline control actions (local FFmpeg via dashboard API).\n */\n\nimport { isCloudConnected } from \"@elizaos/cloud-routing\";\nimport type {\n Action,\n ActionParameter,\n ActionResult,\n Content,\n HandlerCallback,\n HandlerOptions,\n IAgentRuntime,\n JsonValue,\n Memory,\n Plugin,\n Provider,\n ProviderResult,\n State,\n} from \"@elizaos/core\";\n\n// ── Overlay layout data (JSON-serializable, no React refs) ──────────────────\n\nexport interface OverlayWidgetInstance {\n id: string;\n type: string;\n enabled: boolean;\n position: { x: number; y: number; width: number; height: number };\n zIndex: number;\n config: Record<string, unknown>;\n}\n\nexport interface OverlayLayoutData {\n version: 1;\n name: string;\n widgets: OverlayWidgetInstance[];\n}\n\n// ── Shared types ────────────────────────────────────────────────────────────\n// Canonical definition — stream-routes.ts re-exports this interface.\n\nexport interface StreamingDestination {\n id: string;\n name: string;\n getCredentials(): Promise<{ rtmpUrl: string; rtmpKey: string }>;\n onStreamStart?(): Promise<void>;\n onStreamStop?(): Promise<void>;\n /** Per-destination default overlay layout, seeded on first stream start. */\n defaultOverlayLayout?: OverlayLayoutData;\n}\n\nexport interface StreamingPluginConfig {\n /** Short lowercase identifier, e.g. \"twitch\" or \"youtube\" */\n platformId: string;\n /** Display name, e.g. \"Twitch\" or \"YouTube\" */\n platformName: string;\n /** Env var that holds the stream key, e.g. \"TWITCH_STREAM_KEY\" */\n streamKeyEnvVar: string;\n /** Default RTMP ingest URL for this platform */\n defaultRtmpUrl: string;\n /** Optional env var for a custom RTMP URL (YouTube supports this) */\n rtmpUrlEnvVar?: string;\n /** Override the elizaOS plugin name (defaults to `${platformId}-streaming`) */\n pluginName?: string;\n /** Per-destination default overlay layout, seeded on first stream start. */\n defaultOverlayLayout?: OverlayLayoutData;\n /**\n * When true, the plugin auto-selects between direct RTMP push and the\n * Eliza Cloud RTMP relay backend based on `<UPPER>_STREAMING_BACKEND`\n * (`direct` | `cloud` | `auto`, default `auto`).\n *\n * - `direct` — push to platform RTMP ingest using a local stream key (Mode A).\n * - `cloud` — request a per-session relay from Eliza Cloud (Mode B).\n * The cloud fans the inbound stream out to N destinations.\n * - `auto` — pick `cloud` when Eliza Cloud is connected AND no local\n * stream key is set; otherwise pick `direct`.\n *\n * Existing users with a local `<PLATFORM>_STREAM_KEY` keep the direct path\n * unchanged; cloud relay only activates when they enable cloud and have no\n * local key.\n */\n cloudRelay?: boolean;\n}\n\n// ── Preset layout builder ───────────────────────────────────────────────────\n\n/** All known built-in widget types. */\nconst WIDGET_DEFAULTS: Record<\n string,\n { position: OverlayWidgetInstance[\"position\"]; zIndex: number }\n> = {\n \"thought-bubble\": {\n position: { x: 2, y: 2, width: 30, height: 20 },\n zIndex: 10,\n },\n \"action-ticker\": {\n position: { x: 0, y: 85, width: 100, height: 15 },\n zIndex: 5,\n },\n \"alert-popup\": {\n position: { x: 30, y: 10, width: 40, height: 20 },\n zIndex: 20,\n },\n \"viewer-count\": {\n position: { x: 88, y: 2, width: 10, height: 6 },\n zIndex: 15,\n },\n branding: { position: { x: 2, y: 90, width: 20, height: 8 }, zIndex: 2 },\n \"custom-html\": {\n position: { x: 50, y: 50, width: 30, height: 20 },\n zIndex: 1,\n },\n \"peon-hud\": {\n position: { x: 82, y: 10, width: 16, height: 30 },\n zIndex: 12,\n },\n \"peon-glass\": {\n position: { x: 2, y: 2, width: 32, height: 40 },\n zIndex: 16,\n },\n \"peon-sakura\": {\n position: { x: 0, y: 0, width: 25, height: 50 },\n zIndex: 3,\n },\n};\n\nlet _presetCounter = 0;\n\n/**\n * Build a preset overlay layout with the given widget types enabled.\n * Widget types not listed in `enabledTypes` are included but disabled.\n */\nexport function buildPresetLayout(\n name: string,\n enabledTypes: string[],\n): OverlayLayoutData {\n const enabledSet = new Set(enabledTypes);\n const widgets: OverlayWidgetInstance[] = Object.entries(WIDGET_DEFAULTS).map(\n ([type, defaults]) => {\n _presetCounter += 1;\n return {\n id: `preset${_presetCounter.toString(36)}`,\n type,\n enabled: enabledSet.has(type),\n position: { ...defaults.position },\n zIndex: defaults.zIndex,\n config: {},\n };\n },\n );\n return { version: 1, name, widgets };\n}\n\n// ── Named / custom RTMP (config-driven ingest) ───────────────────────────────\n\nexport function createNamedRtmpDestination(params: {\n id: string;\n name?: string;\n rtmpUrl: string;\n rtmpKey: string;\n}): StreamingDestination {\n const trimmedId = params.id.trim();\n const label = (params.name ?? trimmedId).trim() || trimmedId;\n return {\n id: trimmedId,\n name: label,\n async getCredentials() {\n const rtmpUrl = params.rtmpUrl.trim();\n const rtmpKey = params.rtmpKey.trim();\n if (!rtmpUrl || !rtmpKey) {\n throw new Error(`${label}: RTMP URL and stream key are required`);\n }\n return { rtmpUrl, rtmpKey };\n },\n };\n}\n\nexport function createCustomRtmpDestination(config?: {\n rtmpUrl?: string;\n rtmpKey?: string;\n}): StreamingDestination {\n return {\n id: \"custom-rtmp\",\n name: \"Custom RTMP\",\n async getCredentials() {\n const rtmpUrl = (\n config?.rtmpUrl ??\n process.env.CUSTOM_RTMP_URL ??\n \"\"\n ).trim();\n const rtmpKey = (\n config?.rtmpKey ??\n process.env.CUSTOM_RTMP_KEY ??\n \"\"\n ).trim();\n if (!rtmpUrl || !rtmpKey) {\n throw new Error(\n \"Custom RTMP requires rtmpUrl and rtmpKey in streaming.customRtmp config or CUSTOM_RTMP_* env\",\n );\n }\n return { rtmpUrl, rtmpKey };\n },\n };\n}\n\n// ── Destination factory ─────────────────────────────────────────────────────\n\nexport function createStreamingDestination(\n cfg: StreamingPluginConfig,\n overrides?: { streamKey?: string; rtmpUrl?: string },\n): StreamingDestination {\n return {\n id: cfg.platformId,\n name: cfg.platformName,\n defaultOverlayLayout: cfg.defaultOverlayLayout,\n\n async getCredentials() {\n const streamKey = (\n overrides?.streamKey ??\n process.env[cfg.streamKeyEnvVar] ??\n \"\"\n ).trim();\n if (!streamKey) {\n throw new Error(`${cfg.platformName} stream key not configured`);\n }\n\n const rtmpUrl = (\n overrides?.rtmpUrl ??\n (cfg.rtmpUrlEnvVar ? process.env[cfg.rtmpUrlEnvVar] : undefined) ??\n cfg.defaultRtmpUrl\n ).trim();\n if (!rtmpUrl) {\n throw new Error(`${cfg.platformName} RTMP URL not configured`);\n }\n\n return { rtmpUrl, rtmpKey: streamKey };\n },\n // Platforms detect stream automatically via RTMP ingest -- no API calls needed\n };\n}\n\n// ── Cloud relay destination ────────────────────────────────────────────────\n\nconst CLOUD_BASE_FALLBACK = \"https://www.elizacloud.ai/api/v1\";\n\nfunction readSetting(runtime: IAgentRuntime, key: string): string | null {\n const raw = runtime.getSetting(key);\n if (raw === null || raw === undefined) return null;\n const str = String(raw).trim();\n return str.length > 0 ? str : null;\n}\n\nfunction getCloudBaseUrl(runtime: IAgentRuntime): string {\n const override = readSetting(runtime, \"ELIZAOS_CLOUD_BASE_URL\");\n return (override ?? CLOUD_BASE_FALLBACK).replace(/\\/+$/, \"\");\n}\n\nfunction getCloudApiKey(runtime: IAgentRuntime): string {\n const apiKey = readSetting(runtime, \"ELIZAOS_CLOUD_API_KEY\");\n if (apiKey === null) {\n throw new Error(\n \"Eliza Cloud relay requested but ELIZAOS_CLOUD_API_KEY is not set\",\n );\n }\n return apiKey;\n}\n\ninterface CreateRelaySessionResponse {\n sessionId: string;\n streamKey: string;\n ingestUrl: string;\n wsUrl?: string;\n}\n\n/**\n * Configuration for the Eliza Cloud relay-backed streaming destination.\n *\n * The destination POSTs to `/v1/apis/streaming/sessions` to acquire a\n * per-session ingest URL + stream key. The cloud forwards the inbound\n * stream to the user's stored destinations for `platformId`.\n */\nexport interface CloudRelayDestinationCfg {\n /** Short lowercase platform identifier — e.g. \"twitch\", \"youtube\". */\n platformId: string;\n /** Display name — e.g. \"Twitch\", \"YouTube\". */\n platformName: string;\n /** Active runtime — used to read ELIZAOS_CLOUD_* settings. */\n runtime: IAgentRuntime;\n /** Optional per-destination default overlay layout. */\n defaultOverlayLayout?: OverlayLayoutData;\n}\n\n/**\n * Build a `StreamingDestination` whose RTMP credentials come from the\n * Eliza Cloud relay (Mode B). The cloud-issued credentials point at the\n * SRS ingest, NOT at the platform's RTMP endpoint — the cloud relays the\n * inbound stream to platform RTMP servers using stored per-org credentials.\n *\n * Lifecycle:\n * - `getCredentials()` — POST `/v1/apis/streaming/sessions` →\n * `{ sessionId, ingestUrl, streamKey }`, returned to the caller as\n * `{ rtmpUrl: ingestUrl, rtmpKey: streamKey }`.\n * - `onStreamStop()` — DELETE `/v1/apis/streaming/sessions/{id}`.\n *\n * Throws if Eliza Cloud is not connected.\n */\nexport function createCloudRelayDestination(\n cfg: CloudRelayDestinationCfg,\n): StreamingDestination {\n if (!isCloudConnected(cfg.runtime)) {\n throw new Error(\n `Cloud relay requested for ${cfg.platformName} but Eliza Cloud is not connected ` +\n `(ELIZAOS_CLOUD_API_KEY missing or ELIZAOS_CLOUD_ENABLED falsy)`,\n );\n }\n\n let activeSessionId: string | null = null;\n\n return {\n id: cfg.platformId,\n name: cfg.platformName,\n defaultOverlayLayout: cfg.defaultOverlayLayout,\n\n async getCredentials(): Promise<{ rtmpUrl: string; rtmpKey: string }> {\n const baseUrl = getCloudBaseUrl(cfg.runtime);\n const apiKey = getCloudApiKey(cfg.runtime);\n\n const res = await fetch(`${baseUrl}/apis/streaming/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ destinations: [cfg.platformId] }),\n signal: AbortSignal.timeout(20_000),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `Cloud relay session create failed: ${res.status} ${text}`,\n );\n }\n\n const body = (await res.json()) as CreateRelaySessionResponse;\n if (!body.sessionId || !body.streamKey || !body.ingestUrl) {\n throw new Error(\n \"Cloud relay session create returned malformed response\",\n );\n }\n\n activeSessionId = body.sessionId;\n return { rtmpUrl: body.ingestUrl, rtmpKey: body.streamKey };\n },\n\n async onStreamStop(): Promise<void> {\n if (!activeSessionId) return;\n const baseUrl = getCloudBaseUrl(cfg.runtime);\n const apiKey = getCloudApiKey(cfg.runtime);\n const sessionId = activeSessionId;\n activeSessionId = null;\n\n const res = await fetch(\n `${baseUrl}/apis/streaming/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${apiKey}` },\n signal: AbortSignal.timeout(15_000),\n },\n );\n if (!res.ok && res.status !== 404) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `Cloud relay session close failed: ${res.status} ${text}`,\n );\n }\n },\n };\n}\n\n// ── Backend selection ──────────────────────────────────────────────────────\n\nexport type StreamingBackend = \"direct\" | \"cloud\" | \"auto\";\n\nfunction readBackendSetting(\n runtime: IAgentRuntime,\n envVar: string,\n): StreamingBackend {\n const raw = readSetting(runtime, envVar);\n if (raw === null) return \"auto\";\n const lower = raw.toLowerCase();\n if (lower === \"direct\" || lower === \"cloud\" || lower === \"auto\") return lower;\n throw new Error(\n `Invalid ${envVar}=\"${raw}\" (expected \"direct\" | \"cloud\" | \"auto\")`,\n );\n}\n\n/**\n * Resolve which streaming backend to use for a given platform at runtime.\n *\n * Reads `<UPPER>_STREAMING_BACKEND` (e.g. `TWITCH_STREAMING_BACKEND`) — one\n * of `direct`, `cloud`, or `auto` (default `auto`).\n *\n * `auto` picks `cloud` iff Eliza Cloud is connected AND no local stream key\n * is set in `cfg.streamKeyEnvVar`. Otherwise it picks `direct`.\n */\nexport function resolveStreamingBackend(\n runtime: IAgentRuntime,\n cfg: StreamingPluginConfig,\n): \"direct\" | \"cloud\" {\n const upper = cfg.platformId.toUpperCase().replace(/[^A-Z0-9]/g, \"_\");\n const setting = readBackendSetting(runtime, `${upper}_STREAMING_BACKEND`);\n if (setting === \"direct\" || setting === \"cloud\") return setting;\n\n const localKey = readSetting(runtime, cfg.streamKeyEnvVar);\n if (localKey !== null) return \"direct\";\n return isCloudConnected(runtime) ? \"cloud\" : \"direct\";\n}\n\n// ── Plugin factory ──────────────────────────────────────────────────────────\n\nexport function streamingPipelineLocalPort(): number {\n return Number(process.env.SERVER_PORT || process.env.PORT || \"2138\");\n}\n\n// ── Unified STREAM_OP router action + streamStatus provider ────────────────\n\nexport const STREAMING_PLATFORMS = [\n \"twitch\",\n \"youtube\",\n \"x\",\n \"pumpfun\",\n] as const;\n\nexport type StreamingPlatform = (typeof STREAMING_PLATFORMS)[number];\n\nexport type StreamingOp = \"start\" | \"stop\" | \"status\";\n\nconst PLATFORM_LABELS: Record<StreamingPlatform, string> = {\n twitch: \"Twitch\",\n youtube: \"YouTube\",\n x: \"X (Twitter)\",\n pumpfun: \"pump.fun\",\n};\n\ninterface StreamStatusSnapshot {\n platform: StreamingPlatform;\n running: boolean;\n uptimeSeconds: number | null;\n frames: number | null;\n destination: string;\n}\n\nfunction isStreamingPlatform(value: unknown): value is StreamingPlatform {\n return (\n typeof value === \"string\" &&\n (STREAMING_PLATFORMS as readonly string[]).includes(value)\n );\n}\n\nfunction isStreamingOp(value: unknown): value is StreamingOp {\n return value === \"start\" || value === \"stop\" || value === \"status\";\n}\n\nfunction readParam(\n options: unknown,\n key: string,\n): string | number | boolean | null | undefined {\n if (!options || typeof options !== \"object\" || Array.isArray(options)) {\n return undefined;\n }\n const handler = options as HandlerOptions;\n const params = handler.parameters as Record<string, JsonValue> | undefined;\n if (params && key in params) {\n const v = params[key];\n if (\n typeof v === \"string\" ||\n typeof v === \"number\" ||\n typeof v === \"boolean\" ||\n v === null\n ) {\n return v as string | number | boolean | null;\n }\n }\n const flat = options as Record<string, unknown>;\n const v = flat[key];\n if (\n typeof v === \"string\" ||\n typeof v === \"number\" ||\n typeof v === \"boolean\" ||\n v === null\n ) {\n return v as string | number | boolean | null;\n }\n return undefined;\n}\n\nasync function fetchStreamStatus(\n platform: StreamingPlatform,\n): Promise<StreamStatusSnapshot> {\n const port = streamingPipelineLocalPort();\n const res = await fetch(`http://127.0.0.1:${port}/api/stream/status`, {\n signal: AbortSignal.timeout(10_000),\n });\n const data = (await res.json()) as Record<string, unknown>;\n const uptime = typeof data.uptime === \"number\" ? Number(data.uptime) : null;\n const frames = typeof data.frames === \"number\" ? Number(data.frames) : null;\n return {\n platform,\n running: !!data.running,\n uptimeSeconds: uptime,\n frames,\n destination: PLATFORM_LABELS[platform],\n };\n}\n\nexport interface BuildStreamOpActionParams {\n validate?: () => Promise<boolean>;\n}\n\nexport function buildStreamOpAction(\n params: BuildStreamOpActionParams = {},\n): Action {\n const validate = params.validate ?? (async () => true);\n\n const platformParam: ActionParameter = {\n name: \"platform\",\n description:\n \"Streaming destination platform: twitch, youtube, x, or pumpfun.\",\n descriptionCompressed: \"Platform: twitch|youtube|x|pumpfun.\",\n required: true,\n schema: {\n type: \"string\",\n enum: [...STREAMING_PLATFORMS],\n },\n };\n\n const opParam: ActionParameter = {\n name: \"subaction\",\n description:\n \"Operation to perform: start (go live), stop (go offline), or status.\",\n descriptionCompressed: \"Op: start|stop|status.\",\n required: true,\n schema: {\n type: \"string\",\n enum: [\"start\", \"stop\", \"status\"],\n },\n };\n\n return {\n name: \"STREAM\",\n contexts: [\"media\", \"automation\", \"connectors\"],\n contextGate: { anyOf: [\"media\", \"automation\", \"connectors\"] },\n roleGate: { minRole: \"ADMIN\" },\n description:\n \"Control the local RTMP streaming pipeline for a target platform. Dispatches start, stop, and status calls to the dashboard stream API for twitch, youtube, x, or pumpfun.\",\n descriptionCompressed:\n \"Stream ops: start, stop, status; platforms: twitch, youtube, x, pumpfun.\",\n similes: [\n \"START_STREAM\",\n \"STOP_STREAM\",\n \"GET_STREAM_STATUS\",\n \"GO_LIVE\",\n \"GO_OFFLINE\",\n \"STREAM_STATUS\",\n \"IS_LIVE\",\n ],\n parameters: [platformParam, opParam],\n validate,\n handler: async (\n _runtime: IAgentRuntime,\n _message: Memory,\n _state: State | undefined,\n options:\n | HandlerOptions\n | Record<string, JsonValue | undefined>\n | undefined,\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n const platformRaw = readParam(options, \"platform\");\n const opRaw = readParam(options, \"op\");\n if (!isStreamingPlatform(platformRaw)) {\n const text = `STREAM_OP requires platform in {${STREAMING_PLATFORMS.join(\", \")}}, got ${String(platformRaw)}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: false, error: text };\n }\n if (!isStreamingOp(opRaw)) {\n const text = `STREAM_OP requires op in {start, stop, status}, got ${String(opRaw)}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: false, error: text };\n }\n\n const platform: StreamingPlatform = platformRaw;\n const op: StreamingOp = opRaw;\n const label = PLATFORM_LABELS[platform];\n const port = streamingPipelineLocalPort();\n\n try {\n if (op === \"start\") {\n const res = await fetch(`http://127.0.0.1:${port}/api/stream/live`, {\n method: \"POST\",\n signal: AbortSignal.timeout(30_000),\n });\n const data = (await res.json()) as Record<string, unknown>;\n const ok = !!data.ok;\n const text = ok\n ? `${label} stream started successfully! We're live.`\n : `Failed to start ${label} stream: ${data.error ?? \"unknown error\"}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: ok, text };\n }\n\n if (op === \"stop\") {\n const res = await fetch(\n `http://127.0.0.1:${port}/api/stream/offline`,\n {\n method: \"POST\",\n signal: AbortSignal.timeout(15_000),\n },\n );\n const data = (await res.json()) as Record<string, unknown>;\n const ok = !!data.ok;\n const text = ok\n ? `${label} stream stopped. We're offline now.`\n : `Failed to stop ${label} stream: ${data.error ?? \"unknown error\"}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: ok, text };\n }\n\n const snapshot = await fetchStreamStatus(platform);\n const status = snapshot.running ? \"LIVE\" : \"OFFLINE\";\n const uptime =\n snapshot.uptimeSeconds === null\n ? \"n/a\"\n : `${Math.floor(snapshot.uptimeSeconds / 60)}m`;\n const text = `${label} stream status: ${status} | Uptime: ${uptime} | Destination: ${label}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: true, text, data: { snapshot } };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const text = `Error running STREAM_OP ${op} for ${label}: ${msg}`;\n if (callback) await callback({ text, actions: [] } as Content);\n return { success: false, error: msg, text };\n }\n },\n examples: [\n [\n {\n name: \"{{user1}}\",\n content: { text: \"Go live on Twitch\" },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Starting the Twitch stream now.\",\n actions: [\"STREAM\"],\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"Stop the YouTube stream\" },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Stopping the stream now.\",\n actions: [\"STREAM\"],\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"Is the X stream live?\" },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Let me check the stream status.\",\n actions: [\"STREAM\"],\n },\n },\n ],\n ],\n };\n}\n\n/**\n * Provider that renders the live status of every supported streaming platform\n * as JSON context. The pipeline currently exposes a single shared\n * `/api/stream/status` endpoint, so each platform row reflects that same\n * snapshot tagged with its destination label.\n */\nexport const streamStatusProvider: Provider = {\n name: \"streamStatus\",\n description:\n \"Live RTMP pipeline status per supported platform (twitch, youtube, x, pumpfun) rendered as JSON.\",\n descriptionCompressed: \"RTMP status per platform.\",\n dynamic: true,\n contexts: [\"media\", \"automation\"],\n contextGate: { anyOf: [\"media\", \"automation\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n get: async (\n _runtime: IAgentRuntime,\n _message: Memory,\n _state: State,\n ): Promise<ProviderResult> => {\n const rows = await Promise.all(\n STREAMING_PLATFORMS.map(async (platform) => {\n try {\n const snap = await fetchStreamStatus(platform);\n return {\n platform: snap.platform,\n running: snap.running,\n uptimeSeconds: snap.uptimeSeconds ?? 0,\n frames: snap.frames ?? 0,\n destination: snap.destination,\n error: null,\n };\n } catch (err) {\n return {\n platform,\n running: false,\n uptimeSeconds: 0,\n frames: 0,\n destination: PLATFORM_LABELS[platform],\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }),\n );\n\n return {\n text: JSON.stringify({\n stream_status: {\n count: rows.length,\n platforms: rows,\n },\n }),\n data: { stream_status: rows },\n };\n },\n};\n\n/**\n * Build a complete elizaOS Plugin for a streaming destination.\n *\n * Returns:\n * - `plugin` -- the Plugin object to register with elizaOS\n * - `createDestination` -- the destination factory (for the streaming pipeline)\n */\n/** Result of {@link createStreamingPlugin} — plugin + a backend-aware destination factory. */\nexport interface CreatedStreamingPlugin {\n plugin: Plugin;\n createDestination: (\n runtime?: IAgentRuntime,\n overrides?: { streamKey?: string; rtmpUrl?: string },\n ) => StreamingDestination;\n}\n\nexport function createStreamingPlugin(\n cfg: StreamingPluginConfig,\n): CreatedStreamingPlugin {\n const NAME = cfg.platformName;\n\n const configEntries: Record<string, string | null> = {\n [cfg.streamKeyEnvVar]: process.env[cfg.streamKeyEnvVar] ?? null,\n };\n if (cfg.rtmpUrlEnvVar) {\n configEntries[cfg.rtmpUrlEnvVar] = process.env[cfg.rtmpUrlEnvVar] ?? null;\n }\n\n const plugin: Plugin = {\n name: cfg.pluginName ?? `${cfg.platformId}-streaming`,\n description: `${NAME} RTMP streaming destination — credentials and overlay layout. Stream control actions live on the unified streaming plugin.`,\n get config() {\n return configEntries;\n },\n actions: [],\n async init(_config: Record<string, string>, _runtime: IAgentRuntime) {\n const streamKey = (\n _config[cfg.streamKeyEnvVar] ??\n process.env[cfg.streamKeyEnvVar] ??\n \"\"\n ).trim();\n if (!streamKey) {\n return;\n }\n },\n };\n\n const createDestination = (\n runtime?: IAgentRuntime,\n overrides?: { streamKey?: string; rtmpUrl?: string },\n ): StreamingDestination => {\n if (cfg.cloudRelay && runtime) {\n const backend = resolveStreamingBackend(runtime, cfg);\n if (backend === \"cloud\") {\n return createCloudRelayDestination({\n platformId: cfg.platformId,\n platformName: cfg.platformName,\n runtime,\n defaultOverlayLayout: cfg.defaultOverlayLayout,\n });\n }\n }\n return createStreamingDestination(cfg, overrides);\n };\n\n return { plugin, createDestination };\n}\n"],"mappings":"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","names":["v","text"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { StreamVisualSettings, StreamVoiceSettings, getHeadlessCaptureConfig, parseDestinationQuery, readOverlayLayout, readStreamSettings, safeDestId, seedOverlayDefaults, validateStreamSettings, writeOverlayLayout, writeStreamSettings } from './api/stream-persistence.js';
|
|
2
|
+
export { StreamRouteState } from './api/stream-route-state.js';
|
|
3
|
+
export { detectCaptureMode, ensureXvfb, getActiveDestination, handleStreamRoute } from './api/stream-routes.js';
|
|
4
|
+
export { StreamingUpdate, StreamingUpdateKind, mergeStreamingText, resolveStreamingUpdate } from './api/streaming-text.js';
|
|
5
|
+
export { TtsRouteContext, handleTtsRoutes } from './api/tts-routes.js';
|
|
6
|
+
import { StreamingDestination } from './core.js';
|
|
7
|
+
export { BuildStreamOpActionParams, CloudRelayDestinationCfg, CreatedStreamingPlugin, OverlayLayoutData, OverlayWidgetInstance, STREAMING_PLATFORMS, StreamingBackend, StreamingOp, StreamingPlatform, StreamingPluginConfig, buildPresetLayout, buildStreamOpAction, createCloudRelayDestination, createCustomRtmpDestination, createNamedRtmpDestination, createStreamingDestination, createStreamingPlugin, resolveStreamingBackend, streamStatusProvider, streamingPipelineLocalPort } from './core.js';
|
|
8
|
+
export { AudioSource, StreamConfig, streamManager } from './services/stream-manager.js';
|
|
9
|
+
import { IAgentRuntime, Plugin } from '@elizaos/core';
|
|
10
|
+
import 'node:http';
|
|
11
|
+
import './services/tts-stream-bridge.js';
|
|
12
|
+
import 'node:stream';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @elizaos/plugin-streaming — RTMP destinations (Twitch, YouTube, X, pump.fun, custom/named ingest).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare function createTwitchDestination(runtime?: IAgentRuntime, config?: {
|
|
19
|
+
streamKey?: string;
|
|
20
|
+
}): StreamingDestination;
|
|
21
|
+
declare function createYoutubeDestination(runtime?: IAgentRuntime, config?: {
|
|
22
|
+
streamKey?: string;
|
|
23
|
+
rtmpUrl?: string;
|
|
24
|
+
}): StreamingDestination;
|
|
25
|
+
declare function createXStreamDestination(runtime?: IAgentRuntime, config?: {
|
|
26
|
+
streamKey?: string;
|
|
27
|
+
rtmpUrl?: string;
|
|
28
|
+
}): StreamingDestination;
|
|
29
|
+
declare function createPumpfunDestination(runtime?: IAgentRuntime, config?: {
|
|
30
|
+
streamKey?: string;
|
|
31
|
+
rtmpUrl?: string;
|
|
32
|
+
}): StreamingDestination;
|
|
33
|
+
declare const streamingPlugin: Plugin;
|
|
34
|
+
|
|
35
|
+
export { StreamingDestination, createPumpfunDestination, createTwitchDestination, createXStreamDestination, createYoutubeDestination, streamingPlugin as default, streamingPlugin };
|