@pellux/goodvibes-agent 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 (398) hide show
  1. package/.goodvibes/GOODVIBES.md +35 -0
  2. package/.goodvibes/agents/reviewer.md +48 -0
  3. package/.goodvibes/skills/add-provider/SKILL.md +199 -0
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +74 -0
  6. package/bin/goodvibes-agent.ts +2 -0
  7. package/docs/README.md +23 -0
  8. package/docs/deployment-and-services.md +57 -0
  9. package/docs/getting-started.md +53 -0
  10. package/docs/release-and-publishing.md +46 -0
  11. package/package.json +134 -0
  12. package/scripts/check-bun.sh +20 -0
  13. package/src/audio/player.ts +156 -0
  14. package/src/audio/spoken-turn-controller.ts +203 -0
  15. package/src/audio/spoken-turn-model-routing.ts +117 -0
  16. package/src/audio/spoken-turn-wiring.ts +44 -0
  17. package/src/audio/text-chunker.ts +110 -0
  18. package/src/cli/bundle-command.ts +227 -0
  19. package/src/cli/completion.ts +90 -0
  20. package/src/cli/config-overrides.ts +159 -0
  21. package/src/cli/endpoints.ts +63 -0
  22. package/src/cli/entrypoint.ts +172 -0
  23. package/src/cli/help.ts +299 -0
  24. package/src/cli/index.ts +11 -0
  25. package/src/cli/management-commands.ts +426 -0
  26. package/src/cli/management.ts +744 -0
  27. package/src/cli/network-posture.ts +46 -0
  28. package/src/cli/package-verification.ts +123 -0
  29. package/src/cli/parser.ts +369 -0
  30. package/src/cli/provider-auth-routes.ts +22 -0
  31. package/src/cli/provider-classification.ts +107 -0
  32. package/src/cli/redaction.ts +105 -0
  33. package/src/cli/service-command.ts +26 -0
  34. package/src/cli/service-posture.ts +482 -0
  35. package/src/cli/status.ts +383 -0
  36. package/src/cli/surface-command.ts +247 -0
  37. package/src/cli/tui-startup.ts +32 -0
  38. package/src/cli/types.ts +69 -0
  39. package/src/cli-flags.ts +21 -0
  40. package/src/config/goodvibes-home-audit.ts +465 -0
  41. package/src/config/index.ts +57 -0
  42. package/src/config/provider-model.ts +23 -0
  43. package/src/config/secret-config.ts +119 -0
  44. package/src/config/secrets.ts +71 -0
  45. package/src/config/surface.ts +1 -0
  46. package/src/core/composer-state.ts +61 -0
  47. package/src/core/conversation-rendering.ts +359 -0
  48. package/src/core/conversation.ts +551 -0
  49. package/src/core/history.ts +45 -0
  50. package/src/core/orchestrator.ts +7 -0
  51. package/src/core/system-message-router.ts +171 -0
  52. package/src/daemon/cli.ts +55 -0
  53. package/src/daemon/safe-serve.ts +61 -0
  54. package/src/input/agent-workspace.ts +428 -0
  55. package/src/input/autocomplete.ts +96 -0
  56. package/src/input/bookmark-modal.ts +115 -0
  57. package/src/input/command-args-hint.ts +36 -0
  58. package/src/input/command-registry.ts +329 -0
  59. package/src/input/commands/agent-externalized-tui.ts +73 -0
  60. package/src/input/commands/agent-workspace-runtime.ts +17 -0
  61. package/src/input/commands/branch-runtime.ts +72 -0
  62. package/src/input/commands/cloudflare-runtime.ts +370 -0
  63. package/src/input/commands/config.ts +18 -0
  64. package/src/input/commands/control-room-runtime.ts +255 -0
  65. package/src/input/commands/conversation-runtime.ts +207 -0
  66. package/src/input/commands/discovery-runtime.ts +52 -0
  67. package/src/input/commands/eval.ts +204 -0
  68. package/src/input/commands/experience-runtime.ts +278 -0
  69. package/src/input/commands/guidance-runtime.ts +106 -0
  70. package/src/input/commands/health-runtime.ts +434 -0
  71. package/src/input/commands/hooks-runtime.ts +148 -0
  72. package/src/input/commands/incident-runtime.ts +95 -0
  73. package/src/input/commands/integration-runtime.ts +394 -0
  74. package/src/input/commands/intelligence-runtime.ts +223 -0
  75. package/src/input/commands/knowledge.ts +531 -0
  76. package/src/input/commands/local-auth-runtime.ts +105 -0
  77. package/src/input/commands/local-provider-runtime.ts +170 -0
  78. package/src/input/commands/local-runtime.ts +392 -0
  79. package/src/input/commands/local-setup-review.ts +199 -0
  80. package/src/input/commands/local-setup-transfer.ts +135 -0
  81. package/src/input/commands/local-setup.ts +282 -0
  82. package/src/input/commands/managed-runtime.ts +209 -0
  83. package/src/input/commands/marketplace-runtime.ts +290 -0
  84. package/src/input/commands/mcp-runtime.ts +432 -0
  85. package/src/input/commands/memory-product-runtime.ts +111 -0
  86. package/src/input/commands/memory.ts +151 -0
  87. package/src/input/commands/notify-runtime.ts +83 -0
  88. package/src/input/commands/onboarding-runtime.ts +14 -0
  89. package/src/input/commands/operator-panel-runtime.ts +146 -0
  90. package/src/input/commands/operator-runtime.ts +392 -0
  91. package/src/input/commands/planning-runtime.ts +205 -0
  92. package/src/input/commands/platform-access-runtime.ts +422 -0
  93. package/src/input/commands/platform-services-runtime.ts +246 -0
  94. package/src/input/commands/policy-dispatch.ts +339 -0
  95. package/src/input/commands/policy.ts +17 -0
  96. package/src/input/commands/product-runtime.ts +351 -0
  97. package/src/input/commands/profile-sync-runtime.ts +99 -0
  98. package/src/input/commands/provider-accounts-runtime.ts +113 -0
  99. package/src/input/commands/provider.ts +363 -0
  100. package/src/input/commands/qrcode-runtime.ts +20 -0
  101. package/src/input/commands/quit-shared.ts +162 -0
  102. package/src/input/commands/recall-bundle.ts +132 -0
  103. package/src/input/commands/recall-capture.ts +152 -0
  104. package/src/input/commands/recall-query.ts +229 -0
  105. package/src/input/commands/recall-review.ts +98 -0
  106. package/src/input/commands/recall-shared.ts +22 -0
  107. package/src/input/commands/remote-runtime-pool.ts +106 -0
  108. package/src/input/commands/remote-runtime-setup.ts +199 -0
  109. package/src/input/commands/remote-runtime.ts +431 -0
  110. package/src/input/commands/replay-runtime.ts +18 -0
  111. package/src/input/commands/runtime-services.ts +291 -0
  112. package/src/input/commands/schedule-runtime.ts +91 -0
  113. package/src/input/commands/services-runtime.ts +209 -0
  114. package/src/input/commands/session-content.ts +408 -0
  115. package/src/input/commands/session-workflow.ts +464 -0
  116. package/src/input/commands/session.ts +375 -0
  117. package/src/input/commands/settings-sync-runtime.ts +174 -0
  118. package/src/input/commands/share-runtime.ts +119 -0
  119. package/src/input/commands/shell-core.ts +307 -0
  120. package/src/input/commands/skills-runtime.ts +221 -0
  121. package/src/input/commands/subscription-runtime.ts +434 -0
  122. package/src/input/commands/tasks-runtime.ts +230 -0
  123. package/src/input/commands/teamwork-runtime.ts +339 -0
  124. package/src/input/commands/teleport-runtime.ts +57 -0
  125. package/src/input/commands/tts-runtime.ts +29 -0
  126. package/src/input/commands/work-plan-runtime.ts +169 -0
  127. package/src/input/commands.ts +131 -0
  128. package/src/input/feed-context-factory.ts +254 -0
  129. package/src/input/file-picker.ts +192 -0
  130. package/src/input/handler-command-route.ts +180 -0
  131. package/src/input/handler-content-actions.ts +497 -0
  132. package/src/input/handler-feed-routes.ts +648 -0
  133. package/src/input/handler-feed.ts +452 -0
  134. package/src/input/handler-interactions.ts +281 -0
  135. package/src/input/handler-modal-routes.ts +418 -0
  136. package/src/input/handler-modal-stack.ts +263 -0
  137. package/src/input/handler-modal-token-routes.ts +329 -0
  138. package/src/input/handler-onboarding-cloudflare.ts +391 -0
  139. package/src/input/handler-onboarding.ts +620 -0
  140. package/src/input/handler-picker-routes.ts +472 -0
  141. package/src/input/handler-prompt-buffer.ts +320 -0
  142. package/src/input/handler-shortcuts.ts +213 -0
  143. package/src/input/handler-ui-state.ts +372 -0
  144. package/src/input/handler.ts +729 -0
  145. package/src/input/input-history.ts +297 -0
  146. package/src/input/keybindings.ts +292 -0
  147. package/src/input/mcp-workspace.ts +554 -0
  148. package/src/input/model-picker-provider-filter.ts +28 -0
  149. package/src/input/model-picker-types.ts +137 -0
  150. package/src/input/model-picker.ts +797 -0
  151. package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
  152. package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
  153. package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
  154. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
  155. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
  156. package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
  157. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
  158. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
  159. package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
  160. package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
  161. package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
  162. package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
  163. package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
  164. package/src/input/onboarding/onboarding-wizard.ts +711 -0
  165. package/src/input/panel-integration-actions.ts +78 -0
  166. package/src/input/profile-picker-modal.ts +222 -0
  167. package/src/input/search.ts +100 -0
  168. package/src/input/selection-modal.ts +163 -0
  169. package/src/input/selection.ts +135 -0
  170. package/src/input/session-picker-modal.ts +136 -0
  171. package/src/input/settings-modal-behavior.ts +37 -0
  172. package/src/input/settings-modal-secrets.ts +41 -0
  173. package/src/input/settings-modal-subscriptions.ts +95 -0
  174. package/src/input/settings-modal-types.ts +91 -0
  175. package/src/input/settings-modal.ts +793 -0
  176. package/src/input/submission-intent.ts +17 -0
  177. package/src/input/submission-router.ts +59 -0
  178. package/src/input/tts-settings-actions.ts +100 -0
  179. package/src/main.ts +792 -0
  180. package/src/mcp/runtime-reload.ts +81 -0
  181. package/src/panels/agent-inspector-panel.ts +521 -0
  182. package/src/panels/agent-inspector-shared.ts +94 -0
  183. package/src/panels/agent-logs-panel.ts +559 -0
  184. package/src/panels/agent-logs-shared.ts +129 -0
  185. package/src/panels/approval-panel.ts +150 -0
  186. package/src/panels/automation-control-panel.ts +212 -0
  187. package/src/panels/base-panel.ts +254 -0
  188. package/src/panels/builtin/agent.ts +117 -0
  189. package/src/panels/builtin/development.ts +31 -0
  190. package/src/panels/builtin/knowledge.ts +26 -0
  191. package/src/panels/builtin/operations.ts +349 -0
  192. package/src/panels/builtin/session.ts +129 -0
  193. package/src/panels/builtin/shared.ts +274 -0
  194. package/src/panels/builtin-panels.ts +23 -0
  195. package/src/panels/cockpit-panel.ts +183 -0
  196. package/src/panels/communication-panel.ts +153 -0
  197. package/src/panels/confirm-state.ts +61 -0
  198. package/src/panels/context-visualizer-panel.ts +204 -0
  199. package/src/panels/control-plane-panel.ts +211 -0
  200. package/src/panels/cost-tracker-panel.ts +444 -0
  201. package/src/panels/debug-panel.ts +432 -0
  202. package/src/panels/diff-panel.ts +520 -0
  203. package/src/panels/docs-panel.ts +283 -0
  204. package/src/panels/eval-panel.ts +399 -0
  205. package/src/panels/file-explorer-panel.ts +584 -0
  206. package/src/panels/file-preview-panel.ts +434 -0
  207. package/src/panels/forensics-panel.ts +364 -0
  208. package/src/panels/git-panel.ts +638 -0
  209. package/src/panels/hooks-panel.ts +239 -0
  210. package/src/panels/incident-review-panel.ts +197 -0
  211. package/src/panels/index.ts +46 -0
  212. package/src/panels/intelligence-panel.ts +176 -0
  213. package/src/panels/knowledge-panel.ts +345 -0
  214. package/src/panels/local-auth-panel.ts +130 -0
  215. package/src/panels/marketplace-panel.ts +212 -0
  216. package/src/panels/memory-panel.ts +225 -0
  217. package/src/panels/ops-control-panel.ts +150 -0
  218. package/src/panels/ops-strategy-panel.ts +235 -0
  219. package/src/panels/orchestration-panel.ts +273 -0
  220. package/src/panels/panel-list-panel.ts +509 -0
  221. package/src/panels/panel-manager.ts +570 -0
  222. package/src/panels/panel-picker.ts +106 -0
  223. package/src/panels/plan-dashboard-panel.ts +274 -0
  224. package/src/panels/plugins-panel.ts +178 -0
  225. package/src/panels/policy-panel.ts +308 -0
  226. package/src/panels/polish.ts +717 -0
  227. package/src/panels/project-planning-panel.ts +711 -0
  228. package/src/panels/provider-account-snapshot.ts +259 -0
  229. package/src/panels/provider-accounts-panel.ts +218 -0
  230. package/src/panels/provider-health-domains.ts +215 -0
  231. package/src/panels/provider-health-panel.ts +727 -0
  232. package/src/panels/provider-health-tracker.ts +115 -0
  233. package/src/panels/provider-stats-panel.ts +366 -0
  234. package/src/panels/qr-panel.ts +182 -0
  235. package/src/panels/remote-panel.ts +449 -0
  236. package/src/panels/routes-panel.ts +178 -0
  237. package/src/panels/sandbox-panel.ts +283 -0
  238. package/src/panels/schedule-panel.ts +329 -0
  239. package/src/panels/scrollable-list-panel.ts +491 -0
  240. package/src/panels/search-focus.ts +32 -0
  241. package/src/panels/security-panel.ts +295 -0
  242. package/src/panels/services-panel.ts +231 -0
  243. package/src/panels/session-browser-panel.ts +400 -0
  244. package/src/panels/session-maintenance.ts +125 -0
  245. package/src/panels/settings-sync-panel.ts +120 -0
  246. package/src/panels/skills-panel.ts +431 -0
  247. package/src/panels/subscription-panel.ts +263 -0
  248. package/src/panels/symbol-outline-panel.ts +486 -0
  249. package/src/panels/system-messages-panel.ts +230 -0
  250. package/src/panels/tasks-panel.ts +399 -0
  251. package/src/panels/thinking-panel.ts +304 -0
  252. package/src/panels/token-budget-panel.ts +475 -0
  253. package/src/panels/tool-inspector-panel.ts +429 -0
  254. package/src/panels/types.ts +54 -0
  255. package/src/panels/watchers-panel.ts +193 -0
  256. package/src/panels/work-plan-panel.ts +175 -0
  257. package/src/panels/worktree-panel.ts +182 -0
  258. package/src/panels/wrfc-panel.ts +609 -0
  259. package/src/permissions/prompt.ts +165 -0
  260. package/src/planning/project-planning-coordinator.ts +543 -0
  261. package/src/plugins/loader.ts +15 -0
  262. package/src/renderer/agent-detail-modal.ts +331 -0
  263. package/src/renderer/agent-workspace.ts +238 -0
  264. package/src/renderer/ansi-sanitize.ts +76 -0
  265. package/src/renderer/autocomplete-overlay.ts +154 -0
  266. package/src/renderer/block-actions.ts +76 -0
  267. package/src/renderer/bookmark-modal.ts +101 -0
  268. package/src/renderer/bottom-bar.ts +58 -0
  269. package/src/renderer/buffer.ts +113 -0
  270. package/src/renderer/code-block.ts +373 -0
  271. package/src/renderer/compositor.ts +283 -0
  272. package/src/renderer/context-inspector.ts +219 -0
  273. package/src/renderer/conversation-layout.ts +67 -0
  274. package/src/renderer/conversation-overlays.ts +140 -0
  275. package/src/renderer/conversation-surface.ts +260 -0
  276. package/src/renderer/diff-view.ts +132 -0
  277. package/src/renderer/diff.ts +130 -0
  278. package/src/renderer/file-picker-overlay.ts +101 -0
  279. package/src/renderer/file-tree.ts +153 -0
  280. package/src/renderer/fullscreen-primitives.ts +130 -0
  281. package/src/renderer/fullscreen-workspace.ts +199 -0
  282. package/src/renderer/git-status.ts +89 -0
  283. package/src/renderer/help-overlay.ts +267 -0
  284. package/src/renderer/history-search-overlay.ts +73 -0
  285. package/src/renderer/layout-engine.ts +97 -0
  286. package/src/renderer/layout.ts +32 -0
  287. package/src/renderer/live-tail-modal.ts +156 -0
  288. package/src/renderer/markdown.ts +635 -0
  289. package/src/renderer/mcp-workspace.ts +237 -0
  290. package/src/renderer/modal-factory.ts +467 -0
  291. package/src/renderer/modal-utils.ts +24 -0
  292. package/src/renderer/model-picker-overlay.ts +473 -0
  293. package/src/renderer/model-workspace.ts +488 -0
  294. package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
  295. package/src/renderer/overlay-box.ts +146 -0
  296. package/src/renderer/overlay-viewport.ts +104 -0
  297. package/src/renderer/panel-composite.ts +158 -0
  298. package/src/renderer/panel-picker-overlay.ts +202 -0
  299. package/src/renderer/panel-tab-bar.ts +69 -0
  300. package/src/renderer/panel-workspace-bar.ts +42 -0
  301. package/src/renderer/process-indicator.ts +96 -0
  302. package/src/renderer/process-modal.ts +656 -0
  303. package/src/renderer/process-summary.ts +67 -0
  304. package/src/renderer/profile-picker-modal.ts +129 -0
  305. package/src/renderer/progress.ts +98 -0
  306. package/src/renderer/qr-renderer.ts +120 -0
  307. package/src/renderer/search-overlay.ts +54 -0
  308. package/src/renderer/selection-modal-overlay.ts +214 -0
  309. package/src/renderer/semantic-diff.ts +369 -0
  310. package/src/renderer/session-picker-modal.ts +127 -0
  311. package/src/renderer/settings-modal-helpers.ts +193 -0
  312. package/src/renderer/settings-modal.ts +537 -0
  313. package/src/renderer/shell-surface.ts +88 -0
  314. package/src/renderer/status-glyphs.ts +21 -0
  315. package/src/renderer/status-token.ts +67 -0
  316. package/src/renderer/surface-layout.ts +101 -0
  317. package/src/renderer/syntax-highlighter.ts +542 -0
  318. package/src/renderer/system-message.ts +83 -0
  319. package/src/renderer/tab-strip.ts +108 -0
  320. package/src/renderer/text-layout.ts +31 -0
  321. package/src/renderer/thinking.ts +17 -0
  322. package/src/renderer/tool-call.ts +234 -0
  323. package/src/renderer/ui-factory.ts +524 -0
  324. package/src/renderer/ui-primitives.ts +96 -0
  325. package/src/runtime/bootstrap-command-context.ts +278 -0
  326. package/src/runtime/bootstrap-command-parts.ts +386 -0
  327. package/src/runtime/bootstrap-core.ts +540 -0
  328. package/src/runtime/bootstrap-hook-bridge.ts +112 -0
  329. package/src/runtime/bootstrap-shell.ts +283 -0
  330. package/src/runtime/bootstrap.ts +575 -0
  331. package/src/runtime/cloudflare-control-plane.ts +349 -0
  332. package/src/runtime/context.ts +142 -0
  333. package/src/runtime/diagnostics/panels/index.ts +24 -0
  334. package/src/runtime/diagnostics/panels/ops.ts +156 -0
  335. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  336. package/src/runtime/diagnostics/panels/policy.ts +177 -0
  337. package/src/runtime/index.ts +662 -0
  338. package/src/runtime/onboarding/apply.ts +642 -0
  339. package/src/runtime/onboarding/derivation.ts +534 -0
  340. package/src/runtime/onboarding/index.ts +7 -0
  341. package/src/runtime/onboarding/markers.ts +148 -0
  342. package/src/runtime/onboarding/snapshot.ts +406 -0
  343. package/src/runtime/onboarding/state.ts +141 -0
  344. package/src/runtime/onboarding/types.ts +404 -0
  345. package/src/runtime/onboarding/verify.ts +171 -0
  346. package/src/runtime/operator-token-cleanup.ts +27 -0
  347. package/src/runtime/perf/panel-contracts.ts +32 -0
  348. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  349. package/src/runtime/sandbox-public-gaps.ts +358 -0
  350. package/src/runtime/services.ts +670 -0
  351. package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
  352. package/src/runtime/store/domains/index.ts +222 -0
  353. package/src/runtime/store/domains/panels.ts +117 -0
  354. package/src/runtime/store/domains/ui-perf.ts +103 -0
  355. package/src/runtime/store/index.ts +305 -0
  356. package/src/runtime/store/selectors/index.ts +359 -0
  357. package/src/runtime/store/state.ts +145 -0
  358. package/src/runtime/surface-feature-flags.ts +65 -0
  359. package/src/runtime/terminal-output-guard.ts +228 -0
  360. package/src/runtime/ui/index.ts +39 -0
  361. package/src/runtime/ui/model-picker/data-provider.ts +182 -0
  362. package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
  363. package/src/runtime/ui/model-picker/index.ts +59 -0
  364. package/src/runtime/ui/model-picker/types.ts +149 -0
  365. package/src/runtime/ui/provider-health/data-provider.ts +244 -0
  366. package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
  367. package/src/runtime/ui/provider-health/index.ts +46 -0
  368. package/src/runtime/ui/provider-health/types.ts +146 -0
  369. package/src/runtime/ui-events.ts +1 -0
  370. package/src/runtime/ui-read-model-helpers.ts +1 -0
  371. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
  372. package/src/runtime/ui-read-models-observability-options.ts +1 -0
  373. package/src/runtime/ui-read-models-observability-remote.ts +1 -0
  374. package/src/runtime/ui-read-models-observability-security.ts +1 -0
  375. package/src/runtime/ui-read-models-observability-system.ts +1 -0
  376. package/src/runtime/ui-read-models-observability.ts +1 -0
  377. package/src/runtime/ui-read-models.ts +61 -0
  378. package/src/runtime/ui-service-queries.ts +1 -0
  379. package/src/runtime/ui-services.ts +190 -0
  380. package/src/scripts/process-messages.ts +42 -0
  381. package/src/shell/blocking-input.ts +98 -0
  382. package/src/shell/service-settings-sync.ts +273 -0
  383. package/src/shell/ui-openers.ts +352 -0
  384. package/src/tools/index.ts +1 -0
  385. package/src/tools/wrfc-agent-guard.ts +49 -0
  386. package/src/types/grid.ts +48 -0
  387. package/src/types/sql-js.d.ts +15 -0
  388. package/src/utils/clipboard.ts +22 -0
  389. package/src/utils/splash-lines.ts +46 -0
  390. package/src/utils/terminal-width.ts +185 -0
  391. package/src/verification/live-verifier.ts +430 -0
  392. package/src/verification/verification-ledger.ts +242 -0
  393. package/src/version.ts +17 -0
  394. package/src/widget/index.ts +2 -0
  395. package/src/widget/types.ts +9 -0
  396. package/src/widget/widget.ts +8 -0
  397. package/src/work-plans/work-plan-store.ts +374 -0
  398. package/tsconfig.json +18 -0
@@ -0,0 +1,259 @@
1
+ import { listBuiltinSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
2
+ import type {
3
+ ProviderAccountInspectionQuery,
4
+ } from '../runtime/ui-service-queries.ts';
5
+
6
+ export type ProviderAuthRoute = 'api-key' | 'subscription' | 'service-oauth' | 'unconfigured';
7
+ export type ProviderAuthFreshness = 'healthy' | 'expiring' | 'expired' | 'pending' | 'unconfigured';
8
+
9
+ export interface ProviderUsageWindow {
10
+ readonly label: string;
11
+ readonly detail: string;
12
+ }
13
+
14
+ export interface ProviderRouteRecord {
15
+ readonly route: Exclude<ProviderAuthRoute, 'unconfigured'>;
16
+ readonly usable: boolean;
17
+ readonly freshness: ProviderAuthFreshness;
18
+ readonly detail: string;
19
+ readonly issues: readonly string[];
20
+ }
21
+
22
+ export interface ProviderAccountRecord {
23
+ readonly providerId: string;
24
+ readonly active: boolean;
25
+ readonly modelCount: number;
26
+ readonly configured: boolean;
27
+ readonly oauthReady: boolean;
28
+ readonly pendingLogin: boolean;
29
+ readonly availableRoutes: readonly ProviderAuthRoute[];
30
+ readonly preferredRoute: ProviderAuthRoute;
31
+ readonly activeRoute: ProviderAuthRoute;
32
+ readonly activeRouteReason: string;
33
+ readonly authFreshness: ProviderAuthFreshness;
34
+ readonly fallbackRoute?: ProviderAuthRoute;
35
+ readonly fallbackRisk?: string;
36
+ readonly expiresAt?: number;
37
+ readonly tokenType?: string;
38
+ readonly notes: readonly string[];
39
+ readonly usageWindows: readonly ProviderUsageWindow[];
40
+ readonly issues: readonly string[];
41
+ readonly recommendedActions: readonly string[];
42
+ readonly routeRecords: readonly ProviderRouteRecord[];
43
+ }
44
+
45
+ export interface ProviderAccountSnapshot {
46
+ readonly capturedAt: number;
47
+ readonly providers: readonly ProviderAccountRecord[];
48
+ readonly configuredCount: number;
49
+ readonly issueCount: number;
50
+ }
51
+
52
+ export interface ProviderAccountSnapshotDeps extends ProviderAccountInspectionQuery {}
53
+
54
+ export interface ProviderAccountSnapshotQuery {
55
+ readonly loadSnapshot: () => Promise<ProviderAccountSnapshot>;
56
+ }
57
+
58
+ export function createProviderAccountSnapshotQuery(
59
+ deps: ProviderAccountSnapshotDeps,
60
+ ): ProviderAccountSnapshotQuery {
61
+ return {
62
+ loadSnapshot: () => buildProviderAccountSnapshot(deps),
63
+ };
64
+ }
65
+
66
+ function determineActiveRoute(routes: readonly ProviderAuthRoute[]): ProviderAuthRoute {
67
+ if (routes.includes('subscription')) return 'subscription';
68
+ if (routes.includes('service-oauth')) return 'service-oauth';
69
+ if (routes.includes('api-key')) return 'api-key';
70
+ return 'unconfigured';
71
+ }
72
+
73
+ function isExpired(expiresAt?: number): boolean {
74
+ return typeof expiresAt === 'number' && Number.isFinite(expiresAt) && expiresAt <= Date.now();
75
+ }
76
+
77
+ function determineFreshness(input: {
78
+ readonly hasSubscription: boolean;
79
+ readonly expiresAt?: number;
80
+ readonly pending: boolean;
81
+ readonly hasServiceOAuth: boolean;
82
+ readonly hasApiKey: boolean;
83
+ }): ProviderAuthFreshness {
84
+ if (input.hasSubscription) {
85
+ if (isExpired(input.expiresAt)) return 'expired';
86
+ if (input.expiresAt && input.expiresAt <= Date.now() + 24 * 60 * 60 * 1000) return 'expiring';
87
+ if (input.pending) return 'pending';
88
+ return 'healthy';
89
+ }
90
+ if (input.hasServiceOAuth || input.hasApiKey) return 'healthy';
91
+ return 'unconfigured';
92
+ }
93
+
94
+ function builtinWindowsForProvider(providerId: string): readonly ProviderUsageWindow[] {
95
+ if (providerId === 'openai') {
96
+ return [
97
+ { label: '5-hour window', detail: 'Subscription-backed Codex access may be constrained by rolling 5-hour usage limits.' },
98
+ { label: '1-week window', detail: 'Subscription-backed Codex access may also be constrained by a rolling weekly limit.' },
99
+ ];
100
+ }
101
+ return [];
102
+ }
103
+
104
+ export async function buildProviderAccountSnapshot(
105
+ deps: ProviderAccountSnapshotDeps,
106
+ ): Promise<ProviderAccountSnapshot> {
107
+ const models = deps.providerModels.listModels();
108
+ const services = deps.services.getAll();
109
+ const subscriptions = deps.subscriptions;
110
+ const builtinSubscriptionProviders = new Set(listBuiltinSubscriptionProviders().map((entry) => entry.provider));
111
+ const serviceInspections = await Promise.all(Object.keys(services).map(async (name) => ({
112
+ name,
113
+ inspection: await deps.services.inspect(name),
114
+ })));
115
+ const serviceOauthByProvider = new Map<string, { configured: boolean; usable: boolean }>();
116
+ for (const { inspection } of serviceInspections) {
117
+ if (!inspection || inspection.config.authType !== 'oauth') continue;
118
+ const providerId = inspection.config.providerId ?? inspection.config.name;
119
+ const existing = serviceOauthByProvider.get(providerId);
120
+ serviceOauthByProvider.set(providerId, {
121
+ configured: true,
122
+ usable: Boolean(existing?.usable || inspection.hasPrimaryCredential),
123
+ });
124
+ }
125
+
126
+ const providerIds = new Set<string>([
127
+ ...models.map((model) => model.provider),
128
+ ...Object.keys(services),
129
+ ...subscriptions.list().map((entry) => entry.provider),
130
+ ...subscriptions.listPending().map((entry) => entry.provider),
131
+ ...builtinSubscriptionProviders,
132
+ ]);
133
+
134
+ const providers = await Promise.all([...providerIds].sort((a, b) => a.localeCompare(b)).map(async (providerId) => {
135
+ const subscription = subscriptions.get(providerId);
136
+ const pending = subscriptions.getPending(providerId);
137
+ const serviceConfig = Object.values(services).find((entry) => (entry.providerId ?? entry.name) === providerId) ?? null;
138
+ const serviceOauth = serviceOauthByProvider.get(providerId);
139
+ const hasApiKey = Boolean(serviceConfig?.tokenKey && deps.environment.hasEnvironmentVariable(serviceConfig.tokenKey));
140
+ const hasSubscription = subscription != null;
141
+ const hasServiceOAuth = Boolean(serviceOauth?.configured);
142
+ const routes: ProviderAuthRoute[] = [];
143
+ if (hasApiKey) routes.push('api-key');
144
+ if (hasSubscription) routes.push('subscription');
145
+ if (hasServiceOAuth) routes.push('service-oauth');
146
+ if (routes.length === 0) routes.push('unconfigured');
147
+
148
+ const usableRoutes: Exclude<ProviderAuthRoute, 'unconfigured'>[] = [];
149
+ if (hasApiKey) usableRoutes.push('api-key');
150
+ if (hasSubscription && !isExpired(subscription.expiresAt)) usableRoutes.push('subscription');
151
+ if (hasServiceOAuth) usableRoutes.push('service-oauth');
152
+
153
+ const activeRoute = determineActiveRoute(usableRoutes.length > 0 ? usableRoutes : routes);
154
+ const preferredRoute = determineActiveRoute(routes);
155
+ const freshness = determineFreshness({
156
+ hasSubscription,
157
+ expiresAt: subscription?.expiresAt,
158
+ pending: pending != null,
159
+ hasServiceOAuth,
160
+ hasApiKey,
161
+ });
162
+ const usageWindows = builtinWindowsForProvider(providerId);
163
+ const routeRecords: ProviderRouteRecord[] = [];
164
+
165
+ if (hasApiKey) {
166
+ routeRecords.push({
167
+ route: 'api-key',
168
+ usable: true,
169
+ freshness: 'healthy',
170
+ detail: 'Ambient API key is available for direct provider access.',
171
+ issues: [],
172
+ });
173
+ }
174
+ if (hasSubscription) {
175
+ routeRecords.push({
176
+ route: 'subscription',
177
+ usable: !isExpired(subscription?.expiresAt),
178
+ freshness: pending ? 'pending' : isExpired(subscription?.expiresAt) ? 'expired' : 'healthy',
179
+ detail: subscription?.overrideAmbientApiKeys
180
+ ? 'Subscription route is configured to override ambient API-key resolution.'
181
+ : 'Subscription route is configured, but ambient API keys remain active unless selected explicitly.',
182
+ issues: isExpired(subscription?.expiresAt) ? ['Stored subscription session is expired.'] : [],
183
+ });
184
+ }
185
+ if (hasServiceOAuth) {
186
+ routeRecords.push({
187
+ route: 'service-oauth',
188
+ usable: true,
189
+ freshness: 'healthy',
190
+ detail: 'Service OAuth credential is available for this provider.',
191
+ issues: [],
192
+ });
193
+ }
194
+
195
+ const issues: string[] = [];
196
+ const notes: string[] = [`${models.filter((model) => model.provider === providerId).length} model${models.filter((model) => model.provider === providerId).length === 1 ? '' : 's'} registered`];
197
+ if (serviceConfig) notes.push(`service config: ${serviceConfig.authType}`);
198
+ const recommendedActions: string[] = [];
199
+ if (routes.length === 1 && routes[0] === 'unconfigured') {
200
+ issues.push('Provider has no configured auth route.');
201
+ recommendedActions.push(`Configure API keys, subscriptions, or service OAuth for ${providerId}.`);
202
+ }
203
+ if (hasSubscription && isExpired(subscription?.expiresAt)) {
204
+ issues.push('Stored subscription session is expired and needs refresh.');
205
+ recommendedActions.push(`Refresh or replace the ${providerId} subscription session before relying on it.`);
206
+ } else if (hasSubscription && subscription?.expiresAt && subscription.expiresAt <= Date.now() + 24 * 60 * 60 * 1000) {
207
+ issues.push('Stored subscription session is nearing expiry.');
208
+ recommendedActions.push(`Renew or verify the ${providerId} subscription session soon to avoid route drift.`);
209
+ }
210
+ if (pending) {
211
+ issues.push('Provider has a pending OAuth login that has not been completed yet.');
212
+ recommendedActions.push(`Finish /subscription login ${providerId} finish <code> or clear the pending login.`);
213
+ }
214
+ if (hasSubscription && hasApiKey) {
215
+ issues.push('Provider has both subscription and API-key auth paths; routing must remain explicit.');
216
+ recommendedActions.push('Review provider routing before switching models or auth paths.');
217
+ }
218
+ if (hasServiceOAuth && !serviceOauth?.usable) {
219
+ issues.push('Service OAuth is configured but missing a usable credential.');
220
+ recommendedActions.push(`Repair service OAuth credentials for ${providerId} in /services or /settings.`);
221
+ }
222
+
223
+ return {
224
+ providerId,
225
+ active: activeRoute !== 'unconfigured',
226
+ modelCount: models.filter((model) => model.provider === providerId).length,
227
+ configured: hasApiKey || hasSubscription || hasServiceOAuth || models.some((model) => model.provider === providerId),
228
+ oauthReady: Boolean(serviceConfig?.oauth),
229
+ pendingLogin: Boolean(pending),
230
+ availableRoutes: routes,
231
+ preferredRoute,
232
+ activeRoute,
233
+ activeRouteReason: activeRoute === 'subscription'
234
+ ? 'Subscription route is currently preferred.'
235
+ : activeRoute === 'service-oauth'
236
+ ? 'Service OAuth route is currently preferred.'
237
+ : activeRoute === 'api-key'
238
+ ? 'Ambient API-key route is currently preferred.'
239
+ : 'No usable auth route is configured for this provider.',
240
+ authFreshness: freshness,
241
+ fallbackRoute: activeRoute !== preferredRoute ? preferredRoute : undefined,
242
+ fallbackRisk: hasSubscription && hasApiKey ? 'Both subscription and API key are present; check route priority.' : undefined,
243
+ expiresAt: subscription?.expiresAt,
244
+ tokenType: subscription?.tokenType,
245
+ notes,
246
+ usageWindows,
247
+ issues,
248
+ recommendedActions,
249
+ routeRecords,
250
+ };
251
+ }));
252
+
253
+ return {
254
+ capturedAt: Date.now(),
255
+ providers,
256
+ configuredCount: providers.filter((provider) => provider.configured).length,
257
+ issueCount: providers.reduce((sum, provider) => sum + provider.issues.length, 0),
258
+ };
259
+ }
@@ -0,0 +1,218 @@
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
+ import { ScrollableListPanel } from './scrollable-list-panel.ts';
4
+ import {
5
+ buildDetailBlock,
6
+ buildEmptyState,
7
+ buildGuidanceLine,
8
+ buildKeyValueLine,
9
+ buildPanelListRow,
10
+ buildPanelLine,
11
+ buildSummaryBlock,
12
+ buildPanelWorkspace,
13
+ DEFAULT_PANEL_PALETTE,
14
+ resolvePrimaryScrollableSection,
15
+ type PanelWorkspaceSection,
16
+ } from './polish.ts';
17
+ import {
18
+ type ProviderAccountRecord,
19
+ type ProviderAccountSnapshot,
20
+ type ProviderAccountSnapshotQuery,
21
+ } from './provider-account-snapshot.ts';
22
+
23
+ export interface ProviderAccountsPanelDeps {
24
+ readonly providerAccounts: ProviderAccountSnapshotQuery;
25
+ }
26
+
27
+ const C = {
28
+ ...DEFAULT_PANEL_PALETTE,
29
+ selectBg: '#1e293b',
30
+ } as const;
31
+
32
+ export class ProviderAccountsPanel extends ScrollableListPanel<ProviderAccountRecord> {
33
+ private records: ProviderAccountRecord[] = [];
34
+ private loading = false;
35
+ private readonly providerAccounts: ProviderAccountSnapshotQuery;
36
+
37
+ public constructor(deps: ProviderAccountsPanelDeps) {
38
+ super('accounts', 'Accounts', 'Q', 'monitoring');
39
+ this.showSelectionGutter = true; // I5: non-color selection affordance
40
+ this.providerAccounts = deps.providerAccounts;
41
+ void this.refresh();
42
+ }
43
+
44
+ public override onActivate(): void {
45
+ super.onActivate();
46
+ if (!this.loading) void this.refresh();
47
+ }
48
+
49
+ protected getItems(): readonly ProviderAccountRecord[] {
50
+ return this.records;
51
+ }
52
+
53
+ protected renderItem(item: ProviderAccountRecord, _index: number, selected: boolean, width: number): Line {
54
+ return buildPanelListRow(width, [
55
+ { text: item.providerId.padEnd(16), fg: item.active ? C.good : C.value },
56
+ { text: ` ${item.activeRoute.padEnd(14)}`, fg: item.activeRoute === 'subscription' ? C.info : item.activeRoute === 'api-key' ? C.warn : item.activeRoute === 'service-oauth' ? C.value : C.dim },
57
+ { text: ` models=${String(item.modelCount).padEnd(4)}`, fg: C.dim },
58
+ { text: ` ${item.authFreshness.padEnd(10)}`, fg: item.authFreshness === 'expired' ? C.bad : item.authFreshness === 'expiring' || item.authFreshness === 'pending' ? C.warn : C.dim },
59
+ { text: ` issues=${String(item.issues.length).padEnd(2)}`, fg: item.issues.length > 0 ? C.bad : C.good },
60
+ ], C, { selected });
61
+ }
62
+
63
+ public handleInput(key: string): boolean {
64
+ if (key === 'r') {
65
+ void this.refresh();
66
+ return true;
67
+ }
68
+ return super.handleInput(key);
69
+ }
70
+
71
+ private async refresh(): Promise<void> {
72
+ this.loading = true;
73
+ this.markDirty();
74
+ const snapshot = await this.buildSnapshot();
75
+ this.records = [...snapshot.providers];
76
+ this.clampSelection();
77
+ this.loading = false;
78
+ this.markDirty();
79
+ }
80
+
81
+ private async buildSnapshot(): Promise<ProviderAccountSnapshot> {
82
+ return this.providerAccounts.loadSnapshot();
83
+ }
84
+
85
+ public render(width: number, height: number): Line[] {
86
+ this.needsRender = false;
87
+ const intro = 'Provider auth routes, subscription posture, quota-window hints, and routing-safety notes.';
88
+ const footerLines = [buildPanelLine(width, [[' Up/Down move r refresh /accounts routes <provider> /accounts repair <provider>', C.dim]])];
89
+ if (this.loading && this.records.length === 0) {
90
+ const lines = buildPanelWorkspace(width, height, {
91
+ title: 'Provider Account Control Room',
92
+ intro,
93
+ sections: [{ lines: [buildPanelLine(width, [[' Loading provider account posture...', C.info]])] }],
94
+ palette: C,
95
+ });
96
+ while (lines.length < height) lines.push(createEmptyLine(width));
97
+ return lines;
98
+ }
99
+ if (this.records.length === 0) {
100
+ const lines = buildPanelWorkspace(width, height, {
101
+ title: 'Provider Account Control Room',
102
+ intro,
103
+ sections: [{
104
+ lines: buildEmptyState(width, ' No provider accounts discovered.', 'Configure API keys or subscriptions to populate account routing posture.', [{ command: '/provider', summary: 'review current provider and model posture' }], C),
105
+ }],
106
+ palette: C,
107
+ });
108
+ while (lines.length < height) lines.push(createEmptyLine(width));
109
+ return lines;
110
+ }
111
+ const issueCount = this.records.reduce((sum, record) => sum + record.issues.length + (record.fallbackRisk ? 1 : 0), 0);
112
+ const expiredCount = this.records.filter((record) => record.authFreshness === 'expired').length;
113
+ const pendingCount = this.records.filter((record) => record.pendingLogin).length;
114
+ const fallbackCount = this.records.filter((record) => Boolean(record.fallbackRisk)).length;
115
+ const selected = this.records[this.selectedIndex]!;
116
+ const postureLines: Line[] = [
117
+ buildKeyValueLine(width, [
118
+ { label: 'providers', value: String(this.records.length), valueColor: C.value },
119
+ { label: 'expired auth', value: String(expiredCount), valueColor: expiredCount > 0 ? C.bad : C.good },
120
+ { label: 'pending login', value: String(pendingCount), valueColor: pendingCount > 0 ? C.warn : C.dim },
121
+ { label: 'fallback risk', value: String(fallbackCount), valueColor: fallbackCount > 0 ? C.warn : C.good },
122
+ ], C),
123
+ buildKeyValueLine(width, [
124
+ { label: 'total issues', value: String(issueCount), valueColor: issueCount > 0 ? C.bad : C.good },
125
+ { label: 'selected', value: selected.providerId, valueColor: C.info },
126
+ { label: 'route', value: selected.activeRoute, valueColor: selected.activeRoute === 'subscription' ? C.info : selected.activeRoute === 'api-key' ? C.warn : C.value },
127
+ { label: 'freshness', value: selected.authFreshness, valueColor: selected.authFreshness === 'expired' ? C.bad : selected.authFreshness === 'expiring' || selected.authFreshness === 'pending' ? C.warn : C.good },
128
+ ], C),
129
+ buildGuidanceLine(width, '/accounts repair <provider>', 'review routing safety, fallback cost, and provider-specific recovery steps', C),
130
+ ];
131
+ const detailRows: Line[] = [
132
+ buildKeyValueLine(width, [
133
+ { label: 'provider', value: selected.providerId, valueColor: C.value },
134
+ { label: 'active route', value: selected.activeRoute, valueColor: selected.activeRoute === 'subscription' ? C.info : selected.activeRoute === 'api-key' ? C.warn : selected.activeRoute === 'service-oauth' ? C.value : C.bad },
135
+ { label: 'preferred route', value: selected.preferredRoute, valueColor: C.dim },
136
+ { label: 'freshness', value: selected.authFreshness, valueColor: selected.authFreshness === 'expired' ? C.bad : selected.authFreshness === 'expiring' || selected.authFreshness === 'pending' ? C.warn : C.good },
137
+ ], C),
138
+ buildKeyValueLine(width, [
139
+ { label: 'configured', value: selected.configured ? 'yes' : 'no', valueColor: selected.configured ? C.good : C.bad },
140
+ { label: 'oauth ready', value: selected.oauthReady ? 'yes' : 'no', valueColor: selected.oauthReady ? C.info : C.dim },
141
+ { label: 'pending login', value: selected.pendingLogin ? 'yes' : 'no', valueColor: selected.pendingLogin ? C.warn : C.dim },
142
+ ], C),
143
+ buildPanelLine(width, [[` Active route reason: ${selected.activeRouteReason}`.slice(0, width), C.dim]]),
144
+ buildPanelLine(width, [[` Available routes: ${selected.availableRoutes.join(', ') || 'unconfigured'}`.slice(0, width), C.dim]]),
145
+ ];
146
+ if (selected.expiresAt) {
147
+ detailRows.push(buildPanelLine(width, [
148
+ [' Expires: ', C.label],
149
+ [new Date(selected.expiresAt).toISOString(), C.dim],
150
+ [' Token: ', C.label],
151
+ [selected.tokenType ?? 'n/a', C.value],
152
+ ]));
153
+ }
154
+ if (selected.fallbackRisk) {
155
+ detailRows.push(buildPanelLine(width, [[` fallback: ${selected.fallbackRisk}`.slice(0, width), C.warn]]));
156
+ }
157
+ for (const route of selected.routeRecords) {
158
+ detailRows.push(buildPanelLine(width, [[
159
+ ` route ${route.route}: ${route.usable ? 'usable' : 'blocked'} • ${route.freshness} • ${route.detail}`.slice(0, width),
160
+ route.usable ? C.dim : C.bad,
161
+ ]]));
162
+ for (const issue of route.issues) {
163
+ detailRows.push(buildPanelLine(width, [[` issue: ${issue}`.slice(0, width), C.bad]]));
164
+ }
165
+ }
166
+ for (const windowHint of selected.usageWindows) {
167
+ detailRows.push(buildPanelLine(width, [[` ${windowHint.label}: ${windowHint.detail}`.slice(0, width), C.dim]]));
168
+ }
169
+ for (const issue of selected.issues) {
170
+ detailRows.push(buildPanelLine(width, [[` issue: ${issue}`.slice(0, width), C.bad]]));
171
+ }
172
+ for (const note of selected.notes) {
173
+ detailRows.push(buildPanelLine(width, [[` note: ${note}`.slice(0, width), C.info]]));
174
+ }
175
+ for (const action of selected.recommendedActions) {
176
+ detailRows.push(buildPanelLine(width, [[` next: ${action}`.slice(0, width), C.value]]));
177
+ }
178
+ if (selected.issues.length === 0 && selected.notes.length === 0 && selected.usageWindows.length === 0 && selected.recommendedActions.length === 0) {
179
+ detailRows.push(buildPanelLine(width, [[' No active account warnings for this provider.', C.dim]]));
180
+ }
181
+ const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'Provider posture', postureLines, C) };
182
+ const detailsSection: PanelWorkspaceSection = { lines: buildDetailBlock(width, 'Selected provider', detailRows, C) };
183
+ const rawProviderLines: Line[] = this.records.map((record, absolute) =>
184
+ this.renderItem(record, absolute, absolute === this.selectedIndex, width),
185
+ );
186
+ const resolvedProvidersSection = resolvePrimaryScrollableSection(width, height, {
187
+ intro,
188
+ footerLines,
189
+ palette: C,
190
+ beforeSections: [postureSection],
191
+ section: {
192
+ title: 'Providers',
193
+ scrollableLines: rawProviderLines,
194
+ selectedIndex: this.selectedIndex,
195
+ scrollOffset: this.scrollStart,
196
+ guardRows: 1,
197
+ minRows: 4,
198
+ appendWindowSummary: { dimColor: C.dim },
199
+ },
200
+ afterSections: [detailsSection],
201
+ });
202
+ this.scrollStart = resolvedProvidersSection.scrollOffset;
203
+ const sections: PanelWorkspaceSection[] = [
204
+ postureSection,
205
+ resolvedProvidersSection.section,
206
+ detailsSection,
207
+ ];
208
+ const lines = buildPanelWorkspace(width, height, {
209
+ title: 'Provider Account Control Room',
210
+ intro,
211
+ sections,
212
+ footerLines,
213
+ palette: C,
214
+ });
215
+ while (lines.length < height) lines.push(createEmptyLine(width));
216
+ return lines.slice(0, height);
217
+ }
218
+ }