@boldvideo/bold-js

中文文档
2.5.0发布于 2026/10/24MITTypeScriptES Module构建来源已验证近 30 天下载 2663
Bold Logo

@ boldvid (韩语)

中文使用文档
文档目录41 节
  1. 安装
  2. 快速启动
  3. API 参考
  4. 视频
  5. 安全播放
  6. 播放列表
  7. 设置
  8. 查看器 API
  9. 查看器管理
  10. 进展跟踪
  11. 通知首选项
  12. 会话管理
  13. 运行时会话
  14. 服务器系统会话管理
  15. 社区 API
  16. 员额
  17. 评论
  18. 引用收件箱
  19. AI 方法
  20. 语音会议
  21. 聊天
  22. 建议
  23. 搜索
  24. AI 交互跟踪
  25. 视频扫描聊天
  26. 获取对话历史
  27. 答题率
  28. 多回合对话
  29. 对话启动器
  30. 分析
  31. 明确AI 约定(浏览器或服务器)
  32. 遗留的播放和页面浏览
  33. 类型脚本
  34. 从v1.7.x移徙
  35. 断开: 现在所有响应类型都使用骆驼 Case
  36. 方法变化
  37. 类型重命名
  38. 相关链接
  39. 捐款
  40. 警卫
  41. 许可证
Bold Logo

@boldvideo/ bold-js (英语)

用于交互的 JavaScript SDK 粗体 API给自己的商业视频平台提供动力.

Bold JS npm

Follow @veryboldvideo Try Bold Video


安装

npm install @boldvideo/bold-js

快速启动

import { createClient } from '@boldvideo/bold-js';

const bold = createClient('your-api-key');

// Fetch videos
const videos = await bold.videos.list();

// AI-powered recommendations
const recs = await bold.ai.recommendations({ 
  topics: ['sales', 'negotiation'],
  stream: false 
});
console.log(recs.guidance);

API 参考

视频

// List latest videos (default: 12)
const videos = await bold.videos.list();

// With limit (backwards compatible)
const videos = await bold.videos.list(20);

// With filters
const videos = await bold.videos.list({ 
  limit: 20,
  tag: 'sales',
  collectionId: 'col_123',
  viewerId: 'viewer_123'  // Include watch progress
});

// Paginated index (uses /videos endpoint)
const videos = await bold.videos.list({ 
  page: 2,
  tag: 'sales',
  collectionId: 'col_123'
});

// Get a single video by ID or slug
const video = await bold.videos.get('video-id');
const videoBySlug = await bold.videos.get('my-video-slug');

// Search videos
const results = await bold.videos.search('pricing strategies');

安全播放

视频 playbackPolicy: 'signed' 需要一个播放符号。 赤马流 和图像 URL 为这些视频返回 403 。 使用独立的助手代替 手建 Mux URL 时:

import { createClient, streamUrl, muxPlayerProps, thumbnailUrl } from '@boldvideo/bold-js';

const bold = createClient('YOUR_API_KEY'); // Keep your tenant API key server-side.
const { data: video } = await bold.videos.get('video-id');
const src = streamUrl(video); // For an HLS player.
const playerProps = muxPlayerProps(video); // Spread into <MuxPlayer {...playerProps} />.
const poster = thumbnailUrl(video, { width: 640, height: 360, time: 0, fitMode: 'crop' });

streamUrl 保存 API 的 URL, 返回 Mux HLS URL, 并附带任何已提供的内容 playbackToken。 。 。 . muxPlayerProps 返回刚刚 { playbackId } 公共录像; 已签名的视频还可获得回放/音标和明确的海报 从 thumbnail。 。 。 . thumbnailUrl 为公共视频构建 Mux 图像 URL 。 签名 视频返回存储的公众 thumbnail 不变 忽略所有选项数字 : 无法使用查询参数大小或查找已签名的 Mux 图像 .

帮助者接受 Video、引用、搜索来源、建议或自己的 带有所需骆驼Case字段的对象(PlaybackSource(a) 载有下列资料的大赦国际消息来源: 即使没有政策,标志也被视为签名;明确的公共政策需要 玩家道具和缩略图优先。 URL 帮助返回 undefined 当 缺少所需的 URL、 播放 ID 或签名缩略图; 玩家道具省略 字段。 它们的格式是提供的数据,而不是验证证书:没有签名的视频 符号不能通过构建的赤网址播放。 对于一个没有缩略图或 故事板符号,重排完整视频以获取可用的字段.

最后一个 12小时。 。 。 . Video.playbackTokenExpiresAt 是可选的无效 ISO8601 倍贴. 重新调试视频 bold.videos.get() 以新的迹象发誓; 没有刷新端点或自动刷新定时器。 脚本字段无效 公共视频。 不登录标志或标志化的 URL .

播放列表

// List all playlists
const playlists = await bold.playlists.list();

// Get a single playlist with videos
const playlist = await bold.playlists.get('playlist-id');

设置

// Fetch channel settings, menus, and featured playlists
const settings = await bold.settings();

// Access portal hero configuration
if (settings.portal.hero.type === 'custom') {
  // Render custom hero section
}

// Menu items with external link handling
settings.menuItems.forEach(item => {
  // item.blank: true opens in a new window/tab
  // item.isExt: true indicates an external URL
  // item.icon: optional icon path (can be null)
});

查看器 API

管理外部用户并跟踪其视频监视进度. 理想的课程平台 整合与粗体视频.

查看器管理

// Create a viewer (e.g., when user signs up)
const { data: viewer } = await bold.viewers.create({
  name: 'John Doe',
  externalId: 'user_123',  // Your platform's user ID
  email: '[email protected]',
  traits: { plan: 'pro', company_name: 'Acme Inc' }
});

// Find viewer by external ID (common for syncing users)
const { data: viewer } = await bold.viewers.lookup({ externalId: 'user_123' });

// Or find by email
const { data: viewer } = await bold.viewers.lookup({ email: '[email protected]' });

// Update viewer
await bold.viewers.update(viewer.id, { 
  traits: { plan: 'enterprise' }  // Note: traits are replaced, not merged
});

// List all viewers
const { data: viewers } = await bold.viewers.list();

进展跟踪

// Save progress as video plays (call every 5-10 seconds)
await bold.viewers.saveProgress(viewerId, videoId, {
  currentTime: 120,  // seconds
  duration: 600      // total video duration
});

// Mark video complete by setting currentTime = duration
await bold.viewers.saveProgress(viewerId, videoId, {
  currentTime: 600,
  duration: 600
});

// Get progress for a specific video
const { data: progress } = await bold.viewers.getProgress(viewerId, videoId);
console.log(`${progress.percentage}% complete`);

// List all progress for a viewer (e.g., for a course dashboard)
const { data: progress, meta } = await bold.viewers.listProgress(viewerId, {
  collectionId: 'course-collection-id',  // Filter to a course
  completed: false  // Only in-progress videos
});
console.log(`Completed ${meta.completed} of ${meta.total} videos`);

通知首选项

阅读或部分更新查看器的电子邮件并按下通知频道 租户 API 键客户端 :

const preferences = await bold.notifications.getPreferences('viewer-uuid');
console.log(preferences.channels.email, preferences.channels.push);

// Only push is changed; the existing email preference is preserved.
await bold.notifications.updatePreferences('viewer-uuid', {
  channels: { push: true }
});

会话管理

使用 BOLD 会话管理作为无头密码共享预防层. 您的应用程序仍然拥有登录,会员,产品访问,以及员工/管理人员检查. 只有在您的应用决定了查看者可以访问保护后才能拨打 BOLD 内容.

运行时会话

运行时会话呼叫使用客户的上游JWT,而不是BOLD API键. 这个 当上游JWT已经存在时,工厂对于浏览器/本地运行时间代码是安全的 该运行时间.

import { createAuthClient } from '@boldvideo/bold-js';

const auth = createAuthClient({
  tenantSlug: 'hrtu',
  upstreamJwt: outsetaJwt
});

const result = await auth.sessions.create({
  deviceId: browserDeviceId,
  platform: 'web',
  userAgent: navigator.userAgent
});

let sessionId: string | undefined;

if ('challengeRequired' in result) {
  // Show an email-code challenge UI, then verify the code.
  const verified = await auth.challenges.verify(result.challengeId, '123456');
  sessionId = verified.sessionId ?? undefined;
} else if (result.sessionId) {
  sessionId = result.sessionId;
}

if (sessionId) {
  localStorage.setItem('bold_session_id', sessionId);
}

if (sessionId) {
  // Register the session for push notifications.
  await auth.notifications.registerDevice(sessionId, {
    provider: 'expo', // 'expo' | 'fcm' | 'apns'
    token: expoPushToken
  });

  const verification = await auth.sessions.verify(sessionId);
  if (!verification.valid) {
    // session_not_found, session_revoked, or session_expired
  }
}

对于刷新过的JWT,通过每个调用:

await auth.sessions.verify(sessionId, { upstreamJwt: freshJwt });

观众自我管理方法需要政策支持:

const { data: sessions } = await auth.sessions.list();
await auth.sessions.revoke(sessionId);
await auth.sessions.revokeOthers(currentSessionId);

当账户没有会话管理时, 设备注册 返回已输入错误 :

import { SessionManagementUnavailableError } from '@boldvideo/bold-js';

try {
  await auth.notifications.registerDevice(sessionId, {
    provider: 'fcm',
    token: fcmToken
  });
} catch (error) {
  if (error instanceof SessionManagementUnavailableError) {
    console.log(error.status, error.code, error.retryable);
  }
}

服务器系统会话管理

客户服务器可以检查 BOLD 设备状态,撤销会话,并管理 每查看器设备限制 超标或豁免 上游外部ID使用 普通 BOLD 租户 API 密钥。 保留此 API 密钥服务器侧 。 别放 浏览器代码中的 BOLD 租户密钥或管理密钥 .

import { createClient } from '@boldvideo/bold-js';

const bold = createClient(process.env.BOLD_TENANT_API_KEY!);

const { data: state } =
  await bold.sessionManagement.getViewerSessionManagementStateByExternalId(
    outsetaPersonUid
  );

if (state.canUpdateDeviceLimitOverride) {
  await bold.sessionManagement.setViewerDeviceLimitOverrideByExternalId(
    outsetaPersonUid,
    5
  );

  await bold.sessionManagement.clearViewerDeviceLimitOverrideByExternalId(
    outsetaPersonUid
  );
}

if (state.canUpdateSessionManagementExemption) {
  await bold.sessionManagement.setViewerSessionManagementExemptionByExternalId(
    outsetaPersonUid,
    true
  );
}

const { data: sessions } =
  await bold.sessionManagement.listViewerSessionsByExternalId(outsetaPersonUid);

for (const session of sessions) {
  if (session.travelVerdict === 'impossible_travel') {
    // Bold flagged this sign-in as physically implausible.
  }
}

await bold.sessionManagement.revokeAllViewerSessionsByExternalId(
  outsetaPersonUid,
  { reason: 'outseta:membership_changed' }
);

setViewerDeviceLimitOverrideByExternalId 需要一个正的安全整数. 使用 clearViewerDeviceLimitOverrideByExternalId 明确清除.

每届会议的项目都包含 travelVerdict: 'impossible_travel' | null - 粗体货 权威的不可能旅行信号,由这些管理员范围显示 届会管理方法。 这是一个 仅标签: SDK 永远不会返回 坐标,生IP,或导出距离/速度. 没错 null 当 签入时没有标出标记(以及字段前的后端).

服务器侧会话管理方法无法更新 JWT 配置, 账户 特性标记、会话 TTL 或电子邮件模板设置。 这些仍然是BOLD操作员 控制系统.


社区 API

以文章、评论和反应建立社区特色。 所有写操作都需要一个 viewerId (表演动作的观众).

员额

// List posts (optionally filter by category)
const { data: posts } = await bold.community.posts.list({ 
  category: 'announcements',
  limit: 20,
  offset: 0,
  viewerId: 'viewer-uuid'  // Include viewerReacted in response
});

// Get a single post with comments
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');

// Create a post and notify viewers by their customer external IDs.
const { data: newPost, mentions } = await bold.community.posts.create('viewer-uuid', {
  content: 'Hello community! **Markdown** supported.',
  category: 'general',
  mentions: ['customer-user-123']
});
console.log(mentions?.skipped); // malformed or unresolved external IDs

// Update a post (owner or admin only)
await bold.community.posts.update('viewer-uuid', 'post-id', {
  content: 'Updated content'
});

// Delete a post (owner or admin only)
await bold.community.posts.delete('viewer-uuid', 'post-id');

// Pin or unpin a post (server only). Use a community moderation key: a
// single-purpose key that can pin and unpin posts and nothing else, so it is
// safe to keep in your server's environment. Ask Bold support to issue one.
// Check that the acting member may moderate first; authorship is not enough.
const admin = createClient(process.env.BOLD_API_KEY, {
  adminApiKey: process.env.BOLD_COMMUNITY_MODERATION_KEY,
});
const { data: pinned } = await admin.community.posts.setPinned('post-id', true);
console.log(pinned.pinned, pinned.pinnedAt); // re-pinning keeps the original pinnedAt
await admin.community.posts.setPinned('post-id', false);

// React to a post (toggle like/unlike)
const reaction = await bold.community.posts.react('viewer-uuid', 'post-id');
console.log(reaction.reacted, reaction.reactionsCount);

评论

// Create a comment on a post
const { data: comment } = await bold.community.comments.create(
  'viewer-uuid',
  'post-id',
  { content: 'Great post!', mentions: ['customer-user-456'] }
);

// Reply to a comment (nested)
const { data: reply } = await bold.community.comments.create(
  'viewer-uuid',
  'post-id',
  { content: 'I agree!', parentId: 'parent-comment-id' }
);

// Delete a comment (owner or admin only)
await bold.community.comments.delete('viewer-uuid', 'comment-id');

// React to a comment (toggle)
const reaction = await bold.community.comments.react('viewer-uuid', 'comment-id');

// Read nested reaction state on comments/replies (viewerId enables viewerHasReacted)
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');
for (const thread of post.comments.items ?? []) {
  console.log(thread.createdAt, thread.reactions.count, thread.reactions.viewerHasReacted);
}

引用收件箱

const { data: mentionItems, meta } = await bold.community.mentions.list(
  'viewer-uuid',
  { page: 1, pageSize: 20 }
);

const { data: unread } =
  await bold.community.mentions.unreadCount('viewer-uuid');

await bold.community.mentions.markRead('viewer-uuid', {
  ids: mentionItems.map((mention) => mention.id)
});

// Or mark every unread mention as read.
await bold.community.mentions.markRead('viewer-uuid', { all: true });

可选 mentions 只有在创建员额/评论时才返回结果 请求包括: mentions 阵列。 提供最多25个粗体工艺 外部 ID ; 格式不正确、 未解决、 或顶级以外的 ID 出现在 skipped, (中文(简体) ), 重复和自我记忆则默默地被省略。 从未跳过的身份 失败内容创建 .

提及 excerpt 价值观是简单、无卫生的内容。 逃脱或消毒 它们在渲染为 HTML 之前.


AI 方法

聊天、搜索和建议都支持流(默认)和非流模式.

语音会议

在不管理WebRTC,麦克风音轨或字幕片段的情况下,与单一视频交谈. 语音需要账户的AI和语音功能,视频记录以及浏览器 在安全上下文(HTTPS,或用于开发的本地主机)中带有麦克风许可. 工厂在服务器渲染过程中同步且安全可调用;仅限 start() 使用浏览器 API. 不需要框架或额外的依赖.

测试准确公布的 npm 释放的操作员专用页面,请参见 语音接收装置。 。 。 . 其记录的合成检查不能确定真正的提供者或Safari/iPhone的接受.

使用账户设置来决定是否显示语音控制 。 失踪或失实 值保持语音隐藏;当会话开始时,经纪人仍然强制访问.

const settings = await bold.settings();
const voiceEnabled = settings.data.account.voice?.enabled === true;
const session = bold.ai.voice.createSession({
  videoId: 'lesson-slug',
  viewer: 'customer-123', // Optional viewer UUID or external ID
  onStatus: (status) => { statusLabel.textContent = status; },
  onCaptions: (turns) => {
    // Each turn has a stable id, speaker ('user' or 'assistant'), text,
    // startedAt/updatedAt in milliseconds, and seekable timestamp segments.
    captions.textContent = turns.map(turn => `${turn.speaker}: ${turn.text}`).join('\n');
  },
  onEnded: (reason) => console.log('Voice ended:', reason),
  onError: (error) => { errorLabel.textContent = error.message; },
});

// Start directly from a user gesture. Handle rejection even when using onError.
startButton.onclick = () => { void session.start().catch(() => {}); };
muteButton.onclick = () => session.setMuted(!session.muted);
endButton.onclick = () => { void session.end(); };

// Optional animation-frame polling: normalized RMS amplitudes, not volume settings.
const { input, output } = session.getAudioLevels();

// In your component unmount / video replacement callback, call session.dispose().

createSession(options) 返回 a 单用途 VoiceSession。创建新手柄 重新连接。 经纪人的请求发生在里面 start(),在麦克风许可后 和社民党谈判;来电者不供应社民党. 可选 viewerProfile 接受一个 字符串或字符串、数字和布尔值的对象,不改变而转发 它的钥匙。 查看器配置需要账户的查看器特性.

  • 只读状态 : status, (中文(简体) ), sessionId (直到经纪人回复为止), muted。 。 。 .
  • 状态 : idle → connecting → live → ending → ended; 远程关闭可能去 直接转到 ended。 。 。 .
  • start(): Promise<void> 解析何时存在,或何时取消 end()编号 :dispose()。 。 。 . 检查 session.status 如果后续代码需要实时连接。 设置失败 拒绝和呼叫 onError。并行开始分享一次尝试.
  • setMuted(boolean) 本地禁用麦克风音轨, 包括启动前的设定 .
  • setPlaybackState({ playing, currentTime }) 哑声麦克风和辅助音频 视频播放时 恢复观众的麦克风哑弹选择, 与助理分享视频位置。 调用游戏,暂停,结束,和 寻求事件(不是每次时间更新). currentTime 必须是有限、非负性的 秒; 上下文以整秒精度被去除。 启动前更新为 立即应用到新媒体上;只有最新的上下文在直播时发送. 结束后的电话被忽略 。 此方法不操作您的视频播放器 .
  • end(): Promise<void> 立即停止麦克风/音频,最多三秒钟 以接近确认。 它是一能的;完成并不确认最后账单.
  • dispose() 默默地立即释放出一切,包括在设置或结束期间. SDK也处理上 pagehide;它无法检测框架卸载.
  • 最终原因 : user, (中文(简体) ), idle, (中文(简体) ), time, (中文(简体) ), server, (中文(简体) ), connection, (中文(简体) ), error。 。 。 .

连接设置有60秒的最后期限 经麦克风许可后; 取消 并及时解决未解决的许可问题,并停止任何给予的麦克风音轨 等会 对付费的会话创建没有自动重复。 SDK执行 服务器在页面运行时的最大持续时间和闲置限制。 可听到的投入/产出或 笔录 dertas 和视频回放计数为活动; 输入的无声音频帧不计数 . 暂停视频重新启动闲置窗口; 回放从未延长最长持续时间 。 浏览器计时器 可以被节制或中止,因此后端限制仍然对计费具有权威性.

经纪人的失败是 VoiceAPIError 可选实例 status, (中文(简体) ), code, (中文(简体) ), retryable, (中文(简体) ), retryAfter (raw HTTP 头条), resetsOn,以及 originalError。这包括 voice_allowance_exceeded (402), (中文(简体) ), voice_session_limit (409个)和 rate_limited (429) (中文(简体) ). 麦克风、回放和WebRTC故障是普通的 Error 实例。 提供者错误 事件调用 onError 但不要自动结束一个活的会话.

说明和寻找按钮. 1500米以内同一发言者产生的碎片 同一回合,包括发言者重叠时. turn.segments 包含 { type: 'text', text } 财务报告和审定财务报表 { type: 'timestamp', text, seconds } 条目。 显示 a 时间戳作为按钮,其点击设置您的视频播放器 currentTime 改为 seconds。 。 。 . 两者 m:ss 财务报告和审定财务报表 h:mm:ss 获得支持,包括: 0:00。说明文本不可信; 使用文本节点或您框架的逃避文本渲染, 而不是 innerHTML。 。 。 . 纯洁的助手 appendVoiceCaption(turns, speaker, delta, now) 财务报告和审定财务报表 splitVoiceTimestamps(text) 也被导出。 SDK不控制你的视频 玩家或注入特定应用程序的问候.

如果提供者要求代表团,SDK用固定的英语回复 只有录音记录: " 只有这种录像记录。 从它的回答, 或者说视频没有覆盖它。” 此协议响应也用非英语发送 届会; VoiceSessionOptions 不显示语言或指令覆盖.

反应清净. 保持手柄在回放, 创建它的效果,从 一个点击处理器。 为每个挂载/视频创建新手柄( 包括严格模式) 效果重放 ; 不要以效果开始麦克风捕获 .

const voiceRef = useRef<VoiceSession | null>(null);

useEffect(() => {
  const session = bold.ai.voice.createSession({
    videoId,
    onStatus: setStatus,
    onCaptions: setCaptions, // useState<readonly VoiceCaptionTurn[]>([])
    onError: (error) => setError(error.message),
  });
  voiceRef.current = session;
  return () => {
    session.dispose();
    voiceRef.current = null;
  };
}, [bold, videoId]); // Keep the bold client stable across renders.

// In a button handler:
const startVoice = () => { void voiceRef.current?.start().catch(() => {}); };

导入 VoiceSession 财务报告和审定财务报表 VoiceCaptionTurn 作为类型从 @boldvideo/bold-js,以及 反应钩从 react。根据需要,将显示的状态/标题重置到视频更改上.

聊天

整个图书馆的谈话AI 深入的QQA 在你的整个视频库.

// Streaming (default)
const stream = await bold.ai.chat({ prompt: 'How do I price my SaaS?' });

for await (const event of stream) {
  if (event.type === 'text_delta') process.stdout.write(event.delta);
  if (event.type === 'sources') console.log('Sources:', event.sources);
}

// Non-streaming
const response = await bold.ai.chat({ 
  prompt: 'What are the best closing techniques?',
  stream: false 
});
console.log(response.content);

选项 :

参数类型说明
promptstring用户的问题( 需要)
streambooleantrue (默认)SSE, false 给 JSON 的
videoIdstring如果提供的话, 视频的范围 而不是整个库
currentTimenumber当前播放位置( 只使用 videoId(中文(简体) )
conversationIdstring继续现有对话的通行证
collectionIdstring过滤到特定收藏
tagsstring[]通过标签过滤

建议

获得基于话题的AI动力视频建议——理想的个性化学习路径,考试准备,以及内容发现.

// Streaming (default)
const stream = await bold.ai.recommendations({ 
  topics: ['contract law', 'ethics', 'client management'],
});

for await (const event of stream) {
  if (event.type === 'recommendations') {
    event.recommendations.forEach(rec => {
      console.log(`${rec.topic}:`);
      rec.videos.forEach(v => console.log(`  - ${v.title} (${v.relevance})`));
    });
  }
  if (event.type === 'text_delta') {
    process.stdout.write(event.delta); // AI guidance
  }
}

// Non-streaming
const response = await bold.ai.recommendations({ 
  topics: ['sales', 'marketing'],
  stream: false 
});
console.log(response.guidance);
console.log(response.recommendations);

选项 :

参数类型说明
topicsstring[]寻找(需要)内容的主题
streambooleantrue (默认)SSE, false 给 JSON 的
limitnumber每个主题的最大视频( 默认: 5, 最大: 20)
collectionIdstring过滤到特定收藏
tagsstring[]通过标签过滤
includeGuidanceboolean包含 AI 学习路径描述( 默认: 真)
contextAIContextMessage[]先前的对话开始后续

以AI生成的简短摘要快速语义搜索.

const stream = await bold.ai.search({ 
  prompt: 'pricing strategies',
  limit: 10 
});

for await (const event of stream) {
  if (event.type === 'sources') {
    console.log(`Found ${event.sources.length} results`);
  }
}

AI 交互跟踪

chat (全图书馆或录像机), ask, (中文(简体) ), coach,以及 search 接受一个 选项 requestId UUID, 发送为 request_id。每次故意生成一次 用户动作并在重试此动作时重新使用。 新问题、后续行动或 故意重复搜索获得一个新的UUID. SDK 不生成ID或重试 自动请求.

search 还接受 searchMode: 'preview' | 'settled'编号 search_mode。 。 。 . 关闭它保存服务器的 settled 默认。 使用 preview 临时 搜索和 settled 最终提交的搜索文件; 处理最后提交的文件 以它自己的UUID作为新的行动.

const action = {
  prompt: 'pricing strategies',
  requestId: crypto.randomUUID(),
  searchMode: 'settled' as const,
  stream: false as const,
};
const response = await bold.ai.search(action);
// If retrying this action, pass the same action.requestId.
console.log(response.interactionId);

贾森 AIResponse 和SSE 组合键 message_complete 事件曝光 interactionId?: string | null (自 interaction_id) (中文(简体) ). 搜索预览 可能返回 null; 旧服务器可能会省略. 这些ID支持 分析 仅复制,而不是回放或模式调用分解。 持久性 是最佳和同步的: 接收ID是 不是一个持久的承认。 。 。 .

这不会改变 videos.search(),该调用 /videos?query=…; SDK 会的 不覆盖单独的关键字 /search 终点.

聊天,视频聊天, ask编号 :coach,而AI搜索也接受可选归属: channel: 'portal' | 'embed' | 'api' | 'mcp' | 'unknown', (中文(简体) ), clientName: string,以及 clientVersion: string。这些是作为顶级发送的 channel, (中文(简体) ), client_name,以及 client_version字段中省略;后端正常丢失或 频道值为错误 unknown。归属不影响 requestId 相去相减. 设定 channel: 'portal' 明确在端口服务器代理; 这些 标签是分析元数据,而不是授权信号.

视频扫描聊天

通过传递来谈论一个特定的视频 videoId只使用视频的笔录作为上下文.

const stream = await bold.ai.chat({ 
  videoId: 'video-id',
  prompt: 'What is discussed at the 5 minute mark?' 
});

for await (const event of stream) {
  if (event.type === 'text_delta') process.stdout.write(event.delta);
}

// With playback context (coming soon)
const stream = await bold.ai.chat({ 
  videoId: 'video-id',
  prompt: 'What does she mean by that?',
  currentTime: 847  // seconds
});

获取对话历史

通过 ID 获取对话以显示消息历史 :

const conversation = await bold.ai.getConversation('550e8400-e29b-41d4-a716-446655440000');

console.log(`Created: ${conversation.createdAt}`);
for (const msg of conversation.messages) {
  console.log(`${msg.role}: ${msg.content}`);
}

答题率

使用可选 messageId 从非流出 AIResponse 或流线 message_complete 用于评分助手回答的事件 :

const answer = await bold.ai.chat({ prompt: 'How do I get started?', stream: false });
if (answer.messageId) {
  const feedback = await bold.ai.rateMessage(answer.messageId, {
    rating: 'down',
    reasons: ['too_short'],
    note: 'Please include an example.',
  });
  await bold.ai.clearRating(feedback.messageId);
}

rating 实值 'up' 或者说 'down'可选 reasons 接受 'wrong', (中文(简体) ), 'not_in_video', (中文(简体) ), 'too_short', (中文(简体) ), 'off_topic',以及 'other'· ; note 是可选的. rateMessage 返回时 MessageFeedback 与 messageId, (中文(简体) ), rating, (中文(简体) ), reasons, (中文(简体) ), 和无效 note。 。 。 . clearRating 解析无值。 都抛出 AIAPIError 和404状态,当消息不在呼叫者的账户上.

多回合对话

使用 context 后续问题参数:

const first = await bold.ai.search({ 
  prompt: 'How do indie designers find clients?',
  stream: false 
});

const followUp = await bold.ai.search({
  prompt: 'What about cold outreach specifically?',
  context: first.context,
  stream: false
});

对话启动器

获取对话启动提示, 可用时按收藏解决 返回账户一级启动人,否则:

// Account-level starters (no collectionIds)
const { data: starters } = await bold.conversationStarters.list();

// Resolve starters for one or more collections
const { data: starters } = await bold.conversationStarters.list({
  collectionIds: ['collection-uuid-1', 'collection-uuid-2']
});

starters.forEach((starter) => {
  console.log(starter.text, starter.source, starter.collectionId);
});

分析

明确AI 约定(浏览器或服务器)

bold.trackEngagement(options: TrackEngagementOptions): Promise<void> 发送 POST /api/v1/event 独立于遗产 trackEvent 和它的油门. 导入 TrackEngagementOptions 从 @boldvideo/bold-js。 。 。 .

SDK 字段电线场含义
eventnsource_open 或者说 video_progress
interactionIdinteraction_idAI 响应中所需的交互 UUID
openIdplayback_id每个有意打开的源头都需要新的UUID; 不是一个 Mux 播放回放Id
videoIdvid所需的源视频 ID
viewer?viewer现有查看器 UUID 或外部 ID
watchedSecondswatched_seconds所需经费 video_progress,缺席 source_open; 累计过期 为此打开播放时长秒
// On a deliberate source open, skip if the response has no interaction ID.
if (response.interactionId) {
  const open = {
    interactionId: response.interactionId,
    openId: crypto.randomUUID(),
    videoId: source.videoId,
  };
  void bold.trackEngagement({ ...open, event: 'source_open' });

  // Later, after the caller has measured 12.5 seconds spent playing:
  void bold.trackEngagement({ ...open, event: 'video_progress', watchedSeconds: 12.5 });
}

source_open 只建立开放; 进步需要开放。 维护 计数器,不包括暂停/缓冲时间。 不发送绝对 玩家头姿势 寻找三角洲 或每个样本增量 重排/重排 进度使用相同的 openId;后端保留了累积的最大. 生成 新鲜的 openId 当用户故意再次打开源时.

呼叫是最佳的 并且安全 开火和忘记: 运输错误被抓住, 并且不使用自动重试或共享节流阀。 一项坚定的承诺(包括 HTTP 202)为 不是一个持久的承认后端会验证房客,观众, 和源属性;它重复捕获/公开比赛最多30秒 不创建占位符交互。 服务器代理必须获得 API 密钥和 来自信任的租户/会话背景的浏览器引用,而不是浏览器的主张.

遗留的播放和页面浏览

跟踪视频事件和页面浏览分析.

// Track video events (play, pause, complete, etc.)
bold.trackEvent({
  type: 'play',
  videoId: 'video-id',
  timestamp: 0
});

// Track page views
bold.trackPageView({
  path: '/videos/my-video',
  referrer: document.referrer
});

类型脚本

所有类型都导出为全类型脚本支持 :

import type { 
  Video, 
  Playlist, 
  Settings,
  Portal,
  PortalHero,
  MenuItem,
  AIEvent,
  AIResponse,
  ChatOptions,
  SearchOptions,
  RecommendationsOptions,
  RecommendationsResponse,
  Recommendation,
  Conversation,
  ConversationMessage,
  Source,
  Viewer,
  ViewerProgress,
  ViewerLookupParams,
  ListProgressOptions,
  ListVideosOptions,
  ListVideosLatestOptions,
  ListVideosIndexOptions,
  NotificationChannels,
  NotificationPreferencesResponse,
  NotificationChannelUpdate,
  UpdateNotificationPreferencesData,
  NotificationProvider,
  RegisterDeviceData,
  RegisterDeviceResponse,
  // Community API
  Post,
  PostAuthor,
  Comment,
  ReactionResponse,
  Mention,
  MentionSkippedReport,
  CommunityPostCreateResponse,
  CommunityCommentCreateResponse,
  ListPostsOptions,
  ListMentionsOptions,
  MentionsUnreadCountResponse,
  MarkMentionsReadData,
  MarkMentionsReadResponse,
  CreatePostData,
  UpdatePostData,
  CreateCommentData,
  // Conversation Starters
  ConversationStarter,
  ConversationStarterSource,
  ListConversationStartersOptions
} from '@boldvideo/bold-js';

从v1.7.x移徙

断开: 现在所有响应类型都使用骆驼 Case

所有API响应 (videos, playlist, setters, AI)现在被转换为使用平庸的TypeScript/JavaScript命名:

// Before (v1.7.x and earlier)
video.playback_id
video.published_at
video.stream_url
video.meta_data
settings.featured_playlists
settings.menu_items
settings.theme_config
playlist.is_private

// After (v1.8.0)
video.playbackId
video.publishedAt
video.streamUrl
video.metaData
settings.featuredPlaylists
settings.menuItems
settings.themeConfig
playlist.isPrivate

方法变化

旧新设页:1
bold.ai.ask(opts)bold.ai.chat(opts)ask() 还在工作,但已经贬值
bold.ai.coach(opts)bold.ai.chat(opts)coach() 还在工作,但已经贬值
bold.ai.chat(videoId, opts)bold.ai.chat({ videoId, ...opts })过 videoId 选项中
bold.ai.recommend(opts)bold.ai.recommendations(opts)recommend() 还在工作,但已经贬值

类型重命名

旧类型新类型
AskOptionsChatOptions
RecommendOptionsRecommendationsOptions
RecommendResponseRecommendationsResponse

旧类型仍然作为后向相容性的别名被导出.


捐款

见 捐款额 详细介绍如何为该项目作出贡献.

警卫

见 警卫 安全政策和报告脆弱性.

许可证

麻省理工学院

版本与发布记录

@boldvideo/bold-js 共发布 73 个版本,首次发布于 2023/03/05;过去 12 个月发布了 52 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.5.0latest2026/10/2415 天前
2.4.12026/10/2118 天前
2.4.02026/10/2118 天前
2.3.02026/10/2118 天前
2.2.02026/10/1028 天前
2.1.02026/10/041 个月前

关于 @boldvideo/bold-js 的常见问题

@boldvideo/bold-js 是什么?

@ boldvid (韩语)

如何安装 @boldvideo/bold-js?

在项目目录运行 npm install @boldvideo/bold-js,或使用 pnpm add / yarn add / bun add。

@boldvideo/bold-js 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@boldvideo/bold-js 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@boldvideo/bold-js 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@boldvideo/bold-js 可以商用吗?

@boldvideo/bold-js 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@boldvideo/bold-js 的下载量有多少?

根据 npm 官方下载统计,@boldvideo/bold-js 在 2026/10/09 – 2026/11/07 这 30 天内被下载 2,663 次,日均约 89 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@boldvideo/bold-js 还在维护吗?

最新版本 2.5.0 发布于 2026/10/24(15 天前);至今共发布 73 个版本,其中过去 12 个月发布了 52 个;首次发布于 2023/03/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。