@clodocapeo/pulsar-bundle-full 1.8.3 → 3.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 CHANGED
@@ -1,452 +1,193 @@
1
1
  # @clodocapeo/pulsar-bundle-full
2
2
 
3
- [![npm](https://img.shields.io/npm/v/%40clodocapeo%2Fpulsar-bundle-full?logo=npm&color=cb3837)](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle-full)
4
- [![Licence GPL-2.0-or-later](https://img.shields.io/badge/licence-GPL--2.0--or--later-blue)](./LICENSE)
5
- [![Platform](https://img.shields.io/badge/platform-windows--x64-0078d4)](https://github.com/ZabLaboratory/Pulsar/releases)
6
- [![Node ≥ 18](https://img.shields.io/badge/node-%E2%89%A518-339933)](https://nodejs.org)
7
-
8
- The **full** Pulsar bundle: ships `pulsar.exe` + libobs runtime +
9
- encoders + capture plugins **plus** `obs-browser` (CEF for HTML /
10
- JS overlays), `obs-text` (native text sources), and `vlc-video`
11
- (libVLC media playback). Same Node `spawn()` API as
12
- [`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle).
13
-
14
- If you don't need browser sources / native text / VLC media, use
15
- the lighter `@clodocapeo/pulsar-bundle` instead — same API, ~110 MB
16
- less to download.
17
-
18
- ## Table of contents
19
-
20
- - [What's in the box](#whats-in-the-box)
21
- - [Choosing between light and full](#choosing-between-light-and-full)
22
- - [Install](#install)
23
- - [Quick start](#quick-start)
24
- - [Browser / text / VLC sources](#browser--text--vlc-sources)
25
- - [`spawn()` API](#spawn-api)
26
- - [Bundling for distribution](#bundling-for-distribution)
27
- - [CI / offline / mirror](#ci--offline--mirror)
28
- - [Troubleshooting](#troubleshooting)
29
- - [Versioning](#versioning)
30
- - [Compatibility](#compatibility)
31
- - [Licence](#licence)
32
-
33
- ## What's in the box
34
-
35
- Everything in the **light** bundle, plus:
36
-
37
- | Plugin | What it does |
38
- |---|---|
39
- | `obs-browser` (Pulsar fork) | HTML / CSS / JS scenes via Chromium Embedded Framework. Real Chromium runtime, GPU-composited, controllable from the host. |
40
- | `pulsar-browser-page.exe` | The CEF helper executable. Lives next to `libcef.dll` in `obs-plugins/64bit/`. |
41
- | `obs-text` | Native text sources (`text_gdiplus`). Direct GDI+ rendering — fast, sharp, no DOM overhead. |
42
- | `text-freetype2` | FreeType-rendered text sources (`text_ft2_source_v2`). Better cross-script support than gdiplus, less Windows-coupled. |
43
- | `vlc-video` | Local media playback (`vlc_source`) backed by libVLC. Playlists, all the codecs libVLC handles. |
44
-
45
- ### Size
46
-
47
- | | Compressed | Extracted |
48
- |---|---|---|
49
- | Light bundle | ~40 MB | ~100 MB |
50
- | **Full bundle** | **~150 MB** | **~370 MB** |
51
-
52
- The ~110 MB inflation is **CEF** — the Chromium runtime obs-browser
53
- links against. It's what makes browser sources possible.
54
-
55
- ## Choosing between light and full
56
-
57
- **Use the full bundle when** your scenes need any of:
58
-
59
- - HTML / CSS / JS overlays composed in a real browser engine
60
- - Operator-controlled web UIs as broadcast inputs
61
- - Native text overlays (lower-bargain than rendering text in a browser)
62
- - Local video file playback via VLC (formats ffmpeg doesn't ship by default)
63
- - Any obs-studio plugin that depends on the listed plugins
64
-
65
- **Use [`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle) when** you can express your overlays
66
- some other way: text rendered in a `Canvas` upstream of `pulsar.exe`,
67
- overlays drawn into images / video files, scenes composed
68
- client-side. The lean bundle saves ~110 MB and skips a lot of moving
69
- parts (CEF subprocess management, GPU process, V8, …).
70
-
71
- The two packages are **interchangeable from the API point of view**:
3
+ [![npm](https://img.shields.io/npm/v/%40clodocapeo%2Fpulsar-bundle-full)](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle-full)
72
4
 
73
- ```ts
74
- // Identical:
75
- import { spawn } from "@clodocapeo/pulsar-bundle";
76
- import { spawn } from "@clodocapeo/pulsar-bundle-full";
77
- ```
5
+ The **full Windows x64 Pulsar 3.0.0 runtime**, with CEF/browser, native text,
6
+ VLC module and gated NVIDIA effect module in addition to the light features.
78
7
 
79
- Switching is a `package.json` rename no code change.
8
+ It exposes the same `spawn()` API and matching client as the
9
+ [light bundle](../pulsar-bundle/README.md). That guide is the complete shared
10
+ reference for options, errors, namespace isolation, shutdown, offline installs
11
+ and packaging. This document covers the full variant's additional behavior.
80
12
 
81
- ## Install
13
+ ## Install and first run
82
14
 
83
- ```bash
84
- npm install @clodocapeo/pulsar-bundle-full
15
+ ```powershell
16
+ npm install @clodocapeo/pulsar-bundle-full@3.0.0
85
17
  ```
86
18
 
87
- Windows x64 only — `os: ["win32"]` + `cpu: ["x64"]` in `package.json`
88
- make `npm install` skip on every other platform.
89
-
90
- A `postinstall` step downloads
91
- `pulsar-windows-x64-full-v<VERSION>.zip` from the matching Pulsar
92
- GitHub Release (~150 MB) and extracts it into
93
- `node_modules/@clodocapeo/pulsar-bundle-full/binaries/`. Cached: the
94
- download is skipped if `binaries/.version-stamp` already matches the
95
- package version.
96
-
97
- If the download fails (network error, unpublished version, 404), the
98
- postinstall **soft-fails** with a warning — `npm install` completes,
99
- the package installs, but `spawn()` will throw a clear error. Same
100
- behaviour as the light bundle.
101
-
102
- ## Quick start
103
-
104
- ```ts
19
+ ```js
105
20
  import { spawn } from "@clodocapeo/pulsar-bundle-full";
106
21
 
107
- const pulsar = await spawn({
108
- env: {
109
- PULSAR_FPS: "60",
110
- PULSAR_RESOLUTION: "1920x1080",
111
- PULSAR_VIDEO_BITRATE: "6000",
112
- },
113
- });
114
-
115
- console.log(`pulsar booted: libobs ${pulsar.libobsVersion}, ws :${pulsar.port}`);
116
-
117
- // HTML overlay via browser_source — the headline feature of -full
118
- await pulsar.client.obs.call("CreateInput", {
119
- sceneName: "Default",
120
- inputName: "Overlay",
121
- inputKind: "browser_source",
122
- inputSettings: {
123
- url: "https://example.com/my-overlay",
124
- width: 1920,
125
- height: 1080,
126
- },
127
- });
22
+ const pulsar = await spawn({ readyTimeoutMs: 60_000 });
23
+ try {
24
+ console.log(await pulsar.client.capabilities.get());
25
+ } finally {
26
+ await pulsar.shutdown();
27
+ }
28
+ ```
128
29
 
129
- // Multi-destination + adaptive bitrate (same as light bundle)
130
- const dest = await pulsar.client.destinations.create({
131
- kind: "twitch",
132
- key: process.env.TWITCH_KEY!,
133
- });
134
- await pulsar.client.destinations.start(dest.id);
30
+ Node 18+, ESM and Windows x64 are required. Postinstall downloads
31
+ `pulsar-windows-x64-full-v3.0.0.zip` into `binaries/`.
32
+ A download warning can leave npm installation successful without a usable
33
+ binary; check the artifact before deployment.
135
34
 
136
- // ... your application's broadcast workflow ...
35
+ ## Additional capabilities
137
36
 
138
- await pulsar.shutdown();
139
- ```
37
+ | Family | What is included | What to verify |
38
+ |---|---|---|
39
+ | Browser | Pulsar's `browser_source` fork, CEF/helper/resources | Scene URL, rendering path, helper lifetime and actual PGM frames. |
40
+ | Text | GDI+/FreeType native text modules | Registered input kind and installed font. |
41
+ | VLC | `vlc-video` module | Whether libVLC and its codecs are actually available on the host/distribution. |
42
+ | NVIDIA effects | `nv-filters` module | Validated SDK directory/version/models and live capability report. SDK payload is not bundled. |
140
43
 
141
- The full client surface (six namespaces, typed events, typed errors,
142
- v5 baseline passthrough) is documented in
143
- [`@clodocapeo/pulsar-client`'s README](https://www.npmjs.com/package/@clodocapeo/pulsar-client)
144
- — this package re-exports every symbol so a single import gets you
145
- both the spawn API and the client types.
44
+ Window, display, game and DirectShow capture, WASAPI, encoding, streaming,
45
+ recording, replay and dual-lane control are **not full-only**.
146
46
 
147
- ## Browser / text / VLC sources
47
+ The package does not guarantee NDI, arbitrary OBS plugins, Preview audio/AFV,
48
+ every VLC codec or every GPU. Query actual capability and source registration.
148
49
 
149
- The three plugins exclusive to this bundle expose new
150
- [obs-websocket v5 input kinds](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputkindlist).
151
- You create them via `pulsar.client.obs.call("CreateInput", {...})`:
50
+ ## Browser capture example
152
51
 
153
- ### Browser source (`browser_source`)
52
+ With a running `pulsar` handle and your own reachable scene server:
154
53
 
155
- ```ts
156
- await pulsar.client.obs.call("CreateInput", {
157
- sceneName: "Default",
158
- inputName: "Overlay",
159
- inputKind: "browser_source",
160
- inputSettings: {
161
- url: "https://my.cdn/overlay/index.html",
54
+ ```js
55
+ const result = await pulsar.client.obs.call("CallVendorRequest", {
56
+ vendorName: "pulsar-scene",
57
+ requestType: "SetCaptureSource",
58
+ requestData: {
59
+ kind: "browser_source",
60
+ url: "http://127.0.0.1:3000/scene",
162
61
  width: 1920,
163
62
  height: 1080,
164
63
  fps: 60,
165
- fps_custom: true,
166
- css: "body { background: transparent; }",
167
- shutdown: true, // suspend audio when source is hidden
168
- restart_when_active: false,
64
+ reroute_audio: false,
169
65
  },
170
66
  });
171
-
172
- // At runtime, push events / state into the page:
173
- await pulsar.client.obs.call("CallVendorRequest", {
174
- vendorName: "obs-browser",
175
- requestType: "emit_event",
176
- requestData: {
177
- event_name: "score_update",
178
- event_data: { home: 3, away: 2 },
179
- },
180
- });
181
- // In the page: window.addEventListener("obsSourceCustomEvent", ...)
67
+ console.log(result.responseData);
182
68
  ```
183
69
 
184
- CEF runs the page in its own process (`pulsar-browser-page.exe`),
185
- GPU-composited via libobs. The page can use the full Chromium API
186
- including `OffscreenCanvas`, `WebAudio`, `WebGL2`, `WebTransport`, …
187
-
188
- ### Native text sources
189
-
190
- Two flavours, pick whichever renders better for your locale:
70
+ This uses the **legacy single managed-capture replacement** helper.
71
+ For independent Preview preparation and atomic Takes, use
72
+ [scene-switch v1](../../scripts/contracts/scene_switch_v1/README.md).
73
+ Creating a browser source is not proof that its page renders healthy frames.
191
74
 
192
- ```ts
193
- // gdiplus: Windows-native, fast, sharp
194
- await pulsar.client.obs.call("CreateInput", {
195
- sceneName: "Default",
196
- inputName: "Title",
197
- inputKind: "text_gdiplus_v3",
198
- inputSettings: {
199
- text: "LIVE",
200
- font: { face: "Segoe UI", size: 64, flags: 1 },
201
- color1: 0xFFFFFFFF,
202
- outline: true,
203
- outline_color: 0xFF000000,
204
- },
205
- });
206
-
207
- // freetype2: better non-Latin script support
208
- await pulsar.client.obs.call("CreateInput", {
209
- sceneName: "Default",
210
- inputName: "Subtitle",
211
- inputKind: "text_ft2_source_v2",
212
- inputSettings: {
213
- text: "こんにちは",
214
- font: { face: "Noto Sans CJK JP", size: 48 },
215
- },
216
- });
217
- ```
218
-
219
- ### VLC media source (`vlc_source`)
75
+ The managed source pins webpage control to None. The page is content, not
76
+ authorized broadcast-control code. CEF runs without the sandbox SDK in this
77
+ build; the scene server/content is part of the application's trust boundary.
220
78
 
221
- ```ts
222
- await pulsar.client.obs.call("CreateInput", {
223
- sceneName: "Default",
224
- inputName: "Bumper",
225
- inputKind: "vlc_source",
226
- inputSettings: {
227
- playlist: [
228
- { value: "C:/path/to/intro.mp4", selected: false, hidden: false },
229
- { value: "C:/path/to/sponsor.mkv", selected: false, hidden: false },
230
- ],
231
- loop: true,
232
- shuffle: false,
233
- playback_behavior: "stop_restart",
234
- },
235
- });
236
- ```
79
+ ### Render and helper layout
237
80
 
238
- Useful for codecs ffmpeg's default build doesn't support, or playlist
239
- behaviour the FFmpeg muxer source can't express.
81
+ The distribution ships `pulsar-browser.dll` and
82
+ `pulsar-browser-page.exe` next to CEF under `obs-plugins/64bit/`.
83
+ The upstream `obs-browser.dll` is removed to avoid duplicate source
84
+ registration. Preserve all CEF locale/resource files and the helper path.
240
85
 
241
- ## `spawn()` API
86
+ GPU acceleration is the normal supported hardware rendering path;
87
+ software rendering has separate callback tests. Do not disable GPU globally
88
+ to make an accelerated-rendering test green. A no-physical-GPU CI skip is
89
+ reported as unexecuted hardware proof.
242
90
 
243
- Identical to the light bundle's. See the
244
- [`@clodocapeo/pulsar-bundle` README](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle)
245
- for the full reference; it covers:
91
+ Callback admission, asynchronous source tasks and browser close are fenced
92
+ before libobs teardown. Do not kill every browser helper by image name;
93
+ multiple Pulsar runtimes may own independent children.
246
94
 
247
- - The `SpawnOptions` shape (`binariesPath`, `env`, `readyTimeoutMs`,
248
- `onLog`)
249
- - The `SpawnedPulsar` handle (`client`, `child`, `port`,
250
- `libobsVersion`, `shutdown()`)
251
- - Boot environment variables (the full `PULSAR_*` matrix)
252
- - Lifecycle (boot / shutdown / crash recovery)
95
+ ## Text and media sources
253
96
 
254
- Everything below is specific to the full bundle.
97
+ Use `GetInputKindList` / the capability manifest to select the actual kind.
98
+ Typical source IDs include `text_gdiplus_v2`, `text_ft2_source_v2`,
99
+ `vlc_source` and `ffmpeg_source`, subject to registration.
255
100
 
256
- ### Boot time
101
+ For text, select a font available on the target machine. For VLC, do not
102
+ assume the presence of the module means a complete libVLC codec distribution
103
+ was shipped. Validate playback on the installed artifact. FFmpeg media source
104
+ is a different input from the FFmpeg recording/muxer output.
257
105
 
258
- The full bundle takes **~1 s longer to boot** than the light variant —
259
- CEF needs to spin up its own subprocess + GPU process before
260
- `obs-browser` reports ready. The 30 s default `readyTimeoutMs` still
261
- has plenty of headroom; bump it on contended CI runners or under
262
- heavy AV scanning.
106
+ When using a local stinger, pass an explicit absolute `PULSAR_STINGER_ASSET`.
107
+ The legacy cwd-relative fallback is not reliable with private runtime cwd.
108
+ Dual-lane Stinger/Fade requires its own opt-in and validates the asset before
109
+ use; missing/invalid input must not be treated as a successful transition.
263
110
 
264
- ### CEF subprocess
111
+ ## Lifecycle
265
112
 
266
- `pulsar-browser-page.exe` ships in `obs-plugins/64bit/` next to
267
- `libcef.dll` (mandatory CEF resolves its helper executable
268
- relative to the DLL). It is spawned by libobs via CEF's own
269
- multi-process architecture; you don't manage it from Node.
113
+ The shared launcher waits for the idle marker, reads this child's protected
114
+ config and authenticates v5. It does not wait for every browser page to finish
115
+ rendering. Observe actual source/Preview readiness separately.
270
116
 
271
- If you see `pulsar-browser-page.exe` stuck in Task Manager after
272
- `pulsar.exe` has exited, that's a known CEF-side issue (rare, usually
273
- on heavy-load shutdowns). Pulsar's `taskkill` fallback in `shutdown()`
274
- takes care of it; manual cleanup is `taskkill /F /IM pulsar-browser-page.exe`.
117
+ Finalize active outputs and retain files before calling `shutdown()`.
118
+ The Node helper terminates the child with a bounded fallback; Windows
119
+ termination is not a guarantee of graceful CEF/libobs teardown or MP4
120
+ finalization. The native shutdown-event harness is a different path.
275
121
 
276
- ## Bundling for distribution
122
+ Default generated runtime directories are temporary and may be removed after
123
+ shutdown. Supply persistent recording/log destinations or an explicit
124
+ application-owned runtime directory.
277
125
 
278
- The full bundle is bigger — make sure your packaging tooling honours
279
- the `asarUnpack` glob and the `cwd` constraint.
126
+ ## Packaging Electron
280
127
 
281
- ### electron-builder
128
+ Keep the native tree outside ASAR:
282
129
 
283
- ```jsonc
130
+ ```json
284
131
  {
285
132
  "asar": true,
286
133
  "asarUnpack": [
287
134
  "node_modules/@clodocapeo/pulsar-bundle-full/binaries/**/*"
288
- ],
289
- "files": [
290
- "dist/**/*",
291
- "node_modules/@clodocapeo/pulsar-bundle-full/**/*"
292
- ],
293
- "extraResources": []
135
+ ]
294
136
  }
295
137
  ```
296
138
 
297
- Then in your Electron main process:
298
-
299
139
  ```ts
300
140
  import { app } from "electron";
301
- import { spawn } from "@clodocapeo/pulsar-bundle-full";
302
141
  import { resolve } from "node:path";
142
+ import { spawn } from "@clodocapeo/pulsar-bundle-full";
303
143
 
304
144
  const binariesPath = app.isPackaged
305
- ? resolve(
306
- process.resourcesPath,
307
- "app.asar.unpacked",
308
- "node_modules",
309
- "@clodocapeo",
310
- "pulsar-bundle-full",
311
- "binaries",
312
- )
145
+ ? resolve(process.resourcesPath, "app.asar.unpacked", "node_modules",
146
+ "@clodocapeo", "pulsar-bundle-full", "binaries")
313
147
  : undefined;
314
-
315
148
  const pulsar = await spawn({ binariesPath });
316
149
  ```
317
150
 
318
- ### Installer size
319
-
320
- A typical Electron app with `pulsar-bundle-full` in NSIS comes out
321
- **around 200 MB** installed (your app code + Electron + Pulsar full
322
- bundle). If that matters, ship `pulsar-bundle` (light) and
323
- implement scene composition client-side; if it doesn't, the full
324
- bundle gives you the most expressive scene model.
151
+ Verify the final installer and deployed bytes. Use actual ZIP/install size
152
+ instead of historical full-bundle estimates. Preserve the full helper/resource
153
+ layout when staging a sidecar runtime.
325
154
 
326
- ## CI / offline / mirror
155
+ ## Offline and mirror installs
327
156
 
328
- Same env vars as the light bundle:
157
+ `PULSAR_BUNDLE_SKIP_POSTINSTALL=1` skips download.
158
+ `PULSAR_BUNDLE_DOWNLOAD_URL` selects a trusted matching **full** archive.
329
159
 
330
- ```bash
331
- # CI: install but skip the 150 MB download
332
- PULSAR_BUNDLE_SKIP_POSTINSTALL=1 npm ci
160
+ Both light and full postinstall scripts read the same override name.
161
+ An override is not automatically scoped to this package in a workspace install.
162
+ Do not send a full ZIP to both installers unintentionally.
333
163
 
334
- # Internal mirror
335
- PULSAR_BUNDLE_DOWNLOAD_URL=https://my-mirror.internal/pulsar/v1.0.0-full.zip npm install
336
- ```
337
-
338
- `PULSAR_BUNDLE_DOWNLOAD_URL` overrides the URL for **this package
339
- only** — the light and full bundles each have their own download URL,
340
- so a `pulsar-bundle` install in the same workspace fetches its own
341
- zip from the default GitHub Releases path.
164
+ The capture-test-only `PULSAR_BUNDLE_FULL_BINARIES_PATH` is read by the test
165
+ harness, not by public `spawn()`; applications use `binariesPath`.
342
166
 
343
167
  ## Troubleshooting
344
168
 
345
- In addition to everything in the
346
- [light bundle's troubleshooting](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle#troubleshooting):
347
-
348
- ### Browser source shows a blank / black square
349
-
350
- CEF subprocess failed to start. Common causes:
351
-
352
- 1. **`pulsar-browser-page.exe` missing from `obs-plugins/64bit/`** —
353
- antivirus may have quarantined it. Restore + whitelist.
354
- 2. **GPU disabled in CEF.** On some virtualised / RDP environments
355
- CEF can't initialise its GPU process. The `obs-browser` plugin
356
- logs the failure to stderr — capture it via `onLog` and look for
357
- `gpu_process_host`.
358
- 3. **URL not reachable.** Check `pulsar.client.obs.call("GetInputSettings", { inputName })`
359
- matches what you set, and that the URL responds with `200 OK`
360
- (CEF won't render a 404 page by default).
361
-
362
- ### `Helper process exited with code 63`
363
-
364
- This is the CEF subprocess crashing on launch. **Almost always**
365
- means `pulsar-browser-page.exe` is in the wrong directory — it must
366
- be in `obs-plugins/64bit/` next to `libcef.dll`, not in
367
- `bin/64bit/`. The full bundle's CMake places it correctly; if you
368
- re-stage the bundle by hand, preserve that layout.
369
-
370
- ### Audio from a `browser_source` is muffled / out of sync
371
-
372
- Browser source audio goes through CEF's audio pipeline + libobs's
373
- WASAPI graph — two A/V resync paths. Mitigations:
374
-
375
- - Set `restart_when_active: false` so the browser process keeps
376
- running across scene changes (avoids re-handshake stalls).
377
- - Pin the page's audio sample rate to 48000 to match libobs's
378
- audio mix bus.
379
- - For CEF audio + scene-A-to-scene-B transitions, set the source's
380
- audio sync offset via the v5 `SetInputAudioSyncOffset` request.
381
-
382
- ### `vlc_source` shows nothing
383
-
384
- The bundled libVLC is stripped of optional codec support to keep the
385
- zip size manageable. If your media file plays in standalone VLC but
386
- not in Pulsar, the codec is probably not in the bundle. Workarounds:
387
- re-encode to H.264/AAC, or use the FFmpeg muxer source
388
- (`ffmpeg_source`) which ships with the light bundle anyway.
389
-
390
- ## Versioning
391
-
392
- Tracks `pulsar-client` and `pulsar.exe` in lockstep — `1.0.0` of this
393
- package downloads `pulsar-windows-x64-full-v1.0.0.zip` and depends on
394
- `@clodocapeo/pulsar-client@1.0.0`.
395
-
396
- Bump light, full, and client together; npm semver resolution rejects
397
- mixed versions in a workspace.
398
-
399
- ## Compatibility
400
-
401
- | | |
402
- |---|---|
403
- | OS | Windows 10/11 x64 only |
404
- | Node | ≥ 18 |
405
- | Module system | ESM only |
406
- | CEF | bundled — version pinned by upstream `obs-browser` at the recorded SHA |
407
- | Antivirus | Whitelist the `binaries/` directory; CEF helper exes are common false positives |
408
-
409
- ## Licence
410
-
411
- [GPL-2.0-or-later](./LICENSE).
412
-
413
- This package bundles `pulsar.exe`, libobs + Pulsar plugins, CEF, and
414
- libVLC. The aggregate is distributed under GPL-2.0-or-later;
415
- individual components retain their upstream notices:
416
-
417
- | Component | Licence |
169
+ | Symptom | Checks |
418
170
  |---|---|
419
- | libobs, obs-studio plugins, Pulsar plugins | GPL-2.0-or-later |
420
- | CEF (Chromium Embedded Framework) | BSD-3-Clause |
421
- | libVLC | LGPL-2.1-or-later |
422
-
423
- Source for the GPL components is available at
424
- <https://github.com/ZabLaboratory/Pulsar> at the matching version
425
- tag.
426
-
427
- ### Bundling Pulsar in a non-GPL application
428
-
429
- Same four invariants as the light bundle:
430
-
431
- 1. **Process boundary.** Always spawn `pulsar.exe` as a separate OS
432
- process. Never `LoadLibrary` / `dlopen` `pulsar.exe`,
433
- `libcef.dll`, `pulsar-*.dll`, or any libobs binary.
434
- 2. **WebSocket-only IPC.** No FFI, no shared memory, no native
435
- bindings.
436
- 3. **No FFI surface on Pulsar's side.** Don't add
437
- `__declspec(dllexport)` to any plugin. Pulsar's CI gates this.
438
- 4. **No source copy-paste.** Don't include lines copied from
439
- libobs / obs-websocket / obs-browser / CEF source trees in your
440
- application.
441
-
442
- Read [`LICENSE-INVARIANTS.md`](https://github.com/ZabLaboratory/Pulsar/blob/main/LICENSE-INVARIANTS.md)
443
- on the Pulsar repo for the full contract, then
444
- [`CONSUMER-AUDIT.md`](https://github.com/ZabLaboratory/Pulsar/blob/main/CONSUMER-AUDIT.md)
445
- for the empirical checklist your application's CI must enforce.
446
-
447
- If you only need the typed client without any GPL binary, use
448
- [`@clodocapeo/pulsar-client`](https://www.npmjs.com/package/@clodocapeo/pulsar-client)
449
- instead — it's MIT.
450
-
451
- For the binary without browser sources / CEF / VLC, use
452
- [`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle).
171
+ | Browser black/frozen | Scene URL/content, source settings, real recorded frames and helper logs. HTTP success alone is insufficient. |
172
+ | Helper launch/exit error | Matching binary dependencies, one browser loader, correct CEF/helper/resource locations; an exit code alone is not a root-cause proof. |
173
+ | CEF GPU unavailable | Actual adapter/driver/session capability; do not relabel software rendering as accelerated proof. |
174
+ | Text missing | Font and registered text kind on the target machine. |
175
+ | VLC source unavailable | Module plus actual libVLC/runtime/codec availability. |
176
+ | Effect filter absent | Validated SDK capability; no SDK/model is bundled. |
177
+ | Recording lost after shutdown | Persistent output path and awaited finalization before child termination. |
178
+
179
+ The [capture/PGM suite](../capture-pgm-compat/README.md) distinguishes black,
180
+ frozen and healthy recordings. The [embedding guide](../../docs/PRISM-EMBEDDING.md)
181
+ covers artifact verification and multi-instance ownership.
182
+
183
+ ## Version and license
184
+
185
+ The 3.0.0 package downloads the v3.0.0 full archive and pins client 3.0.0.
186
+ npm may resolve multiple versions; explicit version matching remains the
187
+ integrator's responsibility.
188
+
189
+ The runtime bundle is [GPL-2.0-or-later](LICENSE). Component notices retain
190
+ their original licenses, and the client remains MIT. Read the
191
+ [distribution constraints](../../LICENSE-INVARIANTS.md) and
192
+ [consumer audit](../../CONSUMER-AUDIT.md); this README does not promise a
193
+ legal outcome for an arbitrary non-GPL host.
package/dist/spawn.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { type ChildProcess } from "node:child_process";
2
- import { PulsarClient } from "@clodocapeo/pulsar-client";
2
+ import { PulsarClient, type PulsarPrismLogEvent } from "@clodocapeo/pulsar-client";
3
3
  export interface SpawnOptions {
4
4
  /** Override the directory containing bin/64bit/pulsar.exe.
5
5
  * Default: <package>/binaries (populated by postinstall). */
6
6
  binariesPath?: string;
7
7
  /** Extra env vars for pulsar.exe. Common ones:
8
+ * PULSAR_RUNTIME_INSTANCE_ID validated identity (auto-generated by spawn)
9
+ * PULSAR_RUNTIME_DIR explicit per-instance working directory
10
+ * PULSAR_LEGACY_ALIAS required | dedicated | off (default opportunistic)
8
11
  * PULSAR_CAPTURE_WINDOW "<title>:<class>:<exe>" for window_capture
9
12
  * PULSAR_FPS 30 / 60 / etc.
10
13
  * PULSAR_VIDEO_BITRATE kbps
@@ -18,6 +21,8 @@ export interface SpawnOptions {
18
21
  readyTimeoutMs?: number;
19
22
  /** Optional log forwarder. Receives one stdout/stderr line at a time. */
20
23
  onLog?: (stream: "stdout" | "stderr", line: string) => void;
24
+ /** Optional structured projection of runtime lifecycle/native log lines. */
25
+ onPrismLog?: (event: PulsarPrismLogEvent) => void;
21
26
  /** Test-only escape hatch: spawn a different executable instead of
22
27
  * the bundled pulsar.exe. The launched process must still print the
23
28
  * ready marker on stdout and create obs-websocket/config.json under
@@ -41,6 +46,10 @@ export interface SpawnedPulsar {
41
46
  /** libobs version string parsed from the boot log
42
47
  * (e.g. "32.1.2-1-g8c23ba721-pulsar"). */
43
48
  libobsVersion: string;
49
+ /** Validated identity of this isolated Pulsar process. */
50
+ runtimeInstanceId: string;
51
+ /** Process-local cwd/config/log/recording namespace. */
52
+ runtimeDir: string;
44
53
  /** Disconnect the WS client and terminate pulsar.exe. Resolves once
45
54
  * the process has exited. Idempotent. */
46
55
  shutdown(): Promise<void>;
@@ -48,10 +57,10 @@ export interface SpawnedPulsar {
48
57
  /**
49
58
  * Spawn pulsar.exe and return a connected PulsarClient.
50
59
  *
51
- * The child process is started with its working directory set to the
52
- * binary's location so that the loader resolves obs.dll and the libobs
53
- * plugin scanner finds obs-plugins/64bit/ via the relative path
54
- * convention add_default_module_paths uses.
60
+ * The executable is resolved from the bundle, while each child process is
61
+ * started in its own runtime directory. The native bootstrap resolves OBS
62
+ * plugins from the executable location and uses the runtime directory for
63
+ * config, logs, recordings and namespaced DirectShow mappings.
55
64
  *
56
65
  * @example
57
66
  * const pulsar = await spawn({
@@ -1 +1 @@
1
- {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK5E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQzD,MAAM,WAAW,YAAY;IAC3B;kEAC8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yEAAyE;IACzE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5D;;;;;oBAKgB;IAChB,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,YAAY,CAAC;IAErB;;yDAEqD;IACrD,KAAK,EAAE,YAAY,CAAC;IAEpB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IAEb;+CAC2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IAEtB;8CAC0C;IAC1C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAiH3E"}
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQ5E,OAAO,EAAE,YAAY,EAAsB,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAQvG,MAAM,WAAW,YAAY;IAC3B;kEAC8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yEAAyE;IACzE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5D,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAElD;;;;;oBAKgB;IAChB,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,YAAY,CAAC;IAErB;;yDAEqD;IACrD,KAAK,EAAE,YAAY,CAAC;IAEpB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IAEb;+CAC2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IAEtB,0DAA0D;IAC1D,iBAAiB,EAAE,MAAM,CAAC;IAE1B,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IAEnB;8CAC0C;IAC1C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoR3E"}
package/dist/spawn.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { spawn as childSpawn } from "node:child_process";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { randomUUID } from "node:crypto";
4
+ import { createServer } from "node:net";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
7
  import { fileURLToPath } from "node:url";
5
- import { PulsarClient } from "@clodocapeo/pulsar-client";
8
+ import { PulsarClient, PulsarRuntimeError } from "@clodocapeo/pulsar-client";
6
9
  const __filename = fileURLToPath(import.meta.url);
7
10
  const __dirname = dirname(__filename);
8
11
  /** Default location of the bundled binaries inside this package. */
@@ -12,10 +15,10 @@ const IDLE_MARKER = "ready, idling";
12
15
  /**
13
16
  * Spawn pulsar.exe and return a connected PulsarClient.
14
17
  *
15
- * The child process is started with its working directory set to the
16
- * binary's location so that the loader resolves obs.dll and the libobs
17
- * plugin scanner finds obs-plugins/64bit/ via the relative path
18
- * convention add_default_module_paths uses.
18
+ * The executable is resolved from the bundle, while each child process is
19
+ * started in its own runtime directory. The native bootstrap resolves OBS
20
+ * plugins from the executable location and uses the runtime directory for
21
+ * config, logs, recordings and namespaced DirectShow mappings.
19
22
  *
20
23
  * @example
21
24
  * const pulsar = await spawn({
@@ -25,9 +28,83 @@ const IDLE_MARKER = "ready, idling";
25
28
  * await pulsar.shutdown();
26
29
  */
27
30
  export async function spawn(opts = {}) {
31
+ const emitRuntimeError = (code, message, details = {}) => {
32
+ opts.onPrismLog?.({
33
+ schemaVersion: 1,
34
+ severity: "error",
35
+ domain: "service",
36
+ source: "pulsar.runtime",
37
+ code,
38
+ message,
39
+ context: { action: "spawn" },
40
+ details,
41
+ });
42
+ };
28
43
  const binariesPath = opts.binariesPath ?? DEFAULT_BINARIES;
29
- const cwd = join(binariesPath, "bin", "64bit");
30
- const configPath = join(cwd, "obs-websocket", "config.json");
44
+ const binaryCwd = join(binariesPath, "bin", "64bit");
45
+ // #243: every child gets an explicit identity and a private cwd. The
46
+ // native bootstrap validates the same identifier and holds an instance
47
+ // lock before touching config, logs, recordings or DirectShow mappings.
48
+ const requestedRuntimeId = opts.env?.PULSAR_RUNTIME_INSTANCE_ID;
49
+ const runtimeInstanceId = requestedRuntimeId || `node-${randomUUID().replace(/-/g, "")}`;
50
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(runtimeInstanceId)) {
51
+ emitRuntimeError("PULSAR_RUNTIME_ID_INVALID", `Invalid PULSAR_RUNTIME_INSTANCE_ID '${runtimeInstanceId}'.`, {
52
+ runtimeInstanceId,
53
+ });
54
+ throw new PulsarRuntimeError("PULSAR_RUNTIME_ID_INVALID", "PULSAR_RUNTIME_INSTANCE_ID must match [A-Za-z0-9][A-Za-z0-9._-]{0,63}.", { action: "spawn", runtimeInstanceId });
55
+ }
56
+ const requestedRuntimeDir = opts.env?.PULSAR_RUNTIME_DIR;
57
+ const ownsRuntimeDir = requestedRuntimeDir === undefined || requestedRuntimeDir.length === 0;
58
+ const requestedRuntimeRoot = opts.env?.PULSAR_RUNTIME_ROOT ?? process.env.PULSAR_RUNTIME_ROOT;
59
+ const runtimeRoot = resolve(requestedRuntimeRoot || tmpdir());
60
+ let runtimeDir;
61
+ if (!ownsRuntimeDir) {
62
+ runtimeDir = resolve(requestedRuntimeDir);
63
+ mkdirSync(runtimeDir, { recursive: true });
64
+ }
65
+ else {
66
+ mkdirSync(runtimeRoot, { recursive: true });
67
+ runtimeDir = mkdtempSync(join(runtimeRoot, `pulsar-${runtimeInstanceId}-`));
68
+ }
69
+ const configPath = join(runtimeDir, "obs-websocket", "config.json");
70
+ const cleanupRuntimeDir = () => {
71
+ if (!ownsRuntimeDir)
72
+ return;
73
+ const relativeRuntimeDir = relative(runtimeRoot, runtimeDir);
74
+ if (isAbsolute(relativeRuntimeDir) || relativeRuntimeDir.startsWith("..") ||
75
+ !relativeRuntimeDir.startsWith(`pulsar-${runtimeInstanceId}-`))
76
+ return;
77
+ try {
78
+ rmSync(runtimeDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
79
+ }
80
+ catch {
81
+ // Antivirus/CEF can briefly retain a child file. The path is uniquely
82
+ // generated for this call and can be reaped by the caller later.
83
+ }
84
+ };
85
+ const childEnv = {
86
+ ...process.env,
87
+ ...opts.env,
88
+ PULSAR_RUNTIME_INSTANCE_ID: runtimeInstanceId,
89
+ PULSAR_RUNTIME_DIR: runtimeDir,
90
+ };
91
+ // The child starts in runtimeDir, so an explicitly relative root would be
92
+ // interpreted differently by native code. Publish the absolute root used
93
+ // by this helper whenever it owns the generated namespace or a caller
94
+ // supplied a root, keeping instance/alias leases on the same state tree.
95
+ if (ownsRuntimeDir || requestedRuntimeRoot)
96
+ childEnv.PULSAR_RUNTIME_ROOT = runtimeRoot;
97
+ // An inherited PULSAR_PORT is not a safe default for a second child. Only
98
+ // an explicit per-spawn value opts out of the free-port allocator.
99
+ if (opts.env?.PULSAR_PORT === undefined || opts.env.PULSAR_PORT === "" || opts.env.PULSAR_PORT === "0") {
100
+ try {
101
+ childEnv.PULSAR_PORT = String(await pickFreePort());
102
+ }
103
+ catch (error) {
104
+ cleanupRuntimeDir();
105
+ throw error;
106
+ }
107
+ }
31
108
  // Resolve the executable. Default = bundled pulsar.exe; tests can pass
32
109
  // launchCommand to swap in a fake.
33
110
  let exe;
@@ -37,20 +114,52 @@ export async function spawn(opts = {}) {
37
114
  args = opts.launchCommand.args ?? [];
38
115
  }
39
116
  else {
40
- exe = join(cwd, "pulsar.exe");
117
+ exe = join(binaryCwd, "pulsar.exe");
41
118
  args = [];
42
119
  if (!existsSync(exe)) {
43
- throw new Error(`pulsar.exe not found at ${exe}. ` +
120
+ cleanupRuntimeDir();
121
+ emitRuntimeError("PULSAR_BINARY_UNAVAILABLE", `pulsar.exe not found at ${exe}.`, { binaryPath: exe });
122
+ throw new PulsarRuntimeError("PULSAR_BINARY_UNAVAILABLE", `pulsar.exe not found at ${exe}. ` +
44
123
  `Run \`npm install\` to trigger the postinstall download, ` +
45
- `or set binariesPath to a local checkout.`);
124
+ `or set binariesPath to a local checkout.`, { action: "spawn", binaryPath: exe });
46
125
  }
47
126
  }
48
- const child = childSpawn(exe, args, {
49
- cwd,
50
- env: { ...process.env, ...opts.env },
51
- stdio: ["ignore", "pipe", "pipe"],
52
- windowsHide: true,
53
- });
127
+ let child;
128
+ try {
129
+ child = childSpawn(exe, args, {
130
+ cwd: runtimeDir,
131
+ env: childEnv,
132
+ stdio: ["ignore", "pipe", "pipe"],
133
+ windowsHide: true,
134
+ });
135
+ }
136
+ catch (error) {
137
+ cleanupRuntimeDir();
138
+ throw error;
139
+ }
140
+ // Keep failure cleanup deterministic. In particular, a ready timeout must
141
+ // terminate the child before releasing the generated cwd; otherwise a
142
+ // failed boot can retain the instance lock and leave a partial namespace.
143
+ const terminateChild = async () => {
144
+ if (child.exitCode !== null || child.signalCode !== null)
145
+ return;
146
+ child.kill();
147
+ await new Promise((resolveExit) => {
148
+ if (child.exitCode !== null || child.signalCode !== null) {
149
+ resolveExit();
150
+ return;
151
+ }
152
+ const forceTimer = setTimeout(() => {
153
+ if (child.exitCode === null && child.signalCode === null)
154
+ child.kill("SIGKILL");
155
+ resolveExit();
156
+ }, 5_000);
157
+ child.once("exit", () => {
158
+ clearTimeout(forceTimer);
159
+ resolveExit();
160
+ });
161
+ });
162
+ };
54
163
  const readyTimeout = opts.readyTimeoutMs ?? 30_000;
55
164
  let libobsVersion = "";
56
165
  let resolveReady;
@@ -60,13 +169,30 @@ export async function spawn(opts = {}) {
60
169
  rejectReady = rej;
61
170
  });
62
171
  const watchdog = setTimeout(() => {
63
- rejectReady(new Error(`pulsar.exe did not signal ready within ${readyTimeout}ms`));
172
+ emitRuntimeError("PULSAR_READY_TIMEOUT", `pulsar.exe did not signal ready within ${readyTimeout}ms`, {
173
+ timeoutMs: readyTimeout,
174
+ });
175
+ rejectReady(new PulsarRuntimeError("PULSAR_READY_TIMEOUT", `pulsar.exe did not signal ready within ${readyTimeout}ms`, { action: "spawn", timeoutMs: readyTimeout }));
64
176
  }, readyTimeout);
65
177
  const handleLine = (stream, line) => {
66
178
  if (line.length === 0)
67
179
  return;
180
+ // PULSAR_READY carries the WebSocket password. Keep the raw line only
181
+ // inside this boot parser; every application-visible callback receives a
182
+ // redacted copy.
183
+ const publicLine = redactNativeLine(line);
68
184
  if (opts.onLog)
69
- opts.onLog(stream, line);
185
+ opts.onLog(stream, publicLine);
186
+ opts.onPrismLog?.({
187
+ schemaVersion: 1,
188
+ severity: /error|fail|fatal|refus|declin|unavail/i.test(line) ? "error" : /warn|skip|partial/i.test(line) ? "warning" : "info",
189
+ domain: "service",
190
+ source: "pulsar.process",
191
+ code: "PULSAR_PROCESS_LOG",
192
+ message: publicLine,
193
+ context: { stream },
194
+ details: {},
195
+ });
70
196
  // pulsar-headless prints exactly:
71
197
  // "pulsar-headless: libobs <version> ready, idling (Ctrl+C to exit)"
72
198
  // when boot is complete.
@@ -81,23 +207,74 @@ export async function spawn(opts = {}) {
81
207
  attachLineReader(child, "stderr", (l) => handleLine("stderr", l));
82
208
  child.on("exit", (code, signal) => {
83
209
  clearTimeout(watchdog);
84
- rejectReady(new Error(`pulsar.exe exited prematurely (code=${code}, signal=${signal})`));
210
+ emitRuntimeError("PULSAR_PROCESS_EXITED", `pulsar.exe exited prematurely (code=${code}, signal=${signal})`, {
211
+ exitCode: code,
212
+ signal,
213
+ });
214
+ rejectReady(new PulsarRuntimeError("PULSAR_PROCESS_EXITED", `pulsar.exe exited prematurely (code=${code}, signal=${signal})`, { action: "spawn", exitCode: code, signal }));
215
+ cleanupRuntimeDir();
216
+ });
217
+ child.on("error", (error) => {
218
+ clearTimeout(watchdog);
219
+ emitRuntimeError("PULSAR_PROCESS_ERROR", `pulsar.exe process error: ${error.message}`, {
220
+ message: error.message,
221
+ });
222
+ rejectReady(new PulsarRuntimeError("PULSAR_PROCESS_ERROR", `pulsar.exe process error: ${error.message}`, {
223
+ action: "spawn",
224
+ }));
225
+ cleanupRuntimeDir();
85
226
  });
86
- await ready;
227
+ try {
228
+ await ready;
229
+ }
230
+ catch (error) {
231
+ await terminateChild();
232
+ cleanupRuntimeDir();
233
+ throw error;
234
+ }
87
235
  // obs-websocket writes its config.json on first run -- after the
88
236
  // ready marker, the file is guaranteed to exist with port + password.
89
237
  if (!existsSync(configPath)) {
90
- child.kill();
91
- throw new Error(`obs-websocket config not found at ${configPath} (boot incomplete?)`);
238
+ await terminateChild();
239
+ cleanupRuntimeDir();
240
+ emitRuntimeError("PULSAR_CONFIG_MISSING", `obs-websocket config not found at ${configPath} (boot incomplete?)`, {
241
+ configPath,
242
+ });
243
+ throw new PulsarRuntimeError("PULSAR_CONFIG_MISSING", `obs-websocket config not found at ${configPath} (boot incomplete?)`, { action: "spawn" });
244
+ }
245
+ let config;
246
+ try {
247
+ config = JSON.parse(readFileSync(configPath, "utf8"));
248
+ }
249
+ catch (error) {
250
+ await terminateChild();
251
+ cleanupRuntimeDir();
252
+ const message = `could not parse obs-websocket config at ${configPath}`;
253
+ emitRuntimeError("PULSAR_CONFIG_INVALID", message, { configPath });
254
+ throw new PulsarRuntimeError("PULSAR_CONFIG_INVALID", message, { action: "spawn" });
92
255
  }
93
- const config = JSON.parse(readFileSync(configPath, "utf8"));
94
256
  const port = config.server_port ?? 4455;
257
+ if (!Number.isInteger(port) || port < 1 || port > 65_535) {
258
+ await terminateChild();
259
+ cleanupRuntimeDir();
260
+ const message = `obs-websocket config contains invalid server_port at ${configPath}`;
261
+ emitRuntimeError("PULSAR_CONFIG_INVALID", message, { configPath });
262
+ throw new PulsarRuntimeError("PULSAR_CONFIG_INVALID", message, { action: "spawn" });
263
+ }
95
264
  const password = config.server_password;
96
265
  const client = new PulsarClient();
97
- await client.connect({
98
- url: `ws://127.0.0.1:${port}`,
99
- ...(password !== undefined ? { password } : {}),
100
- });
266
+ client.on("prismLog", (event) => opts.onPrismLog?.(event));
267
+ try {
268
+ await client.connect({
269
+ url: `ws://127.0.0.1:${port}`,
270
+ ...(password !== undefined ? { password } : {}),
271
+ });
272
+ }
273
+ catch (error) {
274
+ await terminateChild();
275
+ cleanupRuntimeDir();
276
+ throw error;
277
+ }
101
278
  let shutdownPromise = null;
102
279
  const shutdown = async () => {
103
280
  if (shutdownPromise)
@@ -109,25 +286,30 @@ export async function spawn(opts = {}) {
109
286
  catch {
110
287
  // Already closed -- swallow.
111
288
  }
112
- if (child.exitCode === null && child.signalCode === null) {
113
- child.kill();
114
- await new Promise((res) => {
115
- if (child.exitCode !== null || child.signalCode !== null)
116
- return res();
117
- child.once("exit", () => res());
118
- // Safety net: a stuck process gets a 5 s grace period before
119
- // force-kill so a pending muxer trailer write can land.
120
- setTimeout(() => {
121
- if (child.exitCode === null && child.signalCode === null) {
122
- child.kill("SIGKILL");
123
- }
124
- }, 5_000);
125
- });
126
- }
289
+ await terminateChild();
290
+ cleanupRuntimeDir();
127
291
  })();
128
292
  return shutdownPromise;
129
293
  };
130
- return { client, child, port, libobsVersion, shutdown };
294
+ return { client, child, port, libobsVersion, runtimeInstanceId, runtimeDir, shutdown };
295
+ }
296
+ function pickFreePort() {
297
+ return new Promise((resolvePort, rejectPort) => {
298
+ const server = createServer();
299
+ server.once("error", rejectPort);
300
+ server.listen(0, "127.0.0.1", () => {
301
+ const address = server.address();
302
+ const port = typeof address === "object" && address !== null ? address.port : 0;
303
+ server.close((error) => {
304
+ if (error)
305
+ rejectPort(error);
306
+ else if (port > 0)
307
+ resolvePort(port);
308
+ else
309
+ rejectPort(new Error("OS returned no loopback port"));
310
+ });
311
+ });
312
+ });
131
313
  }
132
314
  /** Reads `child[stream]` line-by-line and dispatches each completed line. */
133
315
  function attachLineReader(child, stream, onLine) {
@@ -150,4 +332,9 @@ function attachLineReader(child, stream, onLine) {
150
332
  onLine(buf.replace(/\r$/, ""));
151
333
  });
152
334
  }
335
+ function redactNativeLine(line) {
336
+ return line
337
+ .replace(/(password|token|secret|stream[-_ ]?key)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]")
338
+ .slice(0, 512);
339
+ }
153
340
  //# sourceMappingURL=spawn.js.map
package/dist/spawn.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clodocapeo/pulsar-bundle-full",
3
- "version": "1.8.3",
3
+ "version": "3.0.0",
4
4
  "description": "Full Pulsar bundle: pulsar.exe + DLLs + browser sources (CEF) + text + VLC. Same Node spawn() API as @clodocapeo/pulsar-bundle.",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "repository": {
@@ -38,7 +38,7 @@
38
38
  "prepublishOnly": "npm run build"
39
39
  },
40
40
  "dependencies": {
41
- "@clodocapeo/pulsar-client": "1.8.3"
41
+ "@clodocapeo/pulsar-client": "3.0.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@msgpack/msgpack": "^3.0.0",