ompchamber 2.0.2 → 3.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.
- package/README.md +31 -11
- package/bunfig.toml +11 -0
- package/dist/client/index.html +16 -5
- package/dist/client/index.js +1393 -0
- package/dist/client/static/js/index-8t70jhyj.js +5149 -0
- package/dist/client/static/js/index-n4wkqj7t.css +1 -0
- package/index.html +54 -0
- package/package.json +9 -10
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192x192.png +0 -0
- package/public/icon.svg +60 -0
- package/public/manifest.webmanifest +38 -0
- package/src/cli/lib/commands/serve.js +9 -5
- package/src/cli/lib/runtime.js +18 -7
- package/src/client/components/common/code-editor/handler.test.ts +274 -0
- package/src/client/components/common/code-editor/handler.ts +175 -0
- package/src/client/components/common/code-editor/index.tsx +185 -220
- package/src/client/components/common/code-surface/index.tsx +109 -12
- package/src/client/components/common/lazy-panels.tsx +5 -1
- package/src/client/components/common/provider-icon/glyphs.ts +118 -0
- package/src/client/components/common/provider-icon/index.tsx +85 -0
- package/src/client/components/common/viewport.ts +5 -3
- package/src/client/components/layout/RightActivityBar.tsx +2 -1
- package/src/client/components/layout/desktop-layout/TopNavbar.tsx +21 -4
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +4 -0
- package/src/client/components/layout/desktop-layout/index.tsx +26 -4
- package/src/client/components/mobile/LayoutWrapper.tsx +16 -0
- package/src/client/components/mobile/RightSidebar.tsx +11 -4
- package/src/client/components/mobile/SessionSidebar.tsx +10 -0
- package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +0 -1
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -1
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +3 -0
- package/src/client/components/settings/categories/ProviderSettings.tsx +45 -4
- package/src/client/components/settings/categories/UsageSettings.tsx +22 -7
- package/src/client/components/settings/categories/provider-settings/AddModelModal.tsx +322 -0
- package/src/client/components/settings/categories/provider-settings/AddProviderModal.tsx +152 -56
- package/src/client/components/settings/categories/provider-settings/AuthSection.tsx +52 -1
- package/src/client/components/settings/categories/provider-settings/DeleteProviderModal.tsx +133 -0
- package/src/client/components/settings/categories/provider-settings/DialectFields.tsx +136 -0
- package/src/client/components/settings/categories/provider-settings/EffortLadder.tsx +89 -0
- package/src/client/components/settings/categories/provider-settings/Header.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/LoginModal.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/ModelsList.tsx +22 -2
- package/src/client/components/settings/categories/provider-settings/PresetPicker.tsx +82 -0
- package/src/client/components/settings/categories/provider-settings/PriceFields.tsx +107 -0
- package/src/client/components/settings/categories/provider-settings/ReconnectModal.tsx +43 -22
- package/src/client/components/settings/categories/provider-settings/SidebarList.tsx +2 -2
- package/src/client/components/settings/categories/usage-settings/LimitsSection.tsx +115 -0
- package/src/client/components/settings/categories/usage-settings/ProviderCard.tsx +19 -26
- package/src/client/components/settings/categories/usage-settings/ProviderDetail.tsx +29 -22
- package/src/client/components/settings/categories/usage-settings/SidebarList.tsx +60 -48
- package/src/client/components/settings/categories/usage-settings/format.ts +36 -0
- package/src/client/components/settings/categories/usage-settings/providers.ts +52 -28
- package/src/client/components/workspace/browser-panel/AddressBar.tsx +1 -10
- package/src/client/components/workspace/browser-panel/Screencast.tsx +4 -5
- package/src/client/components/workspace/browser-panel/index.tsx +6 -6
- package/src/client/components/workspace/browser-toolbar/index.tsx +22 -12
- package/src/client/components/workspace/chat-timeline/AiMessageFooter.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/GeneratingIndicator.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Todo.tsx +1 -1
- package/src/client/components/workspace/diff-panel/Toolbar.tsx +71 -38
- package/src/client/components/workspace/diff-panel/index.tsx +139 -53
- package/src/client/components/workspace/editor/CommandPalette.test.ts +242 -0
- package/src/client/components/workspace/editor/CommandPalette.tsx +138 -0
- package/src/client/components/workspace/editor/FindWidget.test.ts +261 -0
- package/src/client/components/workspace/editor/FindWidget.tsx +219 -0
- package/src/client/components/workspace/editor/Tabs.tsx +2 -2
- package/src/client/components/workspace/editor/Toolbar.tsx +129 -26
- package/src/client/components/workspace/editor/index.tsx +180 -62
- package/src/client/components/workspace/file-explorer/Modals.tsx +79 -2
- package/src/client/components/workspace/file-explorer/TreeItem.tsx +43 -113
- package/src/client/components/workspace/model-dropdown/Item.tsx +13 -23
- package/src/client/components/workspace/model-dropdown/Panel.tsx +1 -1
- package/src/client/components/workspace/model-dropdown/Section.tsx +13 -4
- package/src/client/components/workspace/model-dropdown/index.tsx +7 -3
- package/src/client/components/workspace/todo-panel/PhaseGroup.tsx +76 -0
- package/src/client/components/workspace/todo-panel/Row.tsx +69 -0
- package/src/client/components/workspace/todo-panel/index.tsx +161 -0
- package/src/client/components/workspace/usage-panel/ProviderSelect.tsx +10 -5
- package/src/client/components/workspace/usage-panel/index.tsx +36 -18
- package/src/client/data/samples/dialogue-session.ts +1 -1
- package/src/client/data/samples/sample-tools/other.ts +1 -1
- package/src/client/data/settings/provider.ts +141 -9
- package/src/client/hooks/editor/find-harness.test-util.ts +170 -0
- package/src/client/hooks/editor/scroll-container.test.ts +102 -0
- package/src/client/hooks/editor/scroll-container.ts +26 -0
- package/src/client/hooks/editor/use-code-window.ts +2 -20
- package/src/client/hooks/editor/use-editor-find-write.test.ts +170 -0
- package/src/client/hooks/editor/use-editor-find.test.ts +118 -0
- package/src/client/hooks/editor/use-editor-find.ts +321 -0
- package/src/client/hooks/editor/use-editor-history.test.ts +216 -0
- package/src/client/hooks/editor/use-editor-history.ts +134 -0
- package/src/client/hooks/editor/use-file-editor.ts +24 -27
- package/src/client/hooks/editor/use-occurrences.test.ts +236 -0
- package/src/client/hooks/editor/use-occurrences.ts +98 -0
- package/src/client/hooks/models/use-provider-names.ts +1 -1
- package/src/client/hooks/settings/providers/actions.ts +337 -0
- package/src/client/hooks/settings/providers/api.ts +69 -0
- package/src/client/hooks/settings/providers/index.ts +138 -0
- package/src/client/hooks/settings/providers/messages.ts +77 -0
- package/src/client/hooks/settings/useUsageReport.ts +8 -3
- package/src/client/hooks/ui/theme.ts +3 -3
- package/src/client/hooks/workspace/file-tabs.ts +62 -11
- package/src/client/hooks/workspace/file-tree-actions.test.ts +348 -0
- package/src/client/hooks/workspace/file-tree-actions.ts +250 -0
- package/src/client/hooks/workspace/session-todos.ts +102 -0
- package/src/client/stubs/elkjs.ts +6 -1
- package/src/client/tailwind.css +30 -0
- package/src/server/index.ts +20 -2
- package/src/server/lib/assets/font-css.server.ts +86 -0
- package/src/server/lib/assets/fonts.server.ts +117 -0
- package/src/server/lib/bundler/css.ts +116 -0
- package/src/server/lib/lifecycle/shell-route.ts +17 -0
- package/src/server/lib/models/provider-listing.server.ts +257 -0
- package/src/server/lib/models/provider-registry.server.test.ts +122 -0
- package/src/server/lib/models/provider-registry.server.ts +76 -25
- package/src/server/lib/omp/config/models-config.ts +11 -0
- package/src/server/lib/omp/config/models-validation.test.ts +137 -0
- package/src/server/lib/omp/config/models-validation.ts +111 -2
- package/src/server/lib/omp/config/provider-seeds.test.ts +167 -0
- package/src/server/lib/omp/config/provider-seeds.ts +176 -0
- package/src/server/lib/omp/config/providers.ts +94 -100
- package/src/server/lib/omp/core/auth-credentials.ts +45 -0
- package/src/server/lib/omp/session/todos.ts +100 -0
- package/src/server/lib/omp/session/{usage.ts → usage/aggregate.ts} +1 -118
- package/src/server/lib/omp/session/usage/shaping.ts +130 -0
- package/src/server/lib/usage/deepseek.ts +4 -5
- package/src/server/lib/usage/kenari.ts +7 -6
- package/src/server/lib/usage/omp-usage.server.test.ts +126 -0
- package/src/server/lib/usage/omp-usage.server.ts +194 -0
- package/src/server/lib/usage/provider-key.ts +81 -3
- package/src/server/lib/usage/providers.server.test.ts +47 -0
- package/src/server/lib/usage/providers.server.ts +208 -0
- package/src/server/lib/usage/vendor-balance.server.test.ts +104 -0
- package/src/server/lib/usage/vendor-balance.server.ts +284 -0
- package/src/server/plugins/shell.server.ts +56 -0
- package/src/server/plugins/ssr.ts +24 -36
- package/src/server/plugins/static.test.ts +26 -29
- package/src/server/plugins/static.ts +40 -19
- package/src/server/routes/fs/action-create.test.ts +90 -0
- package/src/server/routes/fs/action.ts +23 -0
- package/src/server/routes/omp/index.ts +2 -0
- package/src/server/routes/omp/session-todos.ts +80 -0
- package/src/server/routes/settings/index.ts +2 -0
- package/src/server/routes/settings/provider-model.ts +107 -0
- package/src/server/routes/settings/provider-models.ts +65 -202
- package/src/server/routes/settings/providers.ts +22 -7
- package/src/server/routes/settings/usage.ts +125 -45
- package/src/server/routes/telemetry/tokens.ts +2 -1
- package/src/shared/lib/chat/{todo-parser.ts → todo/parser.ts} +17 -0
- package/src/shared/lib/chat/todo/progress.ts +97 -0
- package/src/shared/lib/chat/todo/snapshot.test.ts +156 -0
- package/src/shared/lib/chat/todo/snapshot.ts +215 -0
- package/src/shared/lib/code/editor/commands.test.ts +155 -0
- package/src/shared/lib/code/editor/commands.ts +260 -0
- package/src/shared/lib/code/editor/edits.test.ts +100 -0
- package/src/shared/lib/code/editor/edits.ts +122 -0
- package/src/shared/lib/code/editor/find-status.test.ts +50 -0
- package/src/shared/lib/code/editor/find-status.ts +36 -0
- package/src/shared/lib/code/editor/find.test.ts +203 -0
- package/src/shared/lib/code/editor/find.ts +224 -0
- package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.test.ts +1 -1
- package/src/shared/lib/code/editor/keymap.test.ts +109 -0
- package/src/shared/lib/code/editor/keymap.ts +151 -0
- package/src/shared/lib/code/editor/layers.test.ts +77 -0
- package/src/shared/lib/code/editor/layers.ts +67 -0
- package/src/shared/lib/code/editor/line-edits.test.ts +131 -0
- package/src/shared/lib/code/editor/line-edits.ts +159 -0
- package/src/shared/lib/code/editor/lines.test.ts +81 -0
- package/src/shared/lib/code/editor/lines.ts +83 -0
- package/src/shared/lib/code/editor/marks.test.ts +86 -0
- package/src/shared/lib/code/editor/marks.ts +106 -0
- package/src/shared/lib/code/editor/occurrences.test.ts +117 -0
- package/src/shared/lib/code/editor/occurrences.ts +111 -0
- package/src/shared/lib/code/editor/palette.test.ts +91 -0
- package/src/shared/lib/code/editor/palette.ts +92 -0
- package/src/shared/lib/code/editor/reset-css.test.ts +35 -0
- package/src/shared/lib/code/editor/reset-css.ts +32 -0
- package/src/shared/lib/code/editor/text-commands.test.ts +178 -0
- package/src/shared/lib/code/editor/text-commands.ts +154 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +75 -0
- package/src/shared/lib/code/syntax-highlight.ts +29 -17
- package/src/shared/lib/code/windowed-highlight.ts +52 -1
- package/src/shared/lib/fs/diff-parser.test.ts +116 -0
- package/src/shared/lib/fs/diff-parser.ts +86 -33
- package/src/shared/lib/models/{provider-connection.ts → provider/connection.ts} +37 -0
- package/src/shared/lib/models/provider/dialect.ts +179 -0
- package/src/shared/lib/models/provider/glyph.test.ts +116 -0
- package/src/shared/lib/models/provider/glyph.ts +200 -0
- package/src/shared/lib/models/{provider-models.ts → provider/models.ts} +91 -6
- package/src/shared/lib/models/provider/presets.test.ts +172 -0
- package/src/shared/lib/models/provider/presets.ts +83 -0
- package/src/shared/lib/ui/key-binding.test.ts +115 -0
- package/src/shared/lib/ui/key-binding.ts +143 -0
- package/src/shared/lib/util/platform.test.ts +87 -0
- package/src/shared/lib/util/platform.ts +30 -0
- package/src/shared/lib/workspace/file-tab-id.ts +29 -0
- package/src/shared/lib/workspace/keymap.test.ts +82 -0
- package/src/shared/lib/workspace/keymap.ts +30 -0
- package/src/shared/lib/workspace/refresh-cadence.ts +9 -0
- package/src/shared/lib/workspace/right-panels.ts +8 -0
- package/src/shared/lib/workspace/session-state/store.ts +0 -1
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/settings/provider.ts +81 -0
- package/src/shared/types/settings/usage.ts +88 -14
- package/src/shared/types/todo.ts +108 -0
- package/tsconfig.json +3 -1
- package/dist/client/static/css/index.5f630421b7.css +0 -2
- package/dist/client/static/font/KaTeX_AMS-Regular.a79f1c3119.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Bold.ec17d13264.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Regular.55fac25845.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Bold.d42a5579b0.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Regular.d3c882a649.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Bold.c3fb5ac22f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-BoldItalic.6f2bb1dff2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Italic.8916142bec.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Regular.0462f03bdf.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-BoldItalic.572d331f69.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-Italic.f28c23acad.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Bold.8c5b5494b6.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Italic.3b1e59b3ba.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Regular.ba21ed5f84.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Script-Regular.03e9641d6f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size1-Regular.eae34984b3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size2-Regular.5916a24fa3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size4-Regular.10d95fd3a2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Typewriter-Regular.a8709e3622.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-400-normal.2655b10c55.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-500-normal.92a991f204.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-600-normal.43788eb7f1.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-700-normal.2667215d33.woff2 +0 -0
- package/dist/client/static/js/77.0d81c74cf0.js +0 -139
- package/dist/client/static/js/77.0d81c74cf0.js.LICENSE.txt +0 -8
- package/dist/client/static/js/async/0.8e59f2eb5b.js +0 -1
- package/dist/client/static/js/async/0k.5852e6b4fa.js +0 -1
- package/dist/client/static/js/async/0w.9ed36d5860.js +0 -1
- package/dist/client/static/js/async/1.f387c12b63.js +0 -1
- package/dist/client/static/js/async/12.d091f91c99.js +0 -1
- package/dist/client/static/js/async/13.8f810a8a6c.js +0 -1
- package/dist/client/static/js/async/19.6fe909d55a.js +0 -160
- package/dist/client/static/js/async/1b.23f1141a38.js +0 -1
- package/dist/client/static/js/async/1k.973e8e21fc.js +0 -2
- package/dist/client/static/js/async/1k.973e8e21fc.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/1p.0d5cddda96.js +0 -1
- package/dist/client/static/js/async/2.b589f1733b.js +0 -1
- package/dist/client/static/js/async/2v.96165101de.js +0 -1
- package/dist/client/static/js/async/3.0cacc08423.js +0 -1
- package/dist/client/static/js/async/37.b28b2f61cd.js +0 -1
- package/dist/client/static/js/async/3d.5085fb913f.js +0 -1
- package/dist/client/static/js/async/3j.357c80ff38.js +0 -2
- package/dist/client/static/js/async/3j.357c80ff38.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/3p.bcf32a0983.js +0 -1
- package/dist/client/static/js/async/3r.3a69e98ee0.js +0 -64
- package/dist/client/static/js/async/3u.1228460e2a.js +0 -1
- package/dist/client/static/js/async/3y.3530b824ad.js +0 -198
- package/dist/client/static/js/async/3y.3530b824ad.js.LICENSE.txt +0 -5
- package/dist/client/static/js/async/4.9442eeb472.js +0 -1
- package/dist/client/static/js/async/4e.678285ca55.js +0 -1
- package/dist/client/static/js/async/4k.33a0c7eeb4.js +0 -1
- package/dist/client/static/js/async/4m.928a1dde85.js +0 -31
- package/dist/client/static/js/async/4m.928a1dde85.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/4q.aad20dab25.js +0 -1
- package/dist/client/static/js/async/5.ebaf8b89cc.js +0 -36
- package/dist/client/static/js/async/51.95bcae98af.js +0 -2
- package/dist/client/static/js/async/51.95bcae98af.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
- package/dist/client/static/js/async/5j.d6407c4248.js +0 -287
- package/dist/client/static/js/async/5o.88316d4460.js +0 -62
- package/dist/client/static/js/async/6.f034c9b528.js +0 -1
- package/dist/client/static/js/async/66.5dfa29f957.js +0 -93
- package/dist/client/static/js/async/6o.5b1a06a382.js +0 -1
- package/dist/client/static/js/async/7.e4a9288dd0.js +0 -1
- package/dist/client/static/js/async/73.308dff4da8.js +0 -1
- package/dist/client/static/js/async/7e.a1cc9bfd88.js +0 -1
- package/dist/client/static/js/async/7g.a7814350c3.js +0 -1
- package/dist/client/static/js/async/7l.fadbbc0583.js +0 -1
- package/dist/client/static/js/async/8.5067439d8c.js +0 -1
- package/dist/client/static/js/async/8j.ea110663fb.js +0 -1
- package/dist/client/static/js/async/a.bce4542392.js +0 -1
- package/dist/client/static/js/async/aa.65d3852576.js +0 -1
- package/dist/client/static/js/async/ar.9303e5f2e8.js +0 -2
- package/dist/client/static/js/async/ar.9303e5f2e8.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/az.d656062b0f.js +0 -1
- package/dist/client/static/js/async/b.b4f6f06515.js +0 -1
- package/dist/client/static/js/async/bi.09688ba75c.js +0 -1
- package/dist/client/static/js/async/bq.586bc738d1.js +0 -1
- package/dist/client/static/js/async/c.797d933882.js +0 -1
- package/dist/client/static/js/async/c0.0af1e640d3.js +0 -24
- package/dist/client/static/js/async/ca.8a52cfd447.js +0 -1
- package/dist/client/static/js/async/cf.d34fff5d67.js +0 -1
- package/dist/client/static/js/async/ct.b74928f7a3.js +0 -1
- package/dist/client/static/js/async/cx.3cb0242a3a.js +0 -226
- package/dist/client/static/js/async/d.a5fc8fe1b3.js +0 -1
- package/dist/client/static/js/async/da.f62c70f6c8.js +0 -1
- package/dist/client/static/js/async/dp.1725d6432c.js +0 -1
- package/dist/client/static/js/async/e.93214d622b.js +0 -79
- package/dist/client/static/js/async/f.ab50467719.js +0 -19
- package/dist/client/static/js/async/f.ab50467719.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/f7.46971e9d6d.js +0 -2
- package/dist/client/static/js/async/f7.46971e9d6d.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/f8.bbf0dbae70.js +0 -1
- package/dist/client/static/js/async/g.cb81980498.js +0 -2
- package/dist/client/static/js/async/g.cb81980498.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/h.4e790471e4.js +0 -27
- package/dist/client/static/js/async/h1.393f066d09.js +0 -1
- package/dist/client/static/js/async/h3.216e9798ab.js +0 -148
- package/dist/client/static/js/async/hd.0da931b622.js +0 -1
- package/dist/client/static/js/async/hk.ba7fb04d41.js +0 -1
- package/dist/client/static/js/async/hn.b645041e00.js +0 -1
- package/dist/client/static/js/async/ho.475a5e1ae4.js +0 -1
- package/dist/client/static/js/async/hp.e9fe2e395a.js +0 -2
- package/dist/client/static/js/async/hp.e9fe2e395a.js.LICENSE.txt +0 -17
- package/dist/client/static/js/async/hs.e7c59d8090.js +0 -41
- package/dist/client/static/js/async/i.7c148e8440.js +0 -1
- package/dist/client/static/js/async/id.573df20f29.js +0 -1
- package/dist/client/static/js/async/j.ab925882d8.js +0 -1
- package/dist/client/static/js/async/jd.63886a1f09.js +0 -1
- package/dist/client/static/js/async/jg.d86ebd4013.js +0 -86
- package/dist/client/static/js/async/jl.874d352278.js +0 -1
- package/dist/client/static/js/async/jll.11313b455a.js +0 -1
- package/dist/client/static/js/async/js.67182759ef.js +0 -1
- package/dist/client/static/js/async/jsc.23b70d6f40.js +0 -1
- package/dist/client/static/js/async/jse.334588ff5d.js +0 -1
- package/dist/client/static/js/async/jz.72eb929106.js +0 -77
- package/dist/client/static/js/async/k.3210286401.js +0 -217
- package/dist/client/static/js/async/ka.c1990ecaa3.js +0 -115
- package/dist/client/static/js/async/katex-vendor.de6a87c308.js +0 -1
- package/dist/client/static/js/async/ku.29c8a59d0c.js +0 -1
- package/dist/client/static/js/async/ky.8dab1dade0.js +0 -1
- package/dist/client/static/js/async/l.12dd34e1be.js +0 -1
- package/dist/client/static/js/async/l9.949710adaf.js +0 -83
- package/dist/client/static/js/async/l9.949710adaf.js.LICENSE.txt +0 -13
- package/dist/client/static/js/async/ls.9d9a01026c.js +0 -104
- package/dist/client/static/js/async/m.eb2e6752e2.js +0 -28
- package/dist/client/static/js/async/m.eb2e6752e2.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/m3.d2b05f9604.js +0 -2
- package/dist/client/static/js/async/m3.d2b05f9604.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/ma.2c261b04dd.js +0 -2
- package/dist/client/static/js/async/ma.2c261b04dd.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/me.8ace414f80.js +0 -23
- package/dist/client/static/js/async/ms.98960af4eb.js +0 -67
- package/dist/client/static/js/async/ms.98960af4eb.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/mt.0352e976d8.js +0 -1
- package/dist/client/static/js/async/n.a9f1c51701.js +0 -1
- package/dist/client/static/js/async/na.a36cd532d6.js +0 -1
- package/dist/client/static/js/async/nk.fc7e58325e.js +0 -1
- package/dist/client/static/js/async/o.36353dfb88.js +0 -1
- package/dist/client/static/js/async/oc.b73042e8bd.js +0 -8
- package/dist/client/static/js/async/og.8eb577893a.js +0 -193
- package/dist/client/static/js/async/p.48ecfcd14e.js +0 -1
- package/dist/client/static/js/async/px.fc6877924b.js +0 -1
- package/dist/client/static/js/async/q.22588845a9.js +0 -21
- package/dist/client/static/js/async/q.22588845a9.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/q5.97dc1481b6.js +0 -286
- package/dist/client/static/js/async/qs.2a9ddb3c72.js +0 -1
- package/dist/client/static/js/async/r.51010ad87c.js +0 -1
- package/dist/client/static/js/async/rd.cb6bf495bb.js +0 -2
- package/dist/client/static/js/async/rd.cb6bf495bb.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/s.0f395cdda5.js +0 -94
- package/dist/client/static/js/async/t.4dca0e2ba8.js +0 -1
- package/dist/client/static/js/async/t9.d44f57d518.js +0 -1
- package/dist/client/static/js/async/ta.973534bbec.js +0 -1
- package/dist/client/static/js/async/tr.e878658140.js +0 -309
- package/dist/client/static/js/async/u.0871ac5b22.js +0 -1
- package/dist/client/static/js/async/ud.3065bfee31.js +0 -2
- package/dist/client/static/js/async/ud.3065bfee31.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/up.ea990da0ff.js +0 -32
- package/dist/client/static/js/async/uw.8890abbabc.js +0 -1
- package/dist/client/static/js/async/v.7d28d3c241.js +0 -34
- package/dist/client/static/js/async/vp.9b13bf8610.js +0 -1
- package/dist/client/static/js/async/vt.42416b707d.js +0 -1
- package/dist/client/static/js/async/w.08ac210f60.js +0 -1
- package/dist/client/static/js/async/wt.ad92393a90.js +0 -1
- package/dist/client/static/js/async/x.563f72ca6b.js +0 -1
- package/dist/client/static/js/async/x0.475516418f.js +0 -1
- package/dist/client/static/js/async/x1.da8687159b.js +0 -90
- package/dist/client/static/js/async/xe.da8dcc2015.js +0 -2
- package/dist/client/static/js/async/xe.da8dcc2015.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xl.a3c562a58c.js +0 -1
- package/dist/client/static/js/async/xn.6510c0abe2.js +0 -2
- package/dist/client/static/js/async/xn.6510c0abe2.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/xn1.83a7784fe1.js +0 -10
- package/dist/client/static/js/async/xn1.83a7784fe1.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xnl.feb0021e50.js +0 -2
- package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/y.1a9ed7a5d8.js +0 -1
- package/dist/client/static/js/async/y6.43c57defe4.js +0 -1
- package/dist/client/static/js/async/yb.8c1342401d.js +0 -1
- package/dist/client/static/js/async/ybq.4491f2f2dc.js +0 -1
- package/dist/client/static/js/async/yg.35bebd6d0c.js +0 -149
- package/dist/client/static/js/async/yq.917ddd7ba2.js +0 -1
- package/dist/client/static/js/async/ys.00687cf82d.js +0 -38
- package/dist/client/static/js/async/yv.d99d829136.js +0 -1
- package/dist/client/static/js/async/z.56f61a4374.js +0 -1
- package/dist/client/static/js/async/zi.5fdb07b86a.js +0 -24
- package/dist/client/static/js/async/zj.654f37e8ff.js +0 -1
- package/dist/client/static/js/async/zn.966d6f1cc6.js +0 -1
- package/dist/client/static/js/async/zo.26213b40c0.js +0 -1
- package/dist/client/static/js/async/zx.e9f4105e00.js +0 -1
- package/dist/client/static/js/index.e6a257402a.js +0 -38
- package/dist/client/static/js/index.e6a257402a.js.LICENSE.txt +0 -4
- package/dist/client/static/js/markdown-vendor.71e43278b3.js +0 -144
- package/dist/client/static/js/shiki-vendor.cb8452ddcd.js +0 -1
- package/src/client/components/settings/categories/provider-settings/Icons.tsx +0 -35
- package/src/client/hooks/settings/providers.ts +0 -326
- package/src/server/plugins/dev-assets.ts +0 -49
- package/src/shared/lib/code/editor-utils.ts +0 -16
- package/src/shared/lib/models/provider-presets.ts +0 -29
- /package/dist/client/{apple-touch-icon.png → static/apple-touch-icon-dskx5sr3.png} +0 -0
- /package/dist/client/{icon-192x192.png → static/icon-192x192-6gn5cr2f.png} +0 -0
- /package/dist/client/{icon.svg → static/icon-d07z9dtz.svg} +0 -0
- /package/dist/client/{manifest.webmanifest → static/manifest-bnyy75kt.webmanifest} +0 -0
- /package/{dist/client → public}/icon-512x512.png +0 -0
- /package/{dist/client → public}/icon-maskable-512x512.png +0 -0
- /package/{dist/client → public}/sw.js +0 -0
- /package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.ts +0 -0
- /package/src/shared/lib/models/{provider-cleanup.ts → provider/cleanup.ts} +0 -0
- /package/src/shared/lib/models/{provider-label.ts → provider/label.ts} +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DollarSign } from 'lucide-preact';
|
|
2
|
+
|
|
3
|
+
interface PriceFieldsProps {
|
|
4
|
+
costInput: string;
|
|
5
|
+
costOutput: string;
|
|
6
|
+
costCacheRead: string;
|
|
7
|
+
costCacheWrite: string;
|
|
8
|
+
onCostInputChange: (value: string) => void;
|
|
9
|
+
onCostOutputChange: (value: string) => void;
|
|
10
|
+
onCostCacheReadChange: (value: string) => void;
|
|
11
|
+
onCostCacheWriteChange: (value: string) => void;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** One labelled price box. `hint` distinguishes the four rates. */
|
|
16
|
+
function PriceField({
|
|
17
|
+
value,
|
|
18
|
+
onChange,
|
|
19
|
+
label,
|
|
20
|
+
hint,
|
|
21
|
+
}: {
|
|
22
|
+
value: string;
|
|
23
|
+
onChange: (value: string) => void;
|
|
24
|
+
label: string;
|
|
25
|
+
hint: string;
|
|
26
|
+
}) {
|
|
27
|
+
return (
|
|
28
|
+
<div>
|
|
29
|
+
<label className="block text-[11px] font-medium text-ink/70 mb-1" title={hint}>
|
|
30
|
+
{label}
|
|
31
|
+
</label>
|
|
32
|
+
<div className="relative flex items-center">
|
|
33
|
+
<DollarSign size={12} className="absolute left-2.5 text-ink/40 pointer-events-none" />
|
|
34
|
+
<input
|
|
35
|
+
type="text"
|
|
36
|
+
inputMode="decimal"
|
|
37
|
+
value={value}
|
|
38
|
+
onChange={(e) => onChange(e.currentTarget.value)}
|
|
39
|
+
placeholder="0"
|
|
40
|
+
className="w-full bg-paper border border-ink/20 rounded-md pl-7 pr-2 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The four `models.yml` cost rates, in USD per 1M tokens.
|
|
49
|
+
*
|
|
50
|
+
* omp requires all four fields together, so input/output are the pair that
|
|
51
|
+
* decides whether a price is written at all; the two cache rates then default
|
|
52
|
+
* to 0, which is the honest value for "no cached-token discount". Cache rates
|
|
53
|
+
* matter for accounting rather than routing: without them every cached turn is
|
|
54
|
+
* billed at the full input rate, which is why they are worth asking for
|
|
55
|
+
* separately instead of deriving from input.
|
|
56
|
+
*/
|
|
57
|
+
export function PriceFields({
|
|
58
|
+
costInput,
|
|
59
|
+
costOutput,
|
|
60
|
+
costCacheRead,
|
|
61
|
+
costCacheWrite,
|
|
62
|
+
onCostInputChange,
|
|
63
|
+
onCostOutputChange,
|
|
64
|
+
onCostCacheReadChange,
|
|
65
|
+
onCostCacheWriteChange,
|
|
66
|
+
error,
|
|
67
|
+
}: PriceFieldsProps) {
|
|
68
|
+
return (
|
|
69
|
+
<div>
|
|
70
|
+
<label className="block text-xs font-semibold text-ink mb-1.5">
|
|
71
|
+
Price <span className="font-normal text-ink/40">— USD per 1M tokens, optional</span>
|
|
72
|
+
</label>
|
|
73
|
+
<div className="grid grid-cols-2 gap-3">
|
|
74
|
+
<PriceField
|
|
75
|
+
value={costInput}
|
|
76
|
+
onChange={onCostInputChange}
|
|
77
|
+
label="Input"
|
|
78
|
+
hint="Uncached prompt tokens"
|
|
79
|
+
/>
|
|
80
|
+
<PriceField
|
|
81
|
+
value={costOutput}
|
|
82
|
+
onChange={onCostOutputChange}
|
|
83
|
+
label="Output"
|
|
84
|
+
hint="Generated tokens"
|
|
85
|
+
/>
|
|
86
|
+
<PriceField
|
|
87
|
+
value={costCacheRead}
|
|
88
|
+
onChange={onCostCacheReadChange}
|
|
89
|
+
label="Cache read"
|
|
90
|
+
hint="Prompt tokens served from the provider's cache — usually a fraction of the input rate"
|
|
91
|
+
/>
|
|
92
|
+
<PriceField
|
|
93
|
+
value={costCacheWrite}
|
|
94
|
+
onChange={onCostCacheWriteChange}
|
|
95
|
+
label="Cache write"
|
|
96
|
+
hint="Prompt tokens written into the provider's cache"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
{error && <p className="text-[11px] text-error mt-1">{error}</p>}
|
|
100
|
+
<p className="text-[10px] text-ink/40 mt-1">
|
|
101
|
+
Used for omp's cost accounting. Input and output decide whether a price is
|
|
102
|
+
written at all; the cache rates fall back to 0, which bills cached turns at
|
|
103
|
+
the full input rate.
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useState } from 'preact/hooks';
|
|
2
2
|
import type { FormEvent } from 'preact/compat';
|
|
3
|
-
import { AlertCircle, CheckCircle2, DownloadCloud, Globe, Key, RefreshCw } from 'lucide-preact';
|
|
3
|
+
import { AlertCircle, CheckCircle2, DownloadCloud, Globe, Info, Key, RefreshCw } from 'lucide-preact';
|
|
4
4
|
import type { ProviderItem } from '@/shared/types';
|
|
5
|
-
import { fetchProviderModelsRemote } from '@/shared/lib/models/provider
|
|
6
|
-
import { ProviderIcon } from '@/client/components/
|
|
5
|
+
import { fetchProviderModelsRemote } from '@/shared/lib/models/provider/models';
|
|
6
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
7
7
|
import { Modal } from '@/client/components/common/Modal';
|
|
8
8
|
|
|
9
9
|
interface ReconnectModalProps {
|
|
@@ -23,6 +23,7 @@ export function ReconnectModal({
|
|
|
23
23
|
onReconnect,
|
|
24
24
|
onFetchModels,
|
|
25
25
|
}: ReconnectModalProps) {
|
|
26
|
+
const isKeyless = provider.auth === 'none';
|
|
26
27
|
const [apiKey, setApiKey] = useState(provider.apiKey || 'sk-••••••••••••••••••••••••');
|
|
27
28
|
const [baseUrl, setBaseUrl] = useState(provider.baseUrl || 'https://api.openai.com/v1');
|
|
28
29
|
const [isTesting, setIsTesting] = useState(false);
|
|
@@ -39,7 +40,11 @@ export function ReconnectModal({
|
|
|
39
40
|
const handleTestConnection = async () => {
|
|
40
41
|
setIsTesting(true);
|
|
41
42
|
setTestResult(null);
|
|
42
|
-
const result = await fetchProviderModelsRemote(
|
|
43
|
+
const result = await fetchProviderModelsRemote({
|
|
44
|
+
baseUrl,
|
|
45
|
+
...(draftCredentials.apiKey ? { apiKey: draftCredentials.apiKey } : {}),
|
|
46
|
+
...(provider.api ? { api: provider.api } : {}),
|
|
47
|
+
});
|
|
43
48
|
setIsTesting(false);
|
|
44
49
|
setTestResult(result.ok ? 'success' : 'failed');
|
|
45
50
|
};
|
|
@@ -47,7 +52,10 @@ export function ReconnectModal({
|
|
|
47
52
|
const handleSave = async (e: FormEvent) => {
|
|
48
53
|
e.preventDefault();
|
|
49
54
|
onReconnect({
|
|
50
|
-
|
|
55
|
+
// A keyless provider is configured by `auth: none`; storing the masked
|
|
56
|
+
// placeholder as its credential would show a key that authenticates
|
|
57
|
+
// nothing.
|
|
58
|
+
...(isKeyless ? {} : { apiKey }),
|
|
51
59
|
baseUrl,
|
|
52
60
|
status: 'connected',
|
|
53
61
|
});
|
|
@@ -60,7 +68,7 @@ export function ReconnectModal({
|
|
|
60
68
|
onClose={onClose}
|
|
61
69
|
header={
|
|
62
70
|
<div className="flex items-center gap-2.5">
|
|
63
|
-
<ProviderIcon icon={provider.icon} size={18} />
|
|
71
|
+
<ProviderIcon icon={provider.icon} slug={provider.slug} name={provider.name} size={18} />
|
|
64
72
|
<div>
|
|
65
73
|
<h3 className="text-sm font-semibold text-ink">
|
|
66
74
|
Authentication & Credentials
|
|
@@ -108,21 +116,33 @@ export function ReconnectModal({
|
|
|
108
116
|
</div>
|
|
109
117
|
</div>
|
|
110
118
|
|
|
111
|
-
{/* API Key
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
onChange={(e) => setApiKey(e.currentTarget.value)}
|
|
122
|
-
className="w-full bg-paper border border-ink/20 rounded-md pl-8 pr-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
123
|
-
/>
|
|
119
|
+
{/* API Key — omitted for a keyless provider, which omp configures by
|
|
120
|
+
`auth: none` and which has no credential to store. */}
|
|
121
|
+
{isKeyless ? (
|
|
122
|
+
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-ink/5 border border-ink/10 text-[11px] text-ink/60">
|
|
123
|
+
<Info size={13} className="mt-0.5 flex-shrink-0 text-ink/50" />
|
|
124
|
+
<span>
|
|
125
|
+
This provider is registered with <span className="font-mono">auth: none</span>, so omp
|
|
126
|
+
sends no credential. Set Auth to “API key” in the provider’s own entry if the endpoint
|
|
127
|
+
starts requiring one.
|
|
128
|
+
</span>
|
|
124
129
|
</div>
|
|
125
|
-
|
|
130
|
+
) : (
|
|
131
|
+
<div>
|
|
132
|
+
<label className="block text-xs font-semibold text-ink mb-1">
|
|
133
|
+
API Key / Auth Token
|
|
134
|
+
</label>
|
|
135
|
+
<div className="relative flex items-center">
|
|
136
|
+
<Key size={13} className="absolute left-2.5 text-ink/40 pointer-events-none" />
|
|
137
|
+
<input
|
|
138
|
+
type="text"
|
|
139
|
+
value={apiKey}
|
|
140
|
+
onChange={(e) => setApiKey(e.currentTarget.value)}
|
|
141
|
+
className="w-full bg-paper border border-ink/20 rounded-md pl-8 pr-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
126
146
|
|
|
127
147
|
{/* Test connection indicator */}
|
|
128
148
|
<div className="flex items-center justify-between pt-1">
|
|
@@ -154,8 +174,9 @@ export function ReconnectModal({
|
|
|
154
174
|
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-ink/5 border border-ink/10 text-[11px] text-ink/60">
|
|
155
175
|
<DownloadCloud size={13} className="mt-0.5 flex-shrink-0 text-ink/50" />
|
|
156
176
|
<span>
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
{isKeyless
|
|
178
|
+
? 'On save, models are fetched from the endpoint and merged into the list below — a keyless server needs none to answer.'
|
|
179
|
+
: 'On save, models are fetched from the endpoint and merged into the list below — existing models are kept, only new ones are added.'}
|
|
159
180
|
</span>
|
|
160
181
|
</div>
|
|
161
182
|
</Modal>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useRef, useState } from 'preact/hooks';
|
|
3
3
|
import { Check, ChevronDown, Folder, Plus } from 'lucide-preact';
|
|
4
4
|
import type { ProviderItem } from '@/shared/types';
|
|
5
|
-
import { ProviderIcon } from '@/client/components/
|
|
5
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
6
6
|
import { useOnClickOutside } from '@/client/hooks/ui/on-click-outside';
|
|
7
7
|
|
|
8
8
|
interface ProviderSidebarListProps {
|
|
@@ -115,7 +115,7 @@ export function ProviderSidebarList({
|
|
|
115
115
|
}`}
|
|
116
116
|
>
|
|
117
117
|
<div className="flex items-center gap-2.5 min-w-0">
|
|
118
|
-
<ProviderIcon icon={item.icon} size={15} className="flex-shrink-0" />
|
|
118
|
+
<ProviderIcon icon={item.icon} slug={item.slug} name={item.name} size={15} className="flex-shrink-0" />
|
|
119
119
|
<span className="text-[12px] truncate">{item.name}</span>
|
|
120
120
|
</div>
|
|
121
121
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { UsageLimitWindow } from '@/shared/types';
|
|
2
|
+
import { formatDateTime, formatFractionPercent, formatUsageAmount } from '@/client/components/settings/categories/usage-settings/format';
|
|
3
|
+
|
|
4
|
+
interface LimitRowProps {
|
|
5
|
+
limit: UsageLimitWindow;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** One provider-reported quota window: label, bar, used/remaining, reset. */
|
|
9
|
+
function LimitRow({ limit }: LimitRowProps) {
|
|
10
|
+
const percent = Math.min(100, Math.max(0, (limit.usedFraction ?? 0) * 100));
|
|
11
|
+
const scope = limit.modelId ?? limit.accountId;
|
|
12
|
+
const usedText = limit.used !== undefined ? formatUsageAmount(limit.used, limit.unit) : undefined;
|
|
13
|
+
const limitText = limit.limit !== undefined ? formatUsageAmount(limit.limit, limit.unit) : undefined;
|
|
14
|
+
const remainingText = limit.remaining !== undefined
|
|
15
|
+
? formatUsageAmount(limit.remaining, limit.unit)
|
|
16
|
+
: undefined;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="bg-paper border border-ink/15 rounded-lg p-3 shadow-xs space-y-2">
|
|
20
|
+
<div className="flex items-start justify-between gap-2">
|
|
21
|
+
<div className="min-w-0">
|
|
22
|
+
<div className="text-[11px] font-semibold text-ink truncate">{limit.label}</div>
|
|
23
|
+
{scope && <div className="text-[10px] text-ink/50 truncate">{scope}</div>}
|
|
24
|
+
</div>
|
|
25
|
+
<span className="text-[10px] text-ink/50 flex-shrink-0 whitespace-nowrap">
|
|
26
|
+
{limit.resetsAt !== undefined ? `Resets ${formatDateTime(new Date(limit.resetsAt).toISOString())}` : ''}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div className="h-1.5 w-full bg-ink/10 rounded-full overflow-hidden">
|
|
31
|
+
<div
|
|
32
|
+
className={`h-full rounded-full ${limit.status === 'exhausted' ? 'bg-error/70' : 'bg-ink/80'}`}
|
|
33
|
+
style={{ width: `${percent}%` }}
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-0.5 text-[10px] text-ink/60">
|
|
38
|
+
<span>
|
|
39
|
+
Used{' '}
|
|
40
|
+
<span className="font-semibold text-ink">
|
|
41
|
+
{limit.unit === 'percent' || !usedText ? formatFractionPercent(limit.usedFraction ?? 0) : usedText}
|
|
42
|
+
</span>
|
|
43
|
+
{/* A percent unit is already a share of 100, so naming the limit
|
|
44
|
+
again would print "42% of 100%". */}
|
|
45
|
+
{limit.unit !== 'percent' && usedText && limitText ? ` of ${limitText}` : ''}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="flex items-center gap-2">
|
|
48
|
+
{limit.status !== 'ok' && limit.status !== 'unknown' && (
|
|
49
|
+
<span className="text-[10px] font-mono uppercase px-1.5 py-0.5 rounded bg-ink/10 text-ink/70">
|
|
50
|
+
{limit.status}
|
|
51
|
+
</span>
|
|
52
|
+
)}
|
|
53
|
+
{remainingText && (
|
|
54
|
+
<span>
|
|
55
|
+
Remaining <span className="font-semibold text-ink">{remainingText}</span>
|
|
56
|
+
</span>
|
|
57
|
+
)}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{limit.notes && limit.notes.length > 0 && (
|
|
62
|
+
<ul className="text-[10px] text-ink/50 leading-relaxed space-y-0.5">
|
|
63
|
+
{limit.notes.map((note) => (
|
|
64
|
+
<li key={note}>{note}</li>
|
|
65
|
+
))}
|
|
66
|
+
</ul>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface LimitsSectionProps {
|
|
73
|
+
limits: UsageLimitWindow[];
|
|
74
|
+
/** Whether omp ships a usage adapter for this provider. */
|
|
75
|
+
tracked: boolean;
|
|
76
|
+
/** omp tracks this provider but its endpoint returned nothing. */
|
|
77
|
+
unavailable?: boolean;
|
|
78
|
+
/** Provider-wide caveats shown above the windows. */
|
|
79
|
+
notes?: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Generic quota windows for any provider omp reports usage for. */
|
|
83
|
+
export function LimitsSection({ limits, tracked, unavailable, notes }: LimitsSectionProps) {
|
|
84
|
+
return (
|
|
85
|
+
<div className="space-y-2">
|
|
86
|
+
<h4 className="text-[10px] font-mono font-semibold uppercase tracking-wider text-ink/40">
|
|
87
|
+
Quota windows
|
|
88
|
+
</h4>
|
|
89
|
+
|
|
90
|
+
{notes && notes.length > 0 && (
|
|
91
|
+
<ul className="text-[10px] text-ink/50 leading-relaxed space-y-0.5">
|
|
92
|
+
{notes.map((note) => (
|
|
93
|
+
<li key={note}>{note}</li>
|
|
94
|
+
))}
|
|
95
|
+
</ul>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{limits.length === 0 ? (
|
|
99
|
+
<p className="text-[11px] text-ink/50">
|
|
100
|
+
{!tracked
|
|
101
|
+
? 'No quota source for this provider: omp ships no usage adapter for it and it exposes no balance endpoint the chamber knows.'
|
|
102
|
+
: unavailable
|
|
103
|
+
? 'omp tracks this provider but its usage endpoint returned no data — an invalid or expired credential, an unreachable endpoint, or a plan without quota windows.'
|
|
104
|
+
: 'No quota windows were reported for this provider.'}
|
|
105
|
+
</p>
|
|
106
|
+
) : (
|
|
107
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-2.5">
|
|
108
|
+
{limits.map((limit) => (
|
|
109
|
+
<LimitRow key={limit.id} limit={limit} />
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
import type { ReactNode } from 'preact/compat';
|
|
2
|
-
import { AlertCircle
|
|
2
|
+
import { AlertCircle } from 'lucide-preact';
|
|
3
3
|
|
|
4
4
|
interface ProviderCardProps {
|
|
5
5
|
providerName: string;
|
|
6
|
-
|
|
6
|
+
/** Where the credential was found, e.g. "models.yml · agent.db". */
|
|
7
|
+
credentialSources: string[];
|
|
8
|
+
/** Provider-side failure, already human-readable. */
|
|
7
9
|
error?: string;
|
|
8
10
|
note?: string;
|
|
9
11
|
children: ReactNode;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
/** Provider shell: header,
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
export function ProviderCard({ providerName,
|
|
14
|
+
/** Provider shell: header, credential provenance, error line, and content slot.
|
|
15
|
+
* Every listed provider has a resolved credential — a keyless provider is never
|
|
16
|
+
* rendered at all — so there is no "not configured" state here. */
|
|
17
|
+
export function ProviderCard({ providerName, credentialSources, error, note, children }: ProviderCardProps) {
|
|
16
18
|
return (
|
|
17
19
|
<section className="bg-paper border border-ink/15 rounded-lg shadow-xs">
|
|
18
20
|
<header className="flex items-center justify-between gap-2 px-3.5 py-2.5 border-b border-ink/10">
|
|
19
21
|
<span className="text-xs font-semibold text-ink">{providerName}</span>
|
|
20
|
-
<span
|
|
21
|
-
|
|
22
|
+
<span
|
|
23
|
+
className="text-[10px] font-mono uppercase tracking-wider text-ink/50 truncate"
|
|
24
|
+
title={`Credentials from ${credentialSources.join(', ')}`}
|
|
25
|
+
>
|
|
26
|
+
{credentialSources.join(' · ')}
|
|
22
27
|
</span>
|
|
23
28
|
</header>
|
|
24
29
|
|
|
25
30
|
<div className="p-3.5 space-y-4">
|
|
26
|
-
{
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
Providers to see its balance, quota, and usage here.
|
|
32
|
-
</p>
|
|
33
|
-
</div>
|
|
34
|
-
) : (
|
|
35
|
-
<>
|
|
36
|
-
{error && (
|
|
37
|
-
<p className="flex items-start gap-2 text-error text-xs leading-relaxed">
|
|
38
|
-
<AlertCircle size={14} className="shrink-0 mt-0.5" />
|
|
39
|
-
<span className="break-words">{error}</span>
|
|
40
|
-
</p>
|
|
41
|
-
)}
|
|
42
|
-
{children}
|
|
43
|
-
</>
|
|
31
|
+
{error && (
|
|
32
|
+
<p className="flex items-start gap-2 text-error text-xs leading-relaxed">
|
|
33
|
+
<AlertCircle size={14} className="shrink-0 mt-0.5" />
|
|
34
|
+
<span className="break-words">{error}</span>
|
|
35
|
+
</p>
|
|
44
36
|
)}
|
|
37
|
+
{children}
|
|
45
38
|
|
|
46
39
|
{note && (
|
|
47
40
|
<p className="text-[10px] text-ink/50 leading-relaxed border-t border-ink/10 pt-2.5">{note}</p>
|
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { UsageProviderSummary } from '@/shared/types';
|
|
2
2
|
import { ProviderCard } from '@/client/components/settings/categories/usage-settings/ProviderCard';
|
|
3
3
|
import { KenariCard } from '@/client/components/settings/categories/usage-settings/KenariCard';
|
|
4
4
|
import { DeepSeekCard } from '@/client/components/settings/categories/usage-settings/DeepSeekCard';
|
|
5
|
-
import
|
|
5
|
+
import { LimitsSection } from '@/client/components/settings/categories/usage-settings/LimitsSection';
|
|
6
6
|
|
|
7
7
|
const DEEPSEEK_NOTE =
|
|
8
8
|
'DeepSeek exposes no usage or quota API — only the prepaid balance (and the web console at platform.deepseek.com) is available.';
|
|
9
9
|
|
|
10
10
|
interface ProviderDetailProps {
|
|
11
|
-
|
|
12
|
-
report: UsageReport;
|
|
11
|
+
summary: UsageProviderSummary;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
error={report.kenari.error}
|
|
23
|
-
>
|
|
24
|
-
<KenariCard report={report.kenari} />
|
|
25
|
-
</ProviderCard>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
14
|
+
/**
|
|
15
|
+
* Renders one provider's card. kenari and DeepSeek carry richer reports (wallet
|
|
16
|
+
* and Rupiah quota / prepaid balance); every other provider renders the generic
|
|
17
|
+
* quota windows, which for a pay-as-you-go vendor is its credit balance.
|
|
18
|
+
*/
|
|
19
|
+
export function ProviderDetail({ summary }: ProviderDetailProps) {
|
|
20
|
+
const hasRichReport = Boolean(summary.kenari || summary.deepseek);
|
|
28
21
|
|
|
29
22
|
return (
|
|
30
23
|
<ProviderCard
|
|
31
|
-
providerName=
|
|
32
|
-
|
|
33
|
-
error={
|
|
34
|
-
note={DEEPSEEK_NOTE}
|
|
24
|
+
providerName={summary.name}
|
|
25
|
+
credentialSources={summary.credentialSources}
|
|
26
|
+
error={summary.error}
|
|
27
|
+
note={summary.deepseek ? DEEPSEEK_NOTE : undefined}
|
|
35
28
|
>
|
|
36
|
-
<
|
|
29
|
+
{summary.kenari && <KenariCard report={summary.kenari} />}
|
|
30
|
+
{summary.deepseek && <DeepSeekCard report={summary.deepseek} />}
|
|
31
|
+
|
|
32
|
+
{/* The rich reports already render their own quota and usage tables, so
|
|
33
|
+
the generic windows would only duplicate them. When the provider
|
|
34
|
+
failed outright and reported no windows, the error line above already
|
|
35
|
+
says so — a second "no quota source" note would contradict it. */}
|
|
36
|
+
{!hasRichReport && (summary.limits.length > 0 || !summary.error) && (
|
|
37
|
+
<LimitsSection
|
|
38
|
+
limits={summary.limits}
|
|
39
|
+
tracked={summary.tracked}
|
|
40
|
+
unavailable={summary.limitsUnavailable}
|
|
41
|
+
notes={summary.notes}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
37
44
|
</ProviderCard>
|
|
38
45
|
);
|
|
39
46
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Bot, Coins } from 'lucide-preact';
|
|
2
1
|
import type { UsageReport } from '@/shared/types';
|
|
3
|
-
import { buildProviders, statusLabel, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
|
|
2
|
+
import { buildProviders, statusLabel, type ProviderEntry, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
|
|
3
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
4
4
|
|
|
5
5
|
interface UsageSidebarListProps {
|
|
6
6
|
report: UsageReport;
|
|
@@ -8,6 +8,14 @@ interface UsageSidebarListProps {
|
|
|
8
8
|
onSelectProvider: (providerId: UsageProviderId) => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
function ProviderBadge({ provider, selected }: { provider: ProviderEntry; selected: boolean }) {
|
|
12
|
+
return (
|
|
13
|
+
<span className={selected ? 'text-ink/80' : 'text-ink/60'}>
|
|
14
|
+
<ProviderIcon slug={provider.id} name={provider.name} size={14} />
|
|
15
|
+
</span>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export function UsageSidebarList({
|
|
12
20
|
report,
|
|
13
21
|
selectedProviderId,
|
|
@@ -18,59 +26,63 @@ export function UsageSidebarList({
|
|
|
18
26
|
return (
|
|
19
27
|
<div className="w-56 sm:w-64 border-r border-ink/10 h-full flex flex-col bg-paper/50 flex-shrink-0 select-none">
|
|
20
28
|
<div className="px-3.5 py-2.5 border-b border-ink/10 flex items-center justify-between">
|
|
21
|
-
<span className="text-xs font-semibold text-ink">
|
|
29
|
+
<span className="text-xs font-semibold text-ink">
|
|
30
|
+
{providers.length} provider{providers.length === 1 ? '' : 's'} with keys
|
|
31
|
+
</span>
|
|
22
32
|
</div>
|
|
23
33
|
|
|
24
34
|
<div className="flex-1 scrollbar-overlay-container scrollbar-overlay-static p-1.5 space-y-3 text-xs">
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
Providers
|
|
28
|
-
</
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
{providers.length === 0 ? (
|
|
36
|
+
<p className="px-2.5 py-3 text-[11px] text-ink/50 leading-relaxed">
|
|
37
|
+
No provider credentials detected. Add an API key in Settings → Providers.
|
|
38
|
+
</p>
|
|
39
|
+
) : (
|
|
40
|
+
<div>
|
|
41
|
+
<div className="px-2.5 pt-1.5 pb-1 text-[10px] font-bold text-ink/50 uppercase tracking-wider">
|
|
42
|
+
Providers
|
|
43
|
+
</div>
|
|
44
|
+
<div className="space-y-0.5">
|
|
45
|
+
{providers.map((provider) => {
|
|
46
|
+
const isSelected = provider.id === selectedProviderId;
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
return (
|
|
49
|
+
<button
|
|
50
|
+
key={provider.id}
|
|
51
|
+
type="button"
|
|
52
|
+
onClick={() => onSelectProvider(provider.id)}
|
|
53
|
+
className={`w-full text-left px-2.5 py-2 rounded-lg transition-colors cursor-pointer flex flex-col gap-0.5 group ${
|
|
54
|
+
isSelected
|
|
55
|
+
? 'bg-ink/10 text-ink font-medium shadow-2xs'
|
|
56
|
+
: 'text-ink/80 hover:bg-ink/5 hover:text-ink'
|
|
57
|
+
}`}
|
|
58
|
+
>
|
|
59
|
+
<div className="flex items-center justify-between gap-1.5">
|
|
60
|
+
<div className="flex items-center gap-1.5 min-w-0">
|
|
61
|
+
<div className="relative">
|
|
62
|
+
<ProviderBadge provider={provider} selected={isSelected} />
|
|
63
|
+
<span
|
|
64
|
+
className={`absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full ${
|
|
65
|
+
provider.error ? 'bg-error/60' : 'bg-ink'
|
|
66
|
+
}`}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
<span className="font-semibold text-xs text-ink truncate">{provider.name}</span>
|
|
57
70
|
</div>
|
|
58
|
-
<span
|
|
71
|
+
<span
|
|
72
|
+
className={`text-[10px] font-mono uppercase px-1.5 py-0.5 rounded bg-ink/10 font-medium ${
|
|
73
|
+
provider.error ? 'text-error' : 'text-ink/70'
|
|
74
|
+
}`}
|
|
75
|
+
>
|
|
76
|
+
{statusLabel(provider)}
|
|
77
|
+
</span>
|
|
59
78
|
</div>
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
{statusLabel(provider)}
|
|
66
|
-
</span>
|
|
67
|
-
</div>
|
|
68
|
-
<p className="text-[11px] text-ink/60 line-clamp-1 leading-snug pl-5">{provider.hint}</p>
|
|
69
|
-
</button>
|
|
70
|
-
);
|
|
71
|
-
})}
|
|
79
|
+
<p className="text-[11px] text-ink/60 line-clamp-1 leading-snug pl-5">{provider.hint}</p>
|
|
80
|
+
</button>
|
|
81
|
+
);
|
|
82
|
+
})}
|
|
83
|
+
</div>
|
|
72
84
|
</div>
|
|
73
|
-
|
|
85
|
+
)}
|
|
74
86
|
</div>
|
|
75
87
|
</div>
|
|
76
88
|
);
|