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,633 @@
1
+ /**
2
+ * Console Page Host 根据 manifest 渲染导航与面板,并管理挂载和卸载;不依赖具体页的名称。扩展加载、mount 或面板解析失败仅将对应面板替换为错误卡,不影响其他页与框架。
3
+ */
4
+
5
+ import {
6
+ CONSOLE_PROTOCOL_VERSION,
7
+ type ConsoleManifest,
8
+ type ConsolePageManifest,
9
+ type ConsolePanelManifest,
10
+ } from '../../shared/console-protocol.ts';
11
+ import type { ConsoleMemo, ConsolePanel, Disposable } from '../../shared/client-panel.ts';
12
+ import { get, post } from '../core/api.ts';
13
+ import { Lifecycle } from '../core/lifecycle.ts';
14
+ import type { Router } from '../core/router.ts';
15
+ import type { SocketLike } from '../core/stream.ts';
16
+ import { createConsoleUi } from '../ui/index.ts';
17
+ import { lampRow } from '../ui/lamp.ts';
18
+ import { createConfigView } from '../features/config/view.ts';
19
+ import type { ToolSchemaDoc } from '../features/live/protocol.ts';
20
+ import { createPromptsView } from '../features/prompts/view.ts';
21
+ import { createStorageView } from '../features/storage/view.ts';
22
+ import { S as TOOLS } from './tools/strings.ts';
23
+ import { createToolsView } from './tools/view.ts';
24
+ import { resolveConsoleLinkHref } from '../theme/handoff.ts';
25
+ import type { BuiltinPanels } from './builtins.ts';
26
+ import { createPanelContext, namespacedMemo } from './context.ts';
27
+ import { ConsolePageLoader } from './loader.ts';
28
+ import { S } from './strings.ts';
29
+
30
+ /** 控制台页路由的首段,与框架页路由分开。 */
31
+ export const PROVIDER_ROUTE = 'provider';
32
+ /** 框架页签由 promptDocs、config 与 storage 声明生成;~ 前缀不在合法 panel id 字符集中。 */
33
+ const PROVIDER_PROMPTS_ROUTE = '~prompts';
34
+ const PROVIDER_CONFIG_ROUTE = '~config';
35
+ const PROVIDER_STORAGE_ROUTE = '~storage';
36
+ /** 工具表不来自页面声明:装配好的那一份整个属于 Persona,所以只挂在 Persona 页上。 */
37
+ const PROVIDER_TOOLS_ROUTE = '~tools';
38
+
39
+ function asArray<T>(v: readonly T[] | undefined): readonly T[] {
40
+ return Array.isArray(v) ? v : [];
41
+ }
42
+
43
+ /** 页签只给独立面板;带 slot 的由宿主面板挂。 */
44
+ function tabbed(page: ConsolePageManifest): readonly ConsolePanelManifest[] {
45
+ return asArray(page.panels).filter((p) => p.slot === undefined);
46
+ }
47
+
48
+ export interface ConsolePageHostDeps {
49
+ doc: Document;
50
+ /** 页面内容渲染到这里。host 自己清空/重建它。 */
51
+ root: HTMLElement;
52
+ /** 浮层宿主,页面级容器;绝不能是 `root`。 */
53
+ overlayHost: HTMLElement;
54
+ loader: ConsolePageLoader;
55
+ /** 内核自带的面板实现,键即 `panel.builtin`。 */
56
+ builtins: BuiltinPanels;
57
+ router: Router;
58
+ fetchManifest(): Promise<ConsoleManifest>;
59
+ memo: ConsoleMemo;
60
+ createSocket(url: string): SocketLike;
61
+ wsUrl(path: string): string;
62
+ onError(err: unknown): void;
63
+ /**
64
+ * 面板页签指向的路由。缺省是 `#/provider/<id>/<panel>`;嵌在别的框架页里的宿主
65
+ * 给自己那一页的前缀,页签切换才不会跳出那一页。
66
+ */
67
+ route?(pageId: string, panelId: string): readonly string[];
68
+ }
69
+
70
+ interface MountedPanel {
71
+ pageId: string;
72
+ panelId: string;
73
+ lifecycle: Lifecycle;
74
+ }
75
+
76
+ /** 页头与面板使用独立容器,刷新页头不重挂面板。 */
77
+ interface Panes {
78
+ chrome: HTMLElement;
79
+ slot: HTMLElement;
80
+ }
81
+
82
+ export class ConsolePageHost {
83
+ private readonly deps: ConsolePageHostDeps;
84
+ private snapshot: ConsoleManifest | null = null;
85
+ private mounted: MountedPanel | null = null;
86
+ private panes: Panes | null = null;
87
+ /**
88
+ * 挂载代号。异步 mount 期间用户可能已经走了;回来时对不上号就整个丢弃,
89
+ * 免得把一个已经不该存在的面板贴进 DOM。
90
+ */
91
+ private generation = 0;
92
+ private readonly navListeners = new Set<() => void>();
93
+
94
+ constructor(deps: ConsolePageHostDeps) {
95
+ this.deps = deps;
96
+ }
97
+
98
+ get pages(): ConsolePageManifest[] {
99
+ return this.snapshot?.providers ?? [];
100
+ }
101
+
102
+ /** 取一次 manifest。失败时保留上一份(一次网络抖动不该让导航整个消失)。 */
103
+ async load(): Promise<void> {
104
+ try {
105
+ const next = await this.deps.fetchManifest();
106
+ // 拒绝不支持的协议版本。
107
+ if (next && next.protocolVersion !== CONSOLE_PROTOCOL_VERSION) {
108
+ throw new Error(S.protocolMismatch(String(next.protocolVersion), String(CONSOLE_PROTOCOL_VERSION)));
109
+ }
110
+ this.snapshot = next;
111
+ } catch (err) {
112
+ this.deps.onError(err);
113
+ if (!this.snapshot) {
114
+ this.snapshot = {
115
+ protocolVersion: CONSOLE_PROTOCOL_VERSION,
116
+ providers: [],
117
+ framework: { capabilities: {} },
118
+ };
119
+ }
120
+ }
121
+ this.emitNav();
122
+ }
123
+
124
+ /** 重取 manifest 并刷新导航与当前页头,**不重挂面板**。 */
125
+ async refresh(): Promise<void> {
126
+ await this.load();
127
+ const cur = this.mounted;
128
+ if (cur) this.renderChrome(cur.pageId, cur.panelId);
129
+ }
130
+
131
+ onNavChange(cb: () => void): Disposable {
132
+ this.navListeners.add(cb);
133
+ return { dispose: () => this.navListeners.delete(cb) };
134
+ }
135
+
136
+ private emitNav(): void {
137
+ for (const cb of [...this.navListeners]) {
138
+ try {
139
+ cb();
140
+ } catch (err) {
141
+ this.deps.onError(err);
142
+ }
143
+ }
144
+ }
145
+
146
+ find(pageId: string): ConsolePageManifest | undefined {
147
+ return this.pages.find((p) => p.id === pageId);
148
+ }
149
+
150
+ private routeOf(pageId: string, panelId: string): readonly string[] {
151
+ return this.deps.route ? this.deps.route(pageId, panelId) : [PROVIDER_ROUTE, pageId, panelId];
152
+ }
153
+
154
+ /** 卸载当前面板:abort → dispose → 清空 root。顺序见 client-panel.ts 的说明。 */
155
+ unmount(): void {
156
+ const cur = this.mounted;
157
+ this.mounted = null;
158
+ this.panes = null;
159
+ this.generation++;
160
+ if (cur) cur.lifecycle.dispose();
161
+ this.deps.root.replaceChildren();
162
+ }
163
+
164
+ /** 建(或复用)页头与面板槽两个容器。 */
165
+ private ensurePanes(): Panes {
166
+ if (this.panes) return this.panes;
167
+ const doc = this.deps.doc;
168
+ const chrome = doc.createElement('div');
169
+ chrome.className = 'providerchrome';
170
+ const slot = doc.createElement('div');
171
+ slot.className = 'panelslot';
172
+ this.deps.root.replaceChildren(chrome, slot);
173
+ this.panes = { chrome, slot };
174
+ return this.panes;
175
+ }
176
+
177
+ /**
178
+ * 显示指定页的面板,省略 panelId 时选择第一个面板。每次调用先卸载上一面板,以重建完整生命周期。
179
+ * 页头渲染、面板解析及 mount 均处于异常隔离范围,失败时显示该面板的错误卡,不使 show() 的错误扩散为路由空白。
180
+ */
181
+ async show(pageId: string, panelId?: string): Promise<void> {
182
+ try {
183
+ await this.showInner(pageId, panelId);
184
+ } catch (err) {
185
+ this.deps.onError(err);
186
+ try {
187
+ this.renderError(S.pageFailed(pageId), err instanceof Error ? err.message : String(err));
188
+ } catch { /* 连错误卡都画不出来:那是 ui 层的事,不再往上抛 */ }
189
+ }
190
+ }
191
+
192
+ private async showInner(pageId: string, panelId?: string): Promise<void> {
193
+ this.unmount();
194
+ const gen = this.generation;
195
+
196
+ const page = this.find(pageId);
197
+ if (!page) {
198
+ this.renderError(S.noPage(pageId), S.noPageHint);
199
+ return;
200
+ }
201
+ const panels = tabbed(page);
202
+ const prompts = asArray(page.prompts);
203
+ const configGroups = this.configGroupsOf(page);
204
+ const storageKeys = this.storageKeysOf(page);
205
+ const tools = this.toolsTabOf(page);
206
+ const wanted = panelId ?? panels[0]?.id
207
+ ?? (configGroups.length ? PROVIDER_CONFIG_ROUTE : undefined)
208
+ ?? (prompts.length ? PROVIDER_PROMPTS_ROUTE : undefined)
209
+ ?? (storageKeys.length ? PROVIDER_STORAGE_ROUTE : undefined)
210
+ ?? (tools ? PROVIDER_TOOLS_ROUTE : undefined);
211
+ if (wanted === PROVIDER_CONFIG_ROUTE && configGroups.length) {
212
+ this.renderChrome(pageId, PROVIDER_CONFIG_ROUTE);
213
+ await this.showConfig(pageId, configGroups, gen);
214
+ return;
215
+ }
216
+ if (wanted === PROVIDER_PROMPTS_ROUTE && prompts.length) {
217
+ this.renderChrome(pageId, PROVIDER_PROMPTS_ROUTE);
218
+ await this.showPrompts(pageId, prompts.map((doc) => doc.key), gen);
219
+ return;
220
+ }
221
+ if (wanted === PROVIDER_STORAGE_ROUTE && storageKeys.length) {
222
+ this.renderChrome(pageId, PROVIDER_STORAGE_ROUTE);
223
+ await this.showStorage(pageId, storageKeys, gen);
224
+ return;
225
+ }
226
+ if (wanted === PROVIDER_TOOLS_ROUTE && tools) {
227
+ this.renderChrome(pageId, PROVIDER_TOOLS_ROUTE);
228
+ await this.showTools(pageId, gen);
229
+ return;
230
+ }
231
+ const panel = wanted ? panels.find((p) => p.id === wanted) : undefined;
232
+ if (!panel) {
233
+ this.renderChrome(pageId, wanted);
234
+ if (panels.length === 0 && prompts.length === 0 && configGroups.length === 0
235
+ && storageKeys.length === 0 && !tools) {
236
+ this.appendNote(S.noPanels(page.label));
237
+ } else {
238
+ this.renderError(
239
+ S.noSuchPanel(page.label, wanted ?? ''),
240
+ S.provides([
241
+ ...panels.map((p) => p.id),
242
+ ...(configGroups.length ? [PROVIDER_CONFIG_ROUTE] : []),
243
+ ...(prompts.length ? [PROVIDER_PROMPTS_ROUTE] : []),
244
+ ...(storageKeys.length ? [PROVIDER_STORAGE_ROUTE] : []),
245
+ ...(tools ? [PROVIDER_TOOLS_ROUTE] : []),
246
+ ].join(' / ')),
247
+ );
248
+ }
249
+ return;
250
+ }
251
+
252
+ this.renderChrome(pageId, panel.id);
253
+ const { slot } = this.ensurePanes();
254
+
255
+ const lifecycle = new Lifecycle(this.deps.onError);
256
+ this.mounted = { pageId, panelId: panel.id, lifecycle };
257
+
258
+ try {
259
+ const impl = panel.builtin !== undefined
260
+ ? this.builtinPanel(panel.builtin)
261
+ : await this.deps.loader.resolvePanel(pageId, panel.id, page.client);
262
+ if (gen !== this.generation) return; // 等 import 的工夫用户已经走了
263
+
264
+ const out = await impl.mount(
265
+ this.panelContext(pageId, panel.id, slot, lifecycle, gen, {}),
266
+ );
267
+ if (gen !== this.generation) {
268
+ // mount 返回时已卸载,立即释放其 Disposable。
269
+ if (out && typeof out.dispose === 'function') out.dispose();
270
+ return;
271
+ }
272
+ if (out && typeof out.dispose === 'function') lifecycle.own(out);
273
+ } catch (err) {
274
+ if (gen !== this.generation) return;
275
+ this.deps.onError(err);
276
+ lifecycle.dispose();
277
+ slot.replaceChildren();
278
+ this.renderErrorInto(
279
+ slot,
280
+ S.panelFailed(panel.title),
281
+ err instanceof Error ? err.message : String(err),
282
+ );
283
+ }
284
+ }
285
+
286
+ /** 面板上下文。插槽里的面板与页签面板走同一条路,区别只在 root、生命周期与 scope。 */
287
+ private panelContext(
288
+ pageId: string,
289
+ panelId: string,
290
+ root: HTMLElement,
291
+ lifecycle: Lifecycle,
292
+ gen: number,
293
+ scope: Readonly<Record<string, string>>,
294
+ ) {
295
+ return createPanelContext({
296
+ pageId,
297
+ panelId,
298
+ root,
299
+ lifecycle,
300
+ scope,
301
+ mountSlot: (name, host, childScope) =>
302
+ this.mountSlot(pageId, gen, lifecycle, name, host, childScope),
303
+ overlayHost: this.deps.overlayHost,
304
+ refresh: () => this.refresh(),
305
+ addLeaveGuard: (fn) => this.deps.router.addLeaveGuard(fn),
306
+ memo: namespacedMemo(this.deps.memo, pageId, panelId),
307
+ createSocket: this.deps.createSocket,
308
+ wsUrl: this.deps.wsUrl,
309
+ onError: this.deps.onError,
310
+ doc: this.deps.doc,
311
+ });
312
+ }
313
+
314
+ /**
315
+ * 把声明到该插槽的面板按声明顺序挂进 host。整组共用一个生命周期,返回的句柄结束它们;
316
+ * 单块面板挂不上只把该块换成错误卡。
317
+ */
318
+ private async mountSlot(
319
+ pageId: string,
320
+ gen: number,
321
+ parent: Lifecycle,
322
+ slot: string,
323
+ host: HTMLElement,
324
+ scope: Readonly<Record<string, string>>,
325
+ ): Promise<Disposable> {
326
+ const lifecycle = parent.own(new Lifecycle(this.deps.onError));
327
+ const page = this.find(pageId);
328
+ for (const decl of asArray(page?.panels).filter((p) => p.slot === slot)) {
329
+ const box = this.deps.doc.createElement('div');
330
+ host.appendChild(box);
331
+ try {
332
+ const impl = decl.builtin !== undefined
333
+ ? this.builtinPanel(decl.builtin)
334
+ : await this.deps.loader.resolvePanel(pageId, decl.id, page?.client);
335
+ if (gen !== this.generation || lifecycle.disposed) break;
336
+ const out = await impl.mount(
337
+ this.panelContext(pageId, decl.id, box, lifecycle, gen, scope),
338
+ );
339
+ if (out && typeof out.dispose === 'function') {
340
+ if (gen !== this.generation || lifecycle.disposed) out.dispose();
341
+ else lifecycle.own(out);
342
+ }
343
+ } catch (err) {
344
+ this.deps.onError(err);
345
+ box.replaceChildren();
346
+ this.renderErrorInto(
347
+ box,
348
+ S.panelFailed(decl.title),
349
+ err instanceof Error ? err.message : String(err),
350
+ );
351
+ }
352
+ }
353
+ return lifecycle;
354
+ }
355
+
356
+ /**
357
+ * 取一块内核自带的面板。声明了一个内核不认识的名字 → 明确报错,由 `showInner`
358
+ * 的 catch 收成这一格的错误卡:同一页的其他面板与整个框架不受影响。
359
+ *
360
+ * 内置面板整条路不碰 loader,所以一页的面板全是内置时,它没有浏览器产物也正常。
361
+ */
362
+ private builtinPanel(name: string): ConsolePanel {
363
+ const builtins = this.deps.builtins;
364
+ const impl = builtins[name];
365
+ if (!impl) throw new Error(S.noBuiltinPanel(name, Object.keys(builtins).join(' / ') || S.none));
366
+ return impl;
367
+ }
368
+
369
+ /** 仅返回当前页认领且部署支持 config 能力的配置组。 */
370
+ private configGroupsOf(page: ConsolePageManifest): readonly string[] {
371
+ if (this.snapshot?.framework?.capabilities?.config === false) return [];
372
+ return asArray(page.configGroups);
373
+ }
374
+
375
+ /** 仅返回当前页声明且部署支持 storage 能力的存储项 key。 */
376
+ private storageKeysOf(page: ConsolePageManifest): readonly string[] {
377
+ if (this.snapshot?.framework?.capabilities?.storage === false) return [];
378
+ return asArray(page.storageKeys);
379
+ }
380
+
381
+ /** Persona 页才有工具表页签,且要部署挂着 /api/tool-schemas。 */
382
+ private toolsTabOf(page: ConsolePageManifest): boolean {
383
+ if (page.kind !== 'persona') return false;
384
+ return this.snapshot?.framework?.capabilities?.toolSchemas !== false;
385
+ }
386
+
387
+ /** 工具表包含主循环与装配层补充的工具声明。 */
388
+ private async showTools(pageId: string, gen: number): Promise<void> {
389
+ const { slot } = this.ensurePanes();
390
+ const lifecycle = new Lifecycle(this.deps.onError);
391
+ this.mounted = { pageId, panelId: PROVIDER_TOOLS_ROUTE, lifecycle };
392
+ const ui = createConsoleUi({
393
+ memo: namespacedMemo(this.deps.memo, pageId, PROVIDER_TOOLS_ROUTE),
394
+ overlayHost: this.deps.overlayHost,
395
+ signal: lifecycle.signal,
396
+ doc: this.deps.doc,
397
+ });
398
+ const view = createToolsView(ui);
399
+ slot.appendChild(view.el);
400
+ try {
401
+ const out = await get<{ tools?: ToolSchemaDoc[] }>('/api/tool-schemas', { signal: lifecycle.signal });
402
+ view.render(out?.tools ?? []);
403
+ } catch (err) {
404
+ if ((err as { name?: string } | null)?.name !== 'AbortError') {
405
+ this.deps.onError(err);
406
+ view.render([]);
407
+ view.note(TOOLS.loadFailed(err instanceof Error ? err.message : String(err)));
408
+ }
409
+ }
410
+ if (gen !== this.generation) lifecycle.dispose();
411
+ }
412
+
413
+ /** 这一页声明的存储项走同一张 /api/storage 清单;这里只按 key 挑出自己的。 */
414
+ private async showStorage(pageId: string, keys: readonly string[], gen: number): Promise<void> {
415
+ const { slot } = this.ensurePanes();
416
+ const lifecycle = new Lifecycle(this.deps.onError);
417
+ this.mounted = { pageId, panelId: PROVIDER_STORAGE_ROUTE, lifecycle };
418
+ const ui = createConsoleUi({
419
+ memo: namespacedMemo(this.deps.memo, pageId, PROVIDER_STORAGE_ROUTE),
420
+ overlayHost: this.deps.overlayHost,
421
+ signal: lifecycle.signal,
422
+ doc: this.deps.doc,
423
+ });
424
+ const wanted = new Set(keys);
425
+ const view = createStorageView({ ui, signal: lifecycle.signal, filter: (part) => wanted.has(part.key) });
426
+ const sheet = ui.sheet({ title: S.storageTitle, en: 'storage', desc: S.storageDesc });
427
+ sheet.body.appendChild(view.el);
428
+ slot.appendChild(sheet.el);
429
+ await view.load();
430
+ if (gen !== this.generation) lifecycle.dispose();
431
+ }
432
+
433
+ /** manifest 提供组 id,schema 与当前值取自 /api/config;页内省略重复 owner 标签。 */
434
+ private async showConfig(pageId: string, groupIds: readonly string[], gen: number): Promise<void> {
435
+ const { slot } = this.ensurePanes();
436
+ const lifecycle = new Lifecycle(this.deps.onError);
437
+ this.mounted = { pageId, panelId: PROVIDER_CONFIG_ROUTE, lifecycle };
438
+ const ui = createConsoleUi({
439
+ memo: namespacedMemo(this.deps.memo, pageId, PROVIDER_CONFIG_ROUTE),
440
+ overlayHost: this.deps.overlayHost,
441
+ signal: lifecycle.signal,
442
+ doc: this.deps.doc,
443
+ });
444
+ const wanted = new Set(groupIds);
445
+ const view = createConfigView({
446
+ ui,
447
+ lifecycle,
448
+ signal: lifecycle.signal,
449
+ filter: (group) => wanted.has(group.id),
450
+ showOwner: false,
451
+ emptyText: S.configEmpty,
452
+ });
453
+ const sheet = ui.sheet({
454
+ title: S.configTitle,
455
+ en: 'config',
456
+ desc: S.configDesc,
457
+ });
458
+ sheet.body.appendChild(view.el);
459
+ slot.appendChild(sheet.el);
460
+ await view.load();
461
+ if (gen !== this.generation) lifecycle.dispose();
462
+ }
463
+
464
+ /** 这一页声明的提示词模板仍由框架通用编辑器承载,扩展无需重复文件读写 UI。 */
465
+ private async showPrompts(pageId: string, keys: readonly string[], gen: number): Promise<void> {
466
+ const { slot } = this.ensurePanes();
467
+ const lifecycle = new Lifecycle(this.deps.onError);
468
+ this.mounted = { pageId, panelId: PROVIDER_PROMPTS_ROUTE, lifecycle };
469
+ const ui = createConsoleUi({
470
+ memo: namespacedMemo(this.deps.memo, pageId, PROVIDER_PROMPTS_ROUTE),
471
+ overlayHost: this.deps.overlayHost,
472
+ signal: lifecycle.signal,
473
+ doc: this.deps.doc,
474
+ });
475
+ const view = createPromptsView({
476
+ ui,
477
+ lifecycle,
478
+ signal: lifecycle.signal,
479
+ addLeaveGuard: (fn) => this.deps.router.addLeaveGuard(fn),
480
+ onError: this.deps.onError,
481
+ keys,
482
+ });
483
+ slot.appendChild(view.el);
484
+ await view.load();
485
+ if (gen !== this.generation) lifecycle.dispose();
486
+ }
487
+
488
+ // -------------------------------------------------------------------------
489
+ // 页头与错误卡。都只吃 manifest 的通用字段。
490
+ // -------------------------------------------------------------------------
491
+
492
+ private ui(): ReturnType<typeof createConsoleUi> {
493
+ // 页头是 host 自己的 DOM,生命周期跟 host 走,不绑任何面板。
494
+ return createConsoleUi({
495
+ memo: this.deps.memo,
496
+ overlayHost: this.deps.overlayHost,
497
+ signal: new AbortController().signal,
498
+ doc: this.deps.doc,
499
+ });
500
+ }
501
+
502
+ private renderChrome(pageId: string, activePanel?: string): void {
503
+ const page = this.find(pageId);
504
+ const { chrome } = this.ensurePanes();
505
+ chrome.replaceChildren();
506
+ if (!page) return;
507
+
508
+ const ui = this.ui();
509
+ const head = ui.h('header', 'featureintro providerintro');
510
+ const title = ui.h('h1', 'pagetitle', page.label);
511
+ // 未激活页的状态由框架提供。
512
+ title.appendChild(lampRow(
513
+ this.deps.doc,
514
+ page.availability === 'active'
515
+ ? page.lamps ?? []
516
+ : [{ label: S.assembly, state: 'offline', hint: page.availability === 'missing' ? S.notInstalled : S.notActivated }],
517
+ ));
518
+ head.append(title, ui.h('p', 'pagedesc', page.id));
519
+ const bar = ui.rowbar();
520
+
521
+ for (const badge of asArray(page.badges)) {
522
+ bar.appendChild(ui.pill(`${badge.label} ${badge.value}`, badge.tone));
523
+ }
524
+ if (page.availability !== 'active') {
525
+ bar.appendChild(ui.pill(page.availability === 'missing' ? S.notInstalled : S.notActivated, 'off'));
526
+ }
527
+ if (page.agentVisible === false) bar.appendChild(ui.pill(S.hidden, 'off'));
528
+ if (page.prefixDrifted) {
529
+ bar.appendChild(ui.button(S.reloadPrefix, {
530
+ size: 'sm',
531
+ onClick: () => { void this.reloadPrefix(ui); },
532
+ }));
533
+ }
534
+ bar.appendChild(ui.h('span', 'grow'));
535
+
536
+ for (const link of asArray(page.links)) {
537
+ const open = ui.button(link.label || S.open, {
538
+ variant: 'primary',
539
+ size: 'sm',
540
+ onClick: () => {
541
+ this.deps.doc.defaultView?.open(
542
+ resolveConsoleLinkHref(this.deps.doc, link),
543
+ '_blank',
544
+ 'noopener',
545
+ );
546
+ },
547
+ });
548
+ bar.appendChild(open);
549
+ }
550
+ head.appendChild(bar);
551
+ if (page.reason) head.appendChild(ui.msgline(page.reason, true));
552
+ chrome.appendChild(head);
553
+
554
+ const panels = tabbed(page);
555
+ const prompts = asArray(page.prompts);
556
+ const configGroups = this.configGroupsOf(page);
557
+ const storageKeys = this.storageKeysOf(page);
558
+ const tools = this.toolsTabOf(page);
559
+ if (panels.length + (configGroups.length ? 1 : 0) + (prompts.length ? 1 : 0)
560
+ + (storageKeys.length ? 1 : 0) + (tools ? 1 : 0) > 1) {
561
+ const tabs = ui.rowbar();
562
+ const go = (panelId: string): void => this.deps.router.navigate(this.routeOf(page.id, panelId));
563
+ for (const p of panels) {
564
+ const btn = ui.button(p.title || p.id, {
565
+ size: 'sm',
566
+ variant: p.id === activePanel ? 'primary' : 'plain',
567
+ onClick: () => go(p.id),
568
+ });
569
+ tabs.appendChild(btn);
570
+ }
571
+ if (configGroups.length) {
572
+ tabs.appendChild(ui.button(S.configTab, {
573
+ size: 'sm',
574
+ variant: activePanel === PROVIDER_CONFIG_ROUTE ? 'primary' : 'plain',
575
+ onClick: () => go(PROVIDER_CONFIG_ROUTE),
576
+ }));
577
+ }
578
+ if (prompts.length) {
579
+ tabs.appendChild(ui.button(prompts.length === 1 ? prompts[0]!.title : S.promptsTab, {
580
+ size: 'sm',
581
+ variant: activePanel === PROVIDER_PROMPTS_ROUTE ? 'primary' : 'plain',
582
+ onClick: () => go(PROVIDER_PROMPTS_ROUTE),
583
+ }));
584
+ }
585
+ if (storageKeys.length) {
586
+ tabs.appendChild(ui.button(S.storageTab, {
587
+ size: 'sm',
588
+ variant: activePanel === PROVIDER_STORAGE_ROUTE ? 'primary' : 'plain',
589
+ onClick: () => go(PROVIDER_STORAGE_ROUTE),
590
+ }));
591
+ }
592
+ if (tools) {
593
+ tabs.appendChild(ui.button(S.toolsTab, {
594
+ size: 'sm',
595
+ variant: activePanel === PROVIDER_TOOLS_ROUTE ? 'primary' : 'plain',
596
+ onClick: () => go(PROVIDER_TOOLS_ROUTE),
597
+ }));
598
+ }
599
+ chrome.appendChild(tabs);
600
+ }
601
+ }
602
+
603
+ /** 重载前缀后重新读取 manifest 中的漂移状态。 */
604
+ private async reloadPrefix(ui: ReturnType<typeof createConsoleUi>): Promise<void> {
605
+ const ok = await ui.confirm({
606
+ title: S.reloadTitle,
607
+ body: S.reloadBody,
608
+ });
609
+ if (!ok) return;
610
+ try {
611
+ const out = await post<{ result?: string }>('/api/session/reload-prefix');
612
+ ui.toast(out?.result || S.prefixReloaded, 'ok');
613
+ await this.refresh();
614
+ } catch (err) {
615
+ ui.toast(err instanceof Error ? err.message : String(err), 'bad');
616
+ }
617
+ }
618
+
619
+ private appendNote(text: string): void {
620
+ this.ensurePanes().slot.appendChild(this.ui().placeholder(text));
621
+ }
622
+
623
+ private renderError(title: string, detail: string): void {
624
+ this.renderErrorInto(this.ensurePanes().slot, title, detail);
625
+ }
626
+
627
+ private renderErrorInto(target: HTMLElement, title: string, detail: string): void {
628
+ const ui = this.ui();
629
+ const card = ui.sheet({ title, en: 'panel error' });
630
+ card.body.appendChild(ui.msgline(detail, true));
631
+ target.appendChild(card.el);
632
+ }
633
+ }