cortico 0.1.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.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/package.json +43 -0
  4. package/src/boot.ts +46 -0
  5. package/src/bot.ts +1342 -0
  6. package/src/config-file.ts +41 -0
  7. package/src/core/README.md +92 -0
  8. package/src/core/billing.ts +26 -0
  9. package/src/core/blobs.ts +106 -0
  10. package/src/core/bus.ts +311 -0
  11. package/src/core/config-schema.ts +201 -0
  12. package/src/core/config.ts +262 -0
  13. package/src/core/core.ts +699 -0
  14. package/src/core/cost.ts +244 -0
  15. package/src/core/event-store.ts +304 -0
  16. package/src/core/fork.ts +113 -0
  17. package/src/core/generation.ts +118 -0
  18. package/src/core/instance-lock.ts +215 -0
  19. package/src/core/ipc-logger.ts +105 -0
  20. package/src/core/language.ts +62 -0
  21. package/src/core/log-context.ts +38 -0
  22. package/src/core/loop.ts +1858 -0
  23. package/src/core/markers.ts +48 -0
  24. package/src/core/prefix.ts +121 -0
  25. package/src/core/run.ts +95 -0
  26. package/src/core/secrets.ts +18 -0
  27. package/src/core/session.ts +56 -0
  28. package/src/core/sessions.ts +261 -0
  29. package/src/core/state.ts +104 -0
  30. package/src/core/template.ts +72 -0
  31. package/src/core/timers.ts +129 -0
  32. package/src/core/tool-log.ts +135 -0
  33. package/src/core/transcript.ts +92 -0
  34. package/src/core/truncate.ts +121 -0
  35. package/src/core/types.ts +1140 -0
  36. package/src/core/usage-log.ts +79 -0
  37. package/src/core/util.ts +386 -0
  38. package/src/deploy-listing.ts +160 -0
  39. package/src/deploy.ts +162 -0
  40. package/src/extensions/README.md +72 -0
  41. package/src/extensions/dry-mount.ts +390 -0
  42. package/src/extensions/manifest.ts +153 -0
  43. package/src/extensions/runtime.ts +42 -0
  44. package/src/extensions.ts +519 -0
  45. package/src/launcher.ts +285 -0
  46. package/src/paths.ts +155 -0
  47. package/src/protocol/open-responses/LICENSE +201 -0
  48. package/src/protocol/open-responses/README.md +14 -0
  49. package/src/protocol/open-responses/context-helpers.ts +27 -0
  50. package/src/protocol/open-responses/context-log.ts +59 -0
  51. package/src/protocol/open-responses/context.ts +89 -0
  52. package/src/protocol/open-responses/generated.ts +115 -0
  53. package/src/protocol/open-responses/index.ts +33 -0
  54. package/src/protocol/open-responses/openapi.json +4230 -0
  55. package/src/protocol/open-responses/stream.ts +146 -0
  56. package/src/protocol/open-responses/tokens.ts +31 -0
  57. package/src/providers/README.md +86 -0
  58. package/src/providers/base.ts +111 -0
  59. package/src/providers/configuration.ts +97 -0
  60. package/src/providers/console/config.ts +21 -0
  61. package/src/providers/console/settings.ts +443 -0
  62. package/src/providers/console/strings.ts +74 -0
  63. package/src/providers/console/types.ts +11 -0
  64. package/src/providers/llamacpp/archive.ts +148 -0
  65. package/src/providers/llamacpp/catalog.ts +247 -0
  66. package/src/providers/llamacpp/config.ts +36 -0
  67. package/src/providers/llamacpp/console/client.ts +6 -0
  68. package/src/providers/llamacpp/console/models-panel.ts +120 -0
  69. package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
  70. package/src/providers/llamacpp/console/server.ts +153 -0
  71. package/src/providers/llamacpp/index.ts +95 -0
  72. package/src/providers/llamacpp/native.ts +51 -0
  73. package/src/providers/llamacpp/options.ts +117 -0
  74. package/src/providers/llamacpp/runtime-store.ts +139 -0
  75. package/src/providers/llamacpp/runtime.ts +214 -0
  76. package/src/providers/llamacpp/server.ts +286 -0
  77. package/src/providers/llamacpp/strings.ts +230 -0
  78. package/src/providers/openai-responses-compat/config.ts +20 -0
  79. package/src/providers/openai-responses-compat/index.ts +89 -0
  80. package/src/providers/openai-responses-compat/native.ts +127 -0
  81. package/src/providers/openai-responses-compat/strings.ts +19 -0
  82. package/src/providers/pricebook.ts +75 -0
  83. package/src/providers/registry.ts +137 -0
  84. package/src/providers/strings.ts +71 -0
  85. package/src/providers/transport/chat.ts +156 -0
  86. package/src/providers/transport/errors.ts +48 -0
  87. package/src/providers/transport/history.ts +68 -0
  88. package/src/providers/transport/native-input.ts +83 -0
  89. package/src/providers/transport/native-types.ts +13 -0
  90. package/src/providers/transport/response-assembly.ts +191 -0
  91. package/src/providers/transport/response-http.ts +196 -0
  92. package/src/providers/transport/response-meters.ts +27 -0
  93. package/src/providers/transport/responses-input.ts +56 -0
  94. package/src/web/README.md +84 -0
  95. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
  96. package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
  97. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
  98. package/src/web/client/console-pages/builtins.ts +10 -0
  99. package/src/web/client/console-pages/context.ts +135 -0
  100. package/src/web/client/console-pages/host.ts +633 -0
  101. package/src/web/client/console-pages/loader.ts +132 -0
  102. package/src/web/client/console-pages/strings.ts +90 -0
  103. package/src/web/client/console-pages/tools/strings.ts +49 -0
  104. package/src/web/client/console-pages/tools/view.ts +215 -0
  105. package/src/web/client/core/api.ts +200 -0
  106. package/src/web/client/core/language.ts +56 -0
  107. package/src/web/client/core/lifecycle.ts +132 -0
  108. package/src/web/client/core/router.ts +199 -0
  109. package/src/web/client/core/stream.ts +157 -0
  110. package/src/web/client/core/websocket.ts +82 -0
  111. package/src/web/client/features/appearance/index.ts +333 -0
  112. package/src/web/client/features/appearance/strings.ts +87 -0
  113. package/src/web/client/features/config/index.ts +11 -0
  114. package/src/web/client/features/config/strings.ts +41 -0
  115. package/src/web/client/features/config/view.ts +428 -0
  116. package/src/web/client/features/core/events.ts +197 -0
  117. package/src/web/client/features/core/index.ts +254 -0
  118. package/src/web/client/features/core/run.ts +56 -0
  119. package/src/web/client/features/core/runlog.ts +214 -0
  120. package/src/web/client/features/core/sessions.ts +53 -0
  121. package/src/web/client/features/core/strings.ts +157 -0
  122. package/src/web/client/features/extensions/index.ts +422 -0
  123. package/src/web/client/features/extensions/strings.ts +164 -0
  124. package/src/web/client/features/feature.ts +93 -0
  125. package/src/web/client/features/live/context.ts +280 -0
  126. package/src/web/client/features/live/fork.ts +156 -0
  127. package/src/web/client/features/live/index.ts +392 -0
  128. package/src/web/client/features/live/onboarding.ts +113 -0
  129. package/src/web/client/features/live/protocol.ts +122 -0
  130. package/src/web/client/features/live/sessions.ts +71 -0
  131. package/src/web/client/features/live/status.ts +68 -0
  132. package/src/web/client/features/live/strings.ts +252 -0
  133. package/src/web/client/features/live/timeline.ts +590 -0
  134. package/src/web/client/features/prompts/editor.ts +292 -0
  135. package/src/web/client/features/prompts/index.ts +346 -0
  136. package/src/web/client/features/prompts/strings.ts +136 -0
  137. package/src/web/client/features/prompts/view.ts +256 -0
  138. package/src/web/client/features/providers/index.ts +100 -0
  139. package/src/web/client/features/providers/strings.ts +23 -0
  140. package/src/web/client/features/settings/general.ts +28 -0
  141. package/src/web/client/features/settings/index.ts +82 -0
  142. package/src/web/client/features/settings/strings.ts +33 -0
  143. package/src/web/client/features/storage/index.ts +1 -0
  144. package/src/web/client/features/storage/strings.ts +45 -0
  145. package/src/web/client/features/storage/view.ts +154 -0
  146. package/src/web/client/features/usage/chart.ts +486 -0
  147. package/src/web/client/features/usage/index.ts +407 -0
  148. package/src/web/client/features/usage/labels.ts +46 -0
  149. package/src/web/client/features/usage/range.ts +36 -0
  150. package/src/web/client/features/usage/state.ts +37 -0
  151. package/src/web/client/features/usage/strings.ts +191 -0
  152. package/src/web/client/features/usage/tooltip.ts +86 -0
  153. package/src/web/client/features/usage/types.ts +96 -0
  154. package/src/web/client/features/worlds/index.ts +424 -0
  155. package/src/web/client/features/worlds/strings.ts +121 -0
  156. package/src/web/client/main.ts +273 -0
  157. package/src/web/client/shell/avatar.ts +233 -0
  158. package/src/web/client/shell/index.ts +549 -0
  159. package/src/web/client/shell/strings.ts +89 -0
  160. package/src/web/client/theme/handoff.ts +43 -0
  161. package/src/web/client/theme/palette.ts +109 -0
  162. package/src/web/client/theme/registry.ts +130 -0
  163. package/src/web/client/theme/storage.ts +72 -0
  164. package/src/web/client/theme/strings.ts +123 -0
  165. package/src/web/client/theme/studio.ts +366 -0
  166. package/src/web/client/ui/actions.ts +109 -0
  167. package/src/web/client/ui/data.ts +185 -0
  168. package/src/web/client/ui/dom.ts +23 -0
  169. package/src/web/client/ui/fields.ts +191 -0
  170. package/src/web/client/ui/format.ts +75 -0
  171. package/src/web/client/ui/icons.ts +183 -0
  172. package/src/web/client/ui/images.ts +91 -0
  173. package/src/web/client/ui/index.ts +89 -0
  174. package/src/web/client/ui/lamp.ts +125 -0
  175. package/src/web/client/ui/log.ts +98 -0
  176. package/src/web/client/ui/overlay.ts +184 -0
  177. package/src/web/client/ui/page.ts +8 -0
  178. package/src/web/client/ui/prompt-input.tsx +311 -0
  179. package/src/web/client/ui/sheet.ts +131 -0
  180. package/src/web/client/ui/strings.ts +71 -0
  181. package/src/web/console-pages.ts +367 -0
  182. package/src/web/files.ts +107 -0
  183. package/src/web/path-picker.ts +296 -0
  184. package/src/web/public/index.html +17 -0
  185. package/src/web/public/styles.css +1715 -0
  186. package/src/web/server.ts +1883 -0
  187. package/src/web/shared/client-panel.ts +603 -0
  188. package/src/web/shared/console-protocol.ts +560 -0
  189. package/src/web/shared/css.d.ts +1 -0
  190. package/src/web/shared/path-picker.ts +23 -0
  191. package/src/web/shared/theme.ts +256 -0
  192. package/src/web/theme-store.ts +37 -0
  193. package/src/world.ts +449 -0
  194. package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
  195. package/src/worlds/bilibili/README.md +176 -0
  196. package/src/worlds/bilibili/audience-admission.ts +995 -0
  197. package/src/worlds/bilibili/client.ts +528 -0
  198. package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
  199. package/src/worlds/bilibili/config.ts +185 -0
  200. package/src/worlds/bilibili/console/client.ts +202 -0
  201. package/src/worlds/bilibili/definition.ts +35 -0
  202. package/src/worlds/bilibili/gift-frame.ts +131 -0
  203. package/src/worlds/bilibili/normalize.ts +452 -0
  204. package/src/worlds/bilibili/overlay/announcement.ts +76 -0
  205. package/src/worlds/bilibili/overlay/assets.ts +82 -0
  206. package/src/worlds/bilibili/overlay/model.ts +494 -0
  207. package/src/worlds/bilibili/overlay/project.ts +202 -0
  208. package/src/worlds/bilibili/overlay/server.ts +286 -0
  209. package/src/worlds/bilibili/overlay/types.ts +217 -0
  210. package/src/worlds/bilibili/overlay/web/app.js +690 -0
  211. package/src/worlds/bilibili/overlay/web/editor.css +297 -0
  212. package/src/worlds/bilibili/overlay/web/editor.html +102 -0
  213. package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
  214. package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
  215. package/src/worlds/bilibili/overlay/web/styles.css +258 -0
  216. package/src/worlds/bilibili/protobuf.ts +123 -0
  217. package/src/worlds/bilibili/wire.ts +94 -0
  218. package/src/worlds/bilibili/world.ts +1389 -0
  219. package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
  220. package/src/worlds/console-fixture/console/client.ts +72 -0
  221. package/src/worlds/console-fixture/world.ts +73 -0
  222. package/src/worlds/index.ts +15 -0
  223. package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
  224. package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
  225. package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
  226. package/src/worlds/minecraft/README.md +917 -0
  227. package/src/worlds/minecraft/blueprint-plan.ts +915 -0
  228. package/src/worlds/minecraft/blueprint-registry.ts +566 -0
  229. package/src/worlds/minecraft/blueprint-repair.ts +236 -0
  230. package/src/worlds/minecraft/blueprint-resource.ts +272 -0
  231. package/src/worlds/minecraft/blueprint.ts +492 -0
  232. package/src/worlds/minecraft/body-lease.ts +340 -0
  233. package/src/worlds/minecraft/bridge.ts +749 -0
  234. package/src/worlds/minecraft/check.ts +651 -0
  235. package/src/worlds/minecraft/chests.ts +292 -0
  236. package/src/worlds/minecraft/client-launch.ts +272 -0
  237. package/src/worlds/minecraft/client-options.ts +85 -0
  238. package/src/worlds/minecraft/client-skins.ts +164 -0
  239. package/src/worlds/minecraft/client-window.ps1 +91 -0
  240. package/src/worlds/minecraft/client.ts +423 -0
  241. package/src/worlds/minecraft/combat-context.ts +109 -0
  242. package/src/worlds/minecraft/combat.ts +1552 -0
  243. package/src/worlds/minecraft/config.ts +499 -0
  244. package/src/worlds/minecraft/console/access.ts +218 -0
  245. package/src/worlds/minecraft/console/client.ts +244 -0
  246. package/src/worlds/minecraft/console/log.ts +110 -0
  247. package/src/worlds/minecraft/console/mount.ts +420 -0
  248. package/src/worlds/minecraft/console/skin.ts +257 -0
  249. package/src/worlds/minecraft/console/style.css +154 -0
  250. package/src/worlds/minecraft/console/world.ts +449 -0
  251. package/src/worlds/minecraft/deaths.ts +135 -0
  252. package/src/worlds/minecraft/definition.ts +18 -0
  253. package/src/worlds/minecraft/engine-child.ts +274 -0
  254. package/src/worlds/minecraft/engine-ipc.ts +98 -0
  255. package/src/worlds/minecraft/entity-facts.ts +132 -0
  256. package/src/worlds/minecraft/escape.ts +407 -0
  257. package/src/worlds/minecraft/executor.ts +14494 -0
  258. package/src/worlds/minecraft/explored.ts +156 -0
  259. package/src/worlds/minecraft/geometry.ts +205 -0
  260. package/src/worlds/minecraft/goal-plan.ts +647 -0
  261. package/src/worlds/minecraft/item-break.ts +126 -0
  262. package/src/worlds/minecraft/item-facts.ts +146 -0
  263. package/src/worlds/minecraft/item-pick.ts +67 -0
  264. package/src/worlds/minecraft/level-dat.ts +176 -0
  265. package/src/worlds/minecraft/log.ts +132 -0
  266. package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
  267. package/src/worlds/minecraft/names.ts +377 -0
  268. package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
  269. package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
  270. package/src/worlds/minecraft/piglin.ts +49 -0
  271. package/src/worlds/minecraft/policy.ts +547 -0
  272. package/src/worlds/minecraft/precheck.ts +655 -0
  273. package/src/worlds/minecraft/proxy.ts +445 -0
  274. package/src/worlds/minecraft/ranged.ts +635 -0
  275. package/src/worlds/minecraft/readouts.ts +102 -0
  276. package/src/worlds/minecraft/round.ts +26 -0
  277. package/src/worlds/minecraft/search-observation.ts +92 -0
  278. package/src/worlds/minecraft/server-config.ts +467 -0
  279. package/src/worlds/minecraft/server.ts +658 -0
  280. package/src/worlds/minecraft/show.ts +84 -0
  281. package/src/worlds/minecraft/skills.ts +1763 -0
  282. package/src/worlds/minecraft/terrain.ts +1735 -0
  283. package/src/worlds/minecraft/window.ts +96 -0
  284. package/src/worlds/minecraft/works.ts +179 -0
  285. package/src/worlds/minecraft/world.ts +6438 -0
  286. package/src/worlds/qq/ENV_PROMPT.md +5 -0
  287. package/src/worlds/qq/config.ts +108 -0
  288. package/src/worlds/qq/console/client.ts +126 -0
  289. package/src/worlds/qq/console/events.ts +155 -0
  290. package/src/worlds/qq/console/gate.ts +232 -0
  291. package/src/worlds/qq/console/roster.ts +203 -0
  292. package/src/worlds/qq/console/style.css +65 -0
  293. package/src/worlds/qq/conversation.ts +28 -0
  294. package/src/worlds/qq/definition.ts +62 -0
  295. package/src/worlds/qq/driver.ts +375 -0
  296. package/src/worlds/qq/history-tools.ts +209 -0
  297. package/src/worlds/qq/normalize.ts +323 -0
  298. package/src/worlds/qq/qface-map.ts +282 -0
  299. package/src/worlds/qq/vision-prompt.ts +27 -0
  300. package/src/worlds/qq/vision.ts +642 -0
  301. package/src/worlds/qq/vlm.ts +142 -0
  302. package/src/worlds/qq/world.ts +1503 -0
  303. package/src/worlds/terminal/ENV_PROMPT.md +11 -0
  304. package/src/worlds/terminal/config.ts +9 -0
  305. package/src/worlds/terminal/definition.ts +14 -0
  306. package/src/worlds/terminal/world.ts +700 -0
  307. package/src/worlds/websearch/ENV_PROMPT.md +1 -0
  308. package/src/worlds/websearch/brave-client.ts +261 -0
  309. package/src/worlds/websearch/config.ts +75 -0
  310. package/src/worlds/websearch/definition.ts +20 -0
  311. package/src/worlds/websearch/world.ts +171 -0
@@ -0,0 +1,185 @@
1
+ import type { ConfigGroup } from '../../core/types.ts';
2
+ import { cloneOverlayConfig } from './overlay/model.ts';
3
+
4
+ export const BILIBILI_DEFAULTS = {
5
+ // enabled 由Persona的装配层显式开启。
6
+ enabled: false,
7
+ /** 直播间号;短号与真实房间号都行,World 自己换算 */
8
+ roomId: 0,
9
+ /** 登录 cookie 的 SESSDATA;空值使用匿名接入。 */
10
+ sessdata: '',
11
+ /** 礼物使用 flush 投递的金额门槛,单位元。 */
12
+ giftFlushYuan: 1,
13
+ /** 相同弹幕与常规礼物进入总线前的固定归并窗 */
14
+ coalesceWindowMs: 300,
15
+ /** 固定窗内累计到此数量时立即冲刷 */
16
+ coalesceMaxItems: 64,
17
+ /** 高能榜拥挤代理的开启/退出水位 */
18
+ audienceOnlineRankOn: 200,
19
+ audienceOnlineRankOff: 170,
20
+ /** 人数持续不高于退出线达到此时长后解除拥挤态,单位秒。 */
21
+ audienceReleaseHoldSec: 120,
22
+ /** 人数信号新鲜度与已拥挤时的最长陈旧保持 */
23
+ audienceSignalFreshSec: 90,
24
+ audienceActiveStaleHoldSec: 300,
25
+ audienceEventLineBudget: 114,
26
+ audienceEventTokenBudget: 1061,
27
+ /** 重要观众常规互动占总批预算的上限比例,同时受剩余额度限制。 */
28
+ audienceImportantShare: 0.5,
29
+ overlay: cloneOverlayConfig(),
30
+ };
31
+
32
+ export const BILIBILI_CONFIG_GROUP: ConfigGroup = {
33
+ id: 'world:bilibili',
34
+ owner: 'world:bilibili',
35
+ schema: {
36
+ type: 'object',
37
+ title: 'B 站直播间 · 接入',
38
+ description: '只读接入:弹幕、礼物、醒目留言、上舰、进场与人流读数。',
39
+ properties: {
40
+ 'worlds.bilibili.roomId': {
41
+ type: 'integer',
42
+ title: '直播间号',
43
+ minimum: 0,
44
+ maximum: 4294967295,
45
+ 'x-hot': false,
46
+ description: '短号或真实房间号皆可。改动需重启后生效。',
47
+ },
48
+ 'worlds.bilibili.sessdata': {
49
+ type: 'string',
50
+ title: '登录凭证 SESSDATA',
51
+ 'x-hot': false,
52
+ description:
53
+ '浏览器登录 bilibili 后的 SESSDATA cookie。留空使用匿名接入,观众身份可能被脱敏。修改后需重启。',
54
+ },
55
+ 'worlds.bilibili.giftFlushYuan': {
56
+ type: 'number',
57
+ title: '礼物即时投递门槛',
58
+ minimum: 0,
59
+ maximum: 1000,
60
+ 'x-suffix': '元',
61
+ 'x-hot': true,
62
+ description: '达到此金额的礼物立即投递;其余礼物参与常规合批。',
63
+ },
64
+ 'worlds.bilibili.coalesceWindowMs': {
65
+ type: 'integer',
66
+ title: '相同消息归并窗',
67
+ minimum: 0,
68
+ maximum: 2000,
69
+ multipleOf: 50,
70
+ 'x-scale': 1000,
71
+ 'x-suffix': 's',
72
+ 'x-hot': true,
73
+ description: '相同弹幕与常规礼物在进入事件总线前合并;0 关闭。窗口固定且不续期,flush 事件不等待。',
74
+ },
75
+ 'worlds.bilibili.coalesceMaxItems': {
76
+ type: 'integer',
77
+ title: '归并池硬上限',
78
+ minimum: 1,
79
+ maximum: 1000,
80
+ 'x-suffix': '条',
81
+ 'x-hot': true,
82
+ description: '固定窗内累计到此数量时立即输出归并结果,交给事件总线调度。',
83
+ },
84
+ 'worlds.bilibili.audienceOnlineRankOn': {
85
+ type: 'integer',
86
+ title: '高能榜拥挤开启线',
87
+ minimum: 1,
88
+ maximum: 100000,
89
+ 'x-suffix': '人',
90
+ 'x-hot': true,
91
+ description: '高能榜人数达到此值时进入拥挤态;只有同批上下文也超预算才会开始筛选。',
92
+ },
93
+ 'worlds.bilibili.audienceOnlineRankOff': {
94
+ type: 'integer',
95
+ title: '高能榜拥挤退出线',
96
+ minimum: 0,
97
+ maximum: 100000,
98
+ 'x-suffix': '人',
99
+ 'x-hot': true,
100
+ description: '人数持续不高于此值,达到退出等待时间后解除拥挤态。',
101
+ },
102
+ 'worlds.bilibili.audienceReleaseHoldSec': {
103
+ type: 'integer',
104
+ title: '拥挤退出等待',
105
+ minimum: 1,
106
+ maximum: 1800,
107
+ 'x-suffix': 's',
108
+ 'x-hot': true,
109
+ description: '高能榜人数需持续不高于退出线的时长。',
110
+ },
111
+ 'worlds.bilibili.audienceSignalFreshSec': {
112
+ type: 'integer',
113
+ title: '高能榜新鲜时间',
114
+ minimum: 1,
115
+ maximum: 3600,
116
+ 'x-suffix': 's',
117
+ 'x-hot': true,
118
+ description: '超过此时间未更新时标记为陈旧,但已拥挤状态可在最长保持期内继续生效。',
119
+ },
120
+ 'worlds.bilibili.audienceActiveStaleHoldSec': {
121
+ type: 'integer',
122
+ title: '拥挤陈旧保持上限',
123
+ minimum: 1,
124
+ maximum: 3600,
125
+ 'x-suffix': 's',
126
+ 'x-hot': true,
127
+ description: '最后一次高能榜读数过去这么久后,旧的拥挤判定不再启用限流。',
128
+ },
129
+ 'worlds.bilibili.audienceEventLineBudget': {
130
+ type: 'integer',
131
+ title: '观众事件行预算',
132
+ minimum: 1,
133
+ maximum: 5000,
134
+ 'x-suffix': '行/批',
135
+ 'x-hot': true,
136
+ description: '拥挤态下同批归并候选超过此行数才启用筛选。',
137
+ },
138
+ 'worlds.bilibili.audienceEventTokenBudget': {
139
+ type: 'integer',
140
+ title: '观众事件 token 预算',
141
+ minimum: 1,
142
+ maximum: 100000,
143
+ 'x-suffix': 'token/批',
144
+ 'x-hot': true,
145
+ description: '拥挤态下同批归并候选超过此估算量才启用筛选。',
146
+ },
147
+ 'worlds.bilibili.audienceImportantShare': {
148
+ type: 'number',
149
+ title: '重要观众常规互动预算比例',
150
+ minimum: 0.01,
151
+ maximum: 1,
152
+ 'x-scale': 100,
153
+ 'x-suffix': '%',
154
+ 'x-hot': true,
155
+ description: '重要观众常规互动占总批预算的上限比例,同时受批次剩余额度限制。',
156
+ },
157
+ 'worlds.bilibili.overlay.enabled': {
158
+ type: 'boolean',
159
+ title: '启用 Overlay',
160
+ 'x-hot': false,
161
+ description: '在本机启动透明 Overlay 页面。改动需重启后生效。',
162
+ },
163
+ 'worlds.bilibili.overlay.port': {
164
+ type: 'integer',
165
+ title: 'Overlay 端口',
166
+ minimum: 0,
167
+ maximum: 65535,
168
+ 'x-hot': false,
169
+ description: '默认 7795;被占用时会自动顺延。0 表示随机空闲端口。',
170
+ },
171
+ 'worlds.bilibili.overlay.agentNoticeMaxChars': {
172
+ type: 'integer',
173
+ title: 'Agent 公告字数上限',
174
+ minimum: 1,
175
+ maximum: 5000,
176
+ 'x-suffix': '字',
177
+ 'x-hot': true,
178
+ description: 'Agent 公告工具写入纯文本时的 Unicode 字符上限。',
179
+ },
180
+ },
181
+ },
182
+ };
183
+
184
+ /** config.json 的 `worlds.bilibili` 节。 */
185
+ export type BilibiliConfigSection = typeof BILIBILI_DEFAULTS;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * B 站直播间 World 的浏览器扩展。
3
+ *
4
+ * `log` 保留直播间入站诊断。Overlay 编辑器由 World 的回环服务独立承载,
5
+ * 控制台只暴露入口链接。
6
+ *
7
+ * 轮询 state,按 total 差值追加事件。服务端响应类型在浏览器端声明,避免导入 Node 模块。
8
+ */
9
+
10
+ import type { ConsoleClientBundle, ConsolePanelContext, ConsolePanel } from '../../../web/shared/client-panel.ts';
11
+
12
+ export function errText(err: unknown): string {
13
+ return err instanceof Error ? err.message : String(err);
14
+ }
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // 服务端 `BilibiliWorld.console().invoke('log', 'state')` 的返回形状
18
+ // ---------------------------------------------------------------------------
19
+
20
+ interface BilibiliLiveStatus {
21
+ /** `stopped` / `connecting` / `connected` / `retrying` */
22
+ phase: string;
23
+ /** 配置里填的房间号(可能是短号) */
24
+ roomId: number;
25
+ /** 换算出的真实房间号;还没握手完就是 null */
26
+ realRoomId: number | null;
27
+ title: string | null;
28
+ living: boolean;
29
+ /** 登录凭证对应的自己的 uid;匿名接入时为 0 */
30
+ selfUid: number;
31
+ lastError: string | null;
32
+ }
33
+
34
+ /** 待投递的观众统计;计数项累加,其余字段保留最新值。 */
35
+ interface BilibiliAggregate {
36
+ enter: number;
37
+ like: number;
38
+ freeGift: number;
39
+ watched: number | null;
40
+ online: number | null;
41
+ popularity: number | null;
42
+ fans: number | null;
43
+ likeTotal: number | null;
44
+ }
45
+
46
+ interface BilibiliLogState {
47
+ status: BilibiliLiveStatus | null;
48
+ /** 最近一窗弹幕均缺少有效 uid。 */
49
+ desensitized: boolean;
50
+ aggregate: BilibiliAggregate;
51
+ /** 最近事件的文本,按时间倒序排列。 */
52
+ recent: string[];
53
+ /** 记过的总条数(含已被上限挤掉的) */
54
+ total: number;
55
+ /** cmd → 条数,按条数降序;没进白名单也没进黑名单的 cmd 也在里面 */
56
+ counts: Array<[string, number]>;
57
+ audienceAdmission: {
58
+ trackedViewers: number;
59
+ qualifiedViewers: number;
60
+ crowd: { onlineRankCount: number | null; signalFresh: boolean; active: boolean };
61
+ totals: { input: number; selected: number; dropped: number; limitedBatches: number };
62
+ lastBatch: { limitingActive: boolean; input: number; selected: number } | null;
63
+ persistenceError: string | null;
64
+ };
65
+ }
66
+
67
+ const DESC = '直播间事件与各 cmd 计数。';
68
+
69
+ const PHASE_TEXT: Record<string, string> = {
70
+ stopped: '未接入',
71
+ connecting: '连接中',
72
+ connected: '已接入',
73
+ retrying: '重连中',
74
+ };
75
+
76
+ const POLL_MS = 2000;
77
+ /** 与服务端 RECENT_CAP 一致。 */
78
+ const KEEP = 200;
79
+
80
+ const logPanel: ConsolePanel = {
81
+ mount(ctx: ConsolePanelContext) {
82
+ const { ui, root } = ctx;
83
+ const card = ui.sheet({ title: '直播间事件', en: 'bilibili live', desc: DESC });
84
+ root.appendChild(card.el);
85
+
86
+ const err = ui.msgline();
87
+ const info = ui.h('div');
88
+ const gauges = ui.statgrid();
89
+ const view = ui.log({ max: KEEP, maxHeight: '320px', empty: '(等待直播间消息…)' });
90
+ const counts = ui.table({ head: ['cmd', '条数'] });
91
+ const btnEnd = ui.button('回到底部', { size: 'sm', onClick: () => view.scrollToEnd() });
92
+ const bar = ui.rowbar();
93
+ bar.append(ui.h('span', 'grow'), btnEnd);
94
+ card.body.append(err, info, gauges, bar, view.el, counts.el);
95
+
96
+ let shown = 0;
97
+
98
+ const setError = (text: string | null): void => {
99
+ err.textContent = text ?? '';
100
+ err.className = text ? 'msgline bad' : 'msgline';
101
+ err.hidden = !text;
102
+ };
103
+ setError(null);
104
+
105
+ const drawInfo = (st: BilibiliLogState): void => {
106
+ const s = st.status;
107
+ const room = s?.realRoomId
108
+ ? `${s.realRoomId}${s.roomId && s.roomId !== s.realRoomId ? `(短号 ${s.roomId})` : ''}`
109
+ : s?.roomId
110
+ ? `${s.roomId}(还没换算出真实房间号)`
111
+ : '未配置';
112
+ info.replaceChildren(ui.kv([
113
+ { k: '接入', v: PHASE_TEXT[s?.phase ?? 'stopped'] ?? s?.phase ?? '未接入' },
114
+ { k: '直播间', v: room },
115
+ { k: '标题', v: s?.title || '—' },
116
+ { k: '开播', v: ui.pill(s?.living ? '直播中' : '未开播', s?.living ? 'on' : 'off') },
117
+ {
118
+ k: '身份',
119
+ v: st.desensitized
120
+ ? ui.pill('近期弹幕缺少观众 uid', 'off')
121
+ : ui.pill(s?.selfUid ? `已登录(uid ${s.selfUid})` : '待观察', s?.selfUid ? 'on' : 'plain'),
122
+ },
123
+ ...(s?.lastError ? [{ k: '最近错误', v: s.lastError }] : []),
124
+ ]));
125
+ };
126
+
127
+ /** 服务端在生成投递正文后清零聚合计数。 */
128
+ const drawGauges = (agg: BilibiliAggregate, admission: BilibiliLogState['audienceAdmission']): void => {
129
+ const num = (v: number | null): string => (v === null ? '—' : ui.fmt.count(v));
130
+ const last = admission.lastBatch;
131
+ const limiter = admission.crowd.active
132
+ ? last?.limitingActive
133
+ ? `拥挤(上批保留 ${last.selected}/${last.input})`
134
+ : '拥挤'
135
+ : last?.limitingActive
136
+ ? `未限流(上批保留 ${last.selected}/${last.input})`
137
+ : '未限流';
138
+ gauges.replaceChildren(
139
+ ui.stat({ k: '待投递 · 进场', v: agg.enter, unit: '人' }),
140
+ ui.stat({ k: '待投递 · 点赞', v: agg.like, unit: '次' }),
141
+ ui.stat({ k: '待投递 · 免费礼物', v: agg.freeGift, unit: '个' }),
142
+ ui.stat({ k: '高能榜', v: num(admission.crowd.onlineRankCount ?? agg.online), unit: '人' }),
143
+ ui.stat({ k: '事件筛选', v: limiter }),
144
+ ui.stat({ k: '重要观众', v: admission.qualifiedViewers, unit: '人' }),
145
+ ui.stat({ k: '累计过滤', v: admission.totals.dropped, unit: '条' }),
146
+ ui.stat({ k: '看过', v: num(agg.watched) }),
147
+ ui.stat({ k: '人气', v: num(agg.popularity) }),
148
+ ui.stat({ k: '粉丝', v: num(agg.fans) }),
149
+ );
150
+ };
151
+
152
+ const drawCounts = (rows: BilibiliLogState['counts']): void => {
153
+ counts.clear(rows.length ? undefined : '(还没收到任何 cmd)');
154
+ for (const [cmd, n] of rows) counts.addRow([cmd, n]);
155
+ };
156
+
157
+ const appendNew = (st: BilibiliLogState): void => {
158
+ const fresh = Math.min(st.total - shown, st.recent.length);
159
+ if (fresh <= 0) {
160
+ shown = st.total;
161
+ return;
162
+ }
163
+ const chronological = [...st.recent].reverse();
164
+ for (const line of chronological.slice(chronological.length - fresh)) view.append(line);
165
+ shown = st.total;
166
+ };
167
+
168
+ let polling = false;
169
+ const poll = (): void => {
170
+ if (polling) return;
171
+ polling = true;
172
+ void ctx.invoke<BilibiliLogState>('state').then(
173
+ (st) => {
174
+ polling = false;
175
+ if (ctx.signal.aborted) return;
176
+ setError(null);
177
+ drawInfo(st);
178
+ drawGauges(st.aggregate, st.audienceAdmission);
179
+ drawCounts(st.counts);
180
+ appendNew(st);
181
+ },
182
+ (e: unknown) => {
183
+ polling = false;
184
+ if (ctx.signal.aborted) return;
185
+ setError(`读取直播间状态失败: ${errText(e)}`);
186
+ },
187
+ );
188
+ };
189
+
190
+ ctx.interval(poll, POLL_MS);
191
+ poll();
192
+ },
193
+ };
194
+
195
+ const bundle: ConsoleClientBundle = {
196
+ // 键是**局部** panel id,与服务端 `console().panels[].id` 一一对应。
197
+ panels: {
198
+ log: logPanel,
199
+ },
200
+ };
201
+
202
+ export default bundle;
@@ -0,0 +1,35 @@
1
+ import { join } from 'node:path';
2
+ import type { WorldDefinition } from '../../world.ts';
3
+ import { type BilibiliConfigSection, BILIBILI_DEFAULTS } from './config.ts';
4
+ import { BilibiliWorld } from './world.ts';
5
+
6
+ export const BILIBILI: WorldDefinition<BilibiliConfigSection> = {
7
+ id: 'bilibili',
8
+ label: 'B 站直播间',
9
+ defaults: () => structuredClone(BILIBILI_DEFAULTS),
10
+ create: (ctx) => {
11
+ const { cfg } = ctx;
12
+ return new BilibiliWorld({
13
+ roomId: cfg.roomId,
14
+ sessdata: cfg.sessdata,
15
+ giftFlushYuan: () => cfg.giftFlushYuan,
16
+ coalesceWindowMs: () => cfg.coalesceWindowMs,
17
+ coalesceMaxItems: () => cfg.coalesceMaxItems,
18
+ audienceOnlineRankOn: () => cfg.audienceOnlineRankOn,
19
+ audienceOnlineRankOff: () => cfg.audienceOnlineRankOff,
20
+ audienceReleaseHoldSec: () => cfg.audienceReleaseHoldSec,
21
+ audienceSignalFreshSec: () => cfg.audienceSignalFreshSec,
22
+ audienceActiveStaleHoldSec: () => cfg.audienceActiveStaleHoldSec,
23
+ audienceEventLineBudget: () => cfg.audienceEventLineBudget,
24
+ audienceEventTokenBudget: () => cfg.audienceEventTokenBudget,
25
+ audienceImportantShare: () => cfg.audienceImportantShare,
26
+ audienceLedgerFile: join(ctx.dataDir, 'bilibili-audience', 'ledger.json'),
27
+ overlay: cfg.overlay,
28
+ agentNoticeMaxChars: () => cfg.overlay.agentNoticeMaxChars,
29
+ agentNoticeFile: join(ctx.dataDir, 'bilibili-overlay', 'agent-notice.json'),
30
+ overlayAssetDir: join(ctx.dataDir, 'bilibili-overlay', 'assets'),
31
+ onOverlayConfig: (overlay) => ctx.persist({ overlay: { design: overlay.design } }),
32
+ timezone: ctx.timezone,
33
+ });
34
+ },
35
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `SEND_GIFT_V2` 的 protobuf 帧 → V1 的 JSON 字段形状。
3
+ *
4
+ * 此层将 protobuf 字段映射到 V1 的 JSON 名称,使 normalize、overlay/project 共用处理路径,并支持 V1/V2 共存及帧回放。
5
+ *
6
+ * 字段号依据实采帧推断,未有官方 schema;映射如下:
7
+ *
8
+ * ```
9
+ * 顶层
10
+ * 1 uid 送礼人
11
+ * 2 uname
12
+ * 3 face 头像 URL
13
+ * 8 medal 粉丝牌:5=等级 6=牌名。**可能是别的房间的牌子**
14
+ * 10 gift 礼物体,见下
15
+ * 15 sender 送礼人完整档案(1=uid,2={1=名字,2=头像})
16
+ * 礼物体(顶层 10)
17
+ * 1 giftId 2 giftName 3 num 4 giftType(动画礼物=2)
18
+ * 5/6/7 金瓜子三槽,见 coinTotal
19
+ * 8 coin_type("gold"=付费,"silver"=免费)
20
+ * 9 tid(这一笔的交易号,V1 同名字段同形)
21
+ * 12 combo_id 14 连击累计金瓜子(100/200/400/600 这样递增,**不是单笔**)
22
+ * 18 动作词("投喂")
23
+ * ```
24
+ *
25
+ * 以下字段不填:
26
+ *
27
+ * - `guard_level`:粉丝牌里那个档位(medal.12)是**牌子所属房间**的舰长等级,
28
+ * 不能作为本房间的舰长等级。
29
+ * - `is_admin`:尚未确认对应的 protobuf 字段。
30
+ */
31
+ import { pbFromBase64, pbInt, pbSub, pbText, type PbField } from './protobuf.ts';
32
+
33
+ /** 由调用方记录解析告警。 */
34
+ type GiftFrameWarn = (message: string, data?: Record<string, unknown>) => void;
35
+
36
+ /**
37
+ * V1 data 原样返回;V2 返回补齐 V1 字段名的副本。V2 缺少礼物体或名称时返回原 data。
38
+ */
39
+ export function giftFrameData(
40
+ data: Record<string, unknown>,
41
+ warn?: GiftFrameWarn,
42
+ ): Record<string, unknown> {
43
+ if (text(data.giftName) || text(data.gift_name)) return data;
44
+ const top = pbFromBase64(data.pb);
45
+ const gift = pbSub(top, 10);
46
+ const giftName = pbText(gift, 2);
47
+ if (!gift || !giftName) {
48
+ if (data.pb !== undefined) {
49
+ warn?.('SEND_GIFT_V2 缺少有效礼物体或礼物名', {
50
+ decoded: top !== null,
51
+ giftBody: gift !== null,
52
+ });
53
+ }
54
+ return data;
55
+ }
56
+ const sender = pbSub(top, 15);
57
+ const senderBase = pbSub(sender, 2);
58
+ const uid = pbInt(top, 1) ?? pbInt(sender, 1);
59
+ const uname = pbText(top, 2) || pbText(senderBase, 1);
60
+ const face = pbText(top, 3) || pbText(senderBase, 2);
61
+ const num = pbInt(gift, 3);
62
+ const coinType = pbText(gift, 8);
63
+ const totalCoin = coinTotal(gift, num, giftName, warn);
64
+ const giftId = pbInt(gift, 1);
65
+ const tid = pbText(gift, 9);
66
+ return {
67
+ ...data,
68
+ ...(uid !== undefined ? { uid } : {}),
69
+ ...(uname ? { uname } : {}),
70
+ giftName,
71
+ ...(giftId !== undefined ? { giftId } : {}),
72
+ ...(num !== undefined ? { num } : {}),
73
+ ...(coinType ? { coin_type: coinType } : {}),
74
+ ...(totalCoin !== undefined ? { total_coin: totalCoin } : {}),
75
+ ...(tid ? { tid } : {}),
76
+ sender_uinfo: senderUinfo(top, uid, uname, face),
77
+ };
78
+ }
79
+
80
+ function senderUinfo(
81
+ top: readonly PbField[] | null,
82
+ uid: number | undefined,
83
+ uname: string,
84
+ face: string,
85
+ ): Record<string, unknown> {
86
+ const medal = pbSub(top, 8);
87
+ const medalLevel = pbInt(medal, 5);
88
+ const medalName = pbText(medal, 6);
89
+ const hasMedal = medalLevel !== undefined || medalName !== '';
90
+ return {
91
+ ...(uid !== undefined ? { uid } : {}),
92
+ base: {
93
+ ...(uname ? { name: uname } : {}),
94
+ ...(face ? { face } : {}),
95
+ },
96
+ ...(hasMedal
97
+ ? {
98
+ medal: {
99
+ ...(medalLevel !== undefined ? { level: medalLevel } : {}),
100
+ ...(medalName ? { name: medalName } : {}),
101
+ },
102
+ }
103
+ : {}),
104
+ };
105
+ }
106
+
107
+ /**
108
+ * 金瓜子字段映射尚未确认,暂取字段 5/6/7 中正数的最大值作为 fallback。
109
+ * 有效值不相等或已知 num 不为 1 时告警。
110
+ */
111
+ function coinTotal(
112
+ gift: readonly PbField[],
113
+ num: number | undefined,
114
+ giftName: string,
115
+ warn?: GiftFrameWarn,
116
+ ): number | undefined {
117
+ const slots = [5, 6, 7]
118
+ .map((field) => pbInt(gift, field))
119
+ .filter((value): value is number => value !== undefined && value > 0);
120
+ if (slots.length === 0) return undefined;
121
+ const total = Math.max(...slots);
122
+ const settled = slots.every((value) => value === total) && (num ?? 1) === 1;
123
+ if (!settled) {
124
+ warn?.('SEND_GIFT_V2 金瓜子字段映射未确认,暂取最大值', { gift: giftName, slots, num });
125
+ }
126
+ return total;
127
+ }
128
+
129
+ function text(value: unknown): string {
130
+ return typeof value === 'string' ? value : '';
131
+ }