openvibers/openvibe.sdk Commits

openvibe-sdk/cam: capture(options) on the site's own OVCapture (#238)

OP OpenVibers committed 18 h ago · d883ddcbab · 12 files, +173 −20

* openvibe-sdk/cam: capture(options) on the site's own OVCapture

The SDK's cam entry gains capture(options): it loads the calling site's own /shared/ov-capture.js once and calls OVCapture.open, resolving to a MediaStream. It never loads camera code from another origin. The Cam kit SRI pin follows the new kit shipped by OpenVibe.Cam#157 (url ?v=6D72Zg6MS9RC).

* openvibe-sdk 0.161.0: cam.capture() on the site's own OVCapture

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 2235ee1..7bed442 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,15 @@
# Changelog
++## 0.161.0 — 2026-11-12
++
++- **`openvibe-sdk/cam`: `capture(options)`** opens a camera through the site's own shared capture core (Shared's
++ `ov-capture.js`, OVCapture).
++ - It loads the core once from the calling site's own `/shared/` (never another origin) and resolves to a
++ MediaStream. A page that already has OVCapture uses it.
++ - The options are OVCapture's own: mode, facing, lens, mic, resolution, audio and video.
++- **Cam's in-page kit** (`CAM_KIT_SRC` / `CAM_KIT_SRI`) is re-pinned to the kit Cam serves today, checked against
++ the live file at its versioned address.
++
## 0.160.0 — 2026-11-12
**The agent engine's loop, Dispatcher and Run API kit: `openvibe-sdk/agents/loop`, `/dispatch` and `/api`** (agent-engine.md §§3, 9). A model, tools and the person's words become
ddiff --git a/README.md b/README.md
iindex d512430..4802fc6 100644
---- a/README.md
++++ b/README.md
@@@ -8,12 +8,12 @@
**Install and test:** `npm ci && npm test` (Node 20 or newer)
```bash
--npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.160.0
++npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.161.0
```
> Supported browser and server clients for the OpenVibe platform.
--**Status:** alpha, v0.160.0 (the five 1.0 core entries — `core`, `auth`, `events`, `db` and `media` — are frozen: their exported names change only with a major release; roadmap Wave 2; developer apps from Wave 20; Media objects v2 from Wave 4; the Tools platform API; Search, Chat, AI, Vision and consent clients; sign-in with recovery and a client health check). Every release is tagged (see [CHANGELOG.md](CHANGELOG.md)). Tested against local stub servers and the built-in mock platform only, never against the live platform. Services pin release tags; each repository's `STATUS.json` names the one it uses.
++**Status:** alpha, v0.161.0 (the five 1.0 core entries — `core`, `auth`, `events`, `db` and `media` — are frozen: their exported names change only with a major release; roadmap Wave 2; developer apps from Wave 20; Media objects v2 from Wave 4; the Tools platform API; Search, Chat, AI, Vision and consent clients; sign-in with recovery and a client health check). Every release is tagged (see [CHANGELOG.md](CHANGELOG.md)). Tested against local stub servers and the built-in mock platform only, never against the live platform. Services pin release tags; each repository's `STATUS.json` names the one it uses.
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30.
**License:** MIT ([LICENSE](LICENSE)). This package is a library that apps outside the network embed, so it uses MIT. The OpenVibe services themselves stay AGPL-3.0.
@@@ -22,7 +22,7 @@ npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags
After v0.97.1 is tagged:
```bash
--npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.160.0
++npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.161.0
```
It has no runtime dependencies. It needs Node ≥ 20, or any browser with `fetch`, Web Crypto and `TextDecoder`. There is no build step. The package is CommonJS with ESM entry points (`import` works). Each subpath has its own `.d.ts`. For a page with no bundler, `browser/openvibe-sdk.mjs` is one self-contained ES module of the browser-safe subpaths (see [Browser without a bundler](#browser-without-a-bundler)). It does not depend on `openvibe-contracts` at runtime: it copies the contract types it uses (from the temporary Contracts sharing branch, a devDependency the tests check them against).
@@@ -544,6 +544,16 @@ if (result) await upload(result.file);
Closing Cam resolves to `null`. The calling site owns the file and upload. `openvibe-sdk/cam` also exports
`open`, `loadCamKit`, `CAM_KIT_SRC` and `CAM_KIT_SRI` for direct use.
++`capture(options)` is the direct camera stream, on OpenVibe's one shared capture core: it loads the calling
++site's own `/shared/ov-capture.js` once (every OpenVibe site serves it) and calls `OVCapture.open(options)`,
++so `facing`, `lens`, `mic`, `resolution`, `audio` and `video` behave the same as Live's go-live page:
++
++```js
++import { capture } from 'openvibe-sdk/cam';
++const stream = await capture({ mode: 'camera', facing: 'environment', resolution: '1080' });
++video.srcObject = stream;
++```
++
`openvibe-sdk/cam` loads the camera app's framework-free `<ov-camera>` element from OpenVibe.Cam and types its
attributes, events and methods. It injects the embed script once and resolves to the element class:
ddiff --git a/STATUS.json b/STATUS.json
iindex a8a2af1..e52e399 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -3,7 +3,7 @@
"stage": "alpha",
"code": true,
"wave": "W2",
-- "version": "0.160.0",
++ "version": "0.161.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30",
"deployed": false,
"deployment": "none: a package released as git tags; services and apps pin a tag",
ddiff --git a/browser/openvibe-sdk.mjs b/browser/openvibe-sdk.mjs
iindex 1744a43..2a19edb 100644
---- a/browser/openvibe-sdk.mjs
++++ b/browser/openvibe-sdk.mjs
@@@ -1,4 +1,4 @@
--// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.160.0, MIT). Do not edit.
++// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.161.0, MIT). Do not edit.
// One self-contained ES module: core at the top level plus the browser-safe subpaths as namespaces
// (auth, locker, registry, trust, levels, modules, realtime, media, composer, cam, mediaUpload, ui, uiLayout, community, homeFeed, feed, views, bot, search, bench, meme, vision, themes, elements, design, redact, chat, ai, credentials, jobs, agents, frame, tools, projects, geo, history, userCard, player, agentSheet). No server-only code: no service tokens, JWT verification, events, identity.
/* eslint-disable */
@@@ -68,7 +68,7 @@ const { paginate, offsetPager } = __load(7);
const semver = __load(6);
/** The package version (test/version.test.js keeps it equal to package.json). */
--const SDK_VERSION = '0.160.0';
++const SDK_VERSION = '0.161.0';
module.exports = {
createClient,
@@@ -3517,12 +3517,15 @@ function (module, exports) {
const OV_CAMERA_SRC = 'https://openvibe.cam/embed/ov-camera.js';
// The kit at its versioned URL (the query names the version), so Cloudflare never hands the integrity check an older
// copy cached under the plain URL (Cam #116, 11-09). A Cam release that changes the kit ships with these two lines.
--const CAM_KIT_SRC = 'https://openvibe.cam/embed/cam-kit.js?v=yqqjh1FiP7a4';
--const CAM_KIT_SRI = 'sha384-yqqjh1FiP7a4l9uelJHaryd8Y5IwnHVbqjt1d0AXbgCcytI9Z5MMmItFqbwB4YZG';
++const CAM_KIT_SRC = 'https://openvibe.cam/embed/cam-kit.js?v=6D72Zg6MS9RC';
++const CAM_KIT_SRI = 'sha384-6D72Zg6MS9RCQAiXgnP8R8iz/criDdCCgY48Jb87HRjzkVTvCDCCCmDs0E0RLE3s';
++// The shared capture core, from the calling site's own /shared/, never another origin.
++const CAPTURE_PATH = '/shared/ov-capture.js';
// One promise per src: a second call while the script is in flight gets the same load, never a second tag.
const pending = new Map();
let kitPending = null;
++let capturePending = null;
/** Load Cam's in-page recorder once. The pinned script gives the calling site a File. */
function loadCamKit() {
@@@ -3548,6 +3551,40 @@ function loadCamKit() {
/** Open Cam on this page. Resolves to a File and metadata after Use this, or null on cancel. */
function open(options = {}) { return loadCamKit().then((kit) => kit.open(options)); }
++/**
++ * Load the site's own capture core once (the page may already include it) and open a camera through it.
++ * Resolves to a MediaStream; the options are OVCapture's: mode, facing, lens, mic, resolution, audio, video.
++ */
++function loadCapture() {
++ if (typeof window === 'undefined' || typeof document === 'undefined') return Promise.reject(new Error('openvibe-sdk/cam: capture() needs a browser.'));
++ if (window.OVCapture && typeof window.OVCapture.open === 'function') return Promise.resolve(window.OVCapture);
++ if (capturePending) return capturePending;
++ let src = CAPTURE_PATH;
++ try { src = new URL(CAPTURE_PATH, window.location.href).href; } catch { /* a page without a location */ }
++ capturePending = new Promise((resolve, reject) => {
++ const script = document.createElement('script');
++ script.src = src;
++ script.async = true;
++ script.dataset.ovCapture = '';
++ script.addEventListener('load', () => {
++ if (window.OVCapture && typeof window.OVCapture.open === 'function') resolve(window.OVCapture);
++ else {
++ script.remove();
++ reject(new Error(`openvibe-sdk/cam: ${src} loaded but did not define OVCapture.`));
++ }
++ });
++ script.addEventListener('error', () => {
++ script.remove();
++ reject(new Error(`openvibe-sdk/cam: could not load ${src}`));
++ });
++ (document.head || document.documentElement).appendChild(script);
++ }).finally(() => { capturePending = null; });
++ return capturePending;
++}
++
++/** Open a camera through the site's own OVCapture. Resolves to a MediaStream. */
++function capture(options = {}) { return loadCapture().then((core) => core.open(options)); }
++
async function cameraSettings(token, method, body) {
if (typeof token !== 'string' || !token) throw new TypeError('A person’s Network token is required.');
const response = await fetch('https://openvibe.cam/api/v1/me/camera-settings', {
@@@ -3601,7 +3638,7 @@ function defineOvCamera(options = {}) {
return load;
}
--module.exports = { defineOvCamera, OV_CAMERA_SRC, CAM_KIT_SRC, CAM_KIT_SRI, loadCamKit, open, settings };
++module.exports = { defineOvCamera, OV_CAMERA_SRC, CAM_KIT_SRC, CAM_KIT_SRI, loadCamKit, open, capture, settings };
},
// ── 30: src/media-upload.js ─────────────────────────────────────────
function (module, exports) {
ddiff --git a/docs/api-surface.json b/docs/api-surface.json
iindex a6f6c2f..b23f493 100644
---- a/docs/api-surface.json
++++ b/docs/api-surface.json
@@@ -113,6 +113,7 @@
"CAM_KIT_SRC",
"CAM_KIT_SRI",
"OV_CAMERA_SRC",
++ "capture",
"defineOvCamera",
"loadCamKit",
"open",
@@@ -754,6 +755,7 @@
"CAM_KIT_SRC": "string",
"CAM_KIT_SRI": "string",
"OV_CAMERA_SRC": "string",
++ "capture": "function",
"defineOvCamera": "function",
"loadCamKit": "function",
"open": "function",
ddiff --git a/esm/cam.mjs b/esm/cam.mjs
iindex 9a4a106..d92b57f 100644
---- a/esm/cam.mjs
++++ b/esm/cam.mjs
@@@ -3,6 +3,6 @@ import cjs from '../src/cam/index.js';
export const {
defineOvCamera, OV_CAMERA_SRC, CAM_KIT_SRC, CAM_KIT_SRI, loadCamKit, open,
-- settings,
++ capture, settings,
} = cjs;
export default cjs;
ddiff --git a/package-lock.json b/package-lock.json
iindex 5968d81..f514f28 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-sdk",
-- "version": "0.160.0",
++ "version": "0.161.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-sdk",
-- "version": "0.160.0",
++ "version": "0.161.0",
"license": "MIT",
"bin": {
"openvibe-download": "bin/openvibe-download.js"
ddiff --git a/package.json b/package.json
iindex a7e0732..6e61c04 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-sdk",
-- "version": "0.160.0",
++ "version": "0.161.0",
"description": "Supported browser and server clients for the OpenVibe platform: auth (incl. developer apps), registry discovery, identity, user modules, events, realtime, media, community, Bot robots, search, chat, AI runs, Tools (registry, run API and jobs), VIP memberships and developer projects, the async PostgreSQL data layer (db) and the Valkey cache, limits store, queue and pub/sub, with deadlines, safe retries, problem errors and trace propagation.",
"main": "index.js",
"types": "types/index.d.ts",
ddiff --git a/src/cam/index.js b/src/cam/index.js
iindex f973cf2..9f3cc91 100644
---- a/src/cam/index.js
++++ b/src/cam/index.js
@@@ -20,12 +20,15 @@
const OV_CAMERA_SRC = 'https://openvibe.cam/embed/ov-camera.js';
// The kit at its versioned URL (the query names the version), so Cloudflare never hands the integrity check an older
// copy cached under the plain URL (Cam #116, 11-09). A Cam release that changes the kit ships with these two lines.
--const CAM_KIT_SRC = 'https://openvibe.cam/embed/cam-kit.js?v=yqqjh1FiP7a4';
--const CAM_KIT_SRI = 'sha384-yqqjh1FiP7a4l9uelJHaryd8Y5IwnHVbqjt1d0AXbgCcytI9Z5MMmItFqbwB4YZG';
++const CAM_KIT_SRC = 'https://openvibe.cam/embed/cam-kit.js?v=6D72Zg6MS9RC';
++const CAM_KIT_SRI = 'sha384-6D72Zg6MS9RCQAiXgnP8R8iz/criDdCCgY48Jb87HRjzkVTvCDCCCmDs0E0RLE3s';
++// The shared capture core, from the calling site's own /shared/, never another origin.
++const CAPTURE_PATH = '/shared/ov-capture.js';
// One promise per src: a second call while the script is in flight gets the same load, never a second tag.
const pending = new Map();
let kitPending = null;
++let capturePending = null;
/** Load Cam's in-page recorder once. The pinned script gives the calling site a File. */
function loadCamKit() {
@@@ -51,6 +54,40 @@ function loadCamKit() {
/** Open Cam on this page. Resolves to a File and metadata after Use this, or null on cancel. */
function open(options = {}) { return loadCamKit().then((kit) => kit.open(options)); }
++/**
++ * Load the site's own capture core once (the page may already include it) and open a camera through it.
++ * Resolves to a MediaStream; the options are OVCapture's: mode, facing, lens, mic, resolution, audio, video.
++ */
++function loadCapture() {
++ if (typeof window === 'undefined' || typeof document === 'undefined') return Promise.reject(new Error('openvibe-sdk/cam: capture() needs a browser.'));
++ if (window.OVCapture && typeof window.OVCapture.open === 'function') return Promise.resolve(window.OVCapture);
++ if (capturePending) return capturePending;
++ let src = CAPTURE_PATH;
++ try { src = new URL(CAPTURE_PATH, window.location.href).href; } catch { /* a page without a location */ }
++ capturePending = new Promise((resolve, reject) => {
++ const script = document.createElement('script');
++ script.src = src;
++ script.async = true;
++ script.dataset.ovCapture = '';
++ script.addEventListener('load', () => {
++ if (window.OVCapture && typeof window.OVCapture.open === 'function') resolve(window.OVCapture);
++ else {
++ script.remove();
++ reject(new Error(`openvibe-sdk/cam: ${src} loaded but did not define OVCapture.`));
++ }
++ });
++ script.addEventListener('error', () => {
++ script.remove();
++ reject(new Error(`openvibe-sdk/cam: could not load ${src}`));
++ });
++ (document.head || document.documentElement).appendChild(script);
++ }).finally(() => { capturePending = null; });
++ return capturePending;
++}
++
++/** Open a camera through the site's own OVCapture. Resolves to a MediaStream. */
++function capture(options = {}) { return loadCapture().then((core) => core.open(options)); }
++
async function cameraSettings(token, method, body) {
if (typeof token !== 'string' || !token) throw new TypeError('A person’s Network token is required.');
const response = await fetch('https://openvibe.cam/api/v1/me/camera-settings', {
@@@ -104,4 +141,4 @@ function defineOvCamera(options = {}) {
return load;
}
--module.exports = { defineOvCamera, OV_CAMERA_SRC, CAM_KIT_SRC, CAM_KIT_SRI, loadCamKit, open, settings };
++module.exports = { defineOvCamera, OV_CAMERA_SRC, CAM_KIT_SRC, CAM_KIT_SRI, loadCamKit, open, capture, settings };
ddiff --git a/src/core/index.js b/src/core/index.js
iindex c7e2d76..d9901e4 100644
---- a/src/core/index.js
++++ b/src/core/index.js
@@@ -8,7 +8,7 @@ const { paginate, offsetPager } = require('./paginate');
const semver = require('./semver');
/** The package version (test/version.test.js keeps it equal to package.json). */
--const SDK_VERSION = '0.160.0';
++const SDK_VERSION = '0.161.0';
module.exports = {
createClient,
ddiff --git a/test/cam.test.js b/test/cam.test.js
iindex d3476bc..45d27f7 100644
---- a/test/cam.test.js
++++ b/test/cam.test.js
@@@ -32,9 +32,10 @@ run([
} finally { global.fetch = original; }
}],
['the entry resolves by name and names the loader and the default script', async () => {
-- assert.deepEqual(Object.keys(cam).sort(), ['CAM_KIT_SRC', 'CAM_KIT_SRI', 'OV_CAMERA_SRC', 'defineOvCamera', 'loadCamKit', 'open', 'settings']);
++ assert.deepEqual(Object.keys(cam).sort(), ['CAM_KIT_SRC', 'CAM_KIT_SRI', 'OV_CAMERA_SRC', 'capture', 'defineOvCamera', 'loadCamKit', 'open', 'settings']);
assert.equal(cam.OV_CAMERA_SRC, 'https://openvibe.cam/embed/ov-camera.js');
-- assert.equal(cam.CAM_KIT_SRC, 'https://openvibe.cam/embed/cam-kit.js?v=yqqjh1FiP7a4');
++ assert.equal(cam.CAM_KIT_SRC, 'https://openvibe.cam/embed/cam-kit.js?v=6D72Zg6MS9RC');
++ assert.equal(cam.CAM_KIT_SRI, 'sha384-6D72Zg6MS9RCQAiXgnP8R8iz/criDdCCgY48Jb87HRjzkVTvCDCCCmDs0E0RLE3s');
assert.match(cam.CAM_KIT_SRI, /^sha384-/);
assert.equal(require('openvibe-sdk/cam'), cam);
assert.deepEqual(pkg.exports['./cam'], {
@@@ -47,6 +48,7 @@ run([
assert.equal(esm.OV_CAMERA_SRC, cam.OV_CAMERA_SRC);
assert.equal(typeof esm.defineOvCamera, 'function');
assert.equal(typeof esm.open, 'function');
++ assert.equal(typeof esm.capture, 'function');
}],
['in Node it rejects instead of touching a document', async () => {
@@@ -54,6 +56,7 @@ run([
await assert.rejects(cam.defineOvCamera(), /needs a browser/);
await assert.rejects(cam.defineOvCamera({ src: 'https://example.test/ov-camera.js' }), /needs a browser/);
await assert.rejects(cam.open({ mode: 'video' }), /needs a browser/);
++ await assert.rejects(cam.capture({ mode: 'camera' }), /needs a browser/);
}],
['the recorder loader pins one script and forwards the result', async () => {
@@@ -83,6 +86,39 @@ run([
} finally { if (oldWindow === undefined) delete global.window; else global.window = oldWindow; if (oldDocument === undefined) delete global.document; else global.document = oldDocument; }
}],
++ ['capture loads the site\'s own /shared/ov-capture.js once and calls OVCapture.open', async () => {
++ const oldWindow = global.window;
++ const oldDocument = global.document;
++ const tags = [];
++ let core = null;
++ const listeners = {};
++ const script = { dataset: {}, remove() { this.removed = true; } };
++ global.window = { location: { href: 'https://openvibe.network/studio' } };
++ global.document = {
++ createElement: () => script,
++ head: { appendChild: (tag) => {
++ tags.push(tag.src);
++ core = { open: async (options) => ({ mode: options.mode, getTracks: () => [] }) };
++ global.window.OVCapture = core;
++ queueMicrotask(() => listeners.load());
++ } },
++ documentElement: { appendChild: () => { throw new Error('head exists; documentElement must not be used'); } },
++ };
++ script.addEventListener = (type, fn) => { listeners[type] = fn; };
++ try {
++ const [a, b] = await Promise.all([cam.capture({ mode: 'camera' }), cam.capture({ mode: 'camera_only' })]);
++ assert.equal(tags.length, 1, 'one script, however many calls');
++ assert.equal(tags[0], 'https://openvibe.network/shared/ov-capture.js', 'the calling site\'s own copy');
++ assert.equal(script.dataset.ovCapture, '');
++ assert.equal(script.async, true);
++ assert.equal(a.mode, 'camera');
++ assert.equal(b.mode, 'camera_only');
++ } finally {
++ if (oldWindow === undefined) delete global.window; else global.window = oldWindow;
++ if (oldDocument === undefined) delete global.document; else global.document = oldDocument;
++ }
++ }],
++
['a browser gets one <script> per load and the class; a defined element resolves without another', async () => {
const appended = [];
const listeners = {};
@@@ -134,7 +170,7 @@ run([
['the types file names the runtime exports and the element surface', async () => {
const dts = fs.readFileSync(path.join(ROOT, 'types/cam.d.ts'), 'utf8');
for (const name of [
-- 'OV_CAMERA_SRC', 'defineOvCamera', 'DefineOvCameraOptions', 'CAM_KIT_SRC', 'CAM_KIT_SRI', 'CamKitResult', 'CamKitOptions', 'loadCamKit', 'open(options?',
++ 'OV_CAMERA_SRC', 'defineOvCamera', 'DefineOvCameraOptions', 'CAM_KIT_SRC', 'CAM_KIT_SRI', 'CamKitResult', 'CamKitOptions', 'CaptureOptions', 'CaptureMode', 'capture(options?', 'loadCamKit', 'open(options?',
'OvCameraAttributes', 'OvCameraFacing', 'OvCameraFilter', 'OvCameraTimer', 'OvCameraMode', 'OvCameraUpload',
'OvCameraElement', 'OvCameraCaptureDetail', 'OvCameraErrorCode', 'OvCameraErrorDetail',
'OvCameraCaptureEvent', 'OvCameraErrorEvent', 'OvCameraEventMap', 'HTMLElementTagNameMap',
ddiff --git a/types/cam.d.ts b/types/cam.d.ts
iindex 31b082b..67f8ec7 100644
---- a/types/cam.d.ts
++++ b/types/cam.d.ts
@@@ -44,6 +44,27 @@ export declare function loadCamKit(): Promise<{ open(options?: CamKitOptions): P
/** Open Cam on this page and return a file after Use this; cancellation resolves to null. */
export declare function open(options?: CamKitOptions): Promise<CamKitResult | null>;
++/** `camera` (video and microphone), `camera_only`, or `mic_only`; the default is `camera`. */
++export type CaptureMode = 'camera' | 'camera_only' | 'mic_only';
++/** What to open through the site's own OVCapture; every choice is optional. */
++export interface CaptureOptions {
++ mode?: CaptureMode;
++ /** The front (`user`) or back (`environment`) camera. */
++ facing?: 'user' | 'environment';
++ /** A camera's id from `OVCapture.devices()`; it is asked for exactly. */
++ lens?: string;
++ /** A microphone's id from `OVCapture.devices()`; it is asked for exactly. */
++ mic?: string;
++ resolution?: '480' | '720' | '1080' | number;
++ audio?: boolean;
++ video?: boolean;
++}
++/**
++ * Load the calling site's own `/shared/ov-capture.js` once and call `OVCapture.open(options)`.
++ * The site must serve the shared capture core (every OpenVibe site does).
++ */
++export declare function capture(options?: CaptureOptions): Promise<MediaStream>;
++
/** The front camera (`user`) or the back camera (`environment`). */
export type OvCameraFacing = 'user' | 'environment';