mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 15:46:22 +00:00
Revert Kimi partnership UI, restore daily limit survey (#663)
* docs: add uBlock Origin install info to getting started and ad-blocking pages Chrome dropped support for the full uBlock Origin extension — highlight that BrowserOS brings it back and make it easy to install from both the getting started guide and the dedicated ad-blocking page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: revert Kimi partnership UI, restore daily limit survey Remove Kimi/Moonshot AI partnership branding from the rate limit banner, provider card, provider templates, and LLM hub. Restore the original survey CTA on daily limit errors. Moonshot AI remains as a regular provider template without the "Recommended" badge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Greptile review comments - Guard survey CTA with !isCreditsExhausted to avoid showing it for credits-exhausted users who already see "View Usage & Billing" - Remove dead kimi-launch feature flag files (kimi-launch.ts, useKimiLaunch.ts) - Remove unused KIMI_RATE_LIMIT analytics events - Remove VITE_PUBLIC_KIMI_LAUNCH from env schema and .env.example Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,17 @@ title: "Ad Blocking"
|
|||||||
description: "BrowserOS supports full ad blocking with uBlock Origin"
|
description: "BrowserOS supports full ad blocking with uBlock Origin"
|
||||||
---
|
---
|
||||||
|
|
||||||
BrowserOS supports full ad blocking through [uBlock Origin](https://ublockorigin.com/), the most effective open-source ad blocker available.
|
BrowserOS supports full ad blocking through [uBlock Origin](https://ublockorigin.com/), the most powerful open-source ad blocker available — the full extension, not the watered-down "Lite" version.
|
||||||
|
|
||||||
## How It Works
|
## Why BrowserOS?
|
||||||
|
|
||||||
Chrome has been [phasing out support](https://developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline) for Manifest V2 extensions, which uBlock Origin relies on for its full blocking capabilities. We re-enabled Manifest V2 support in BrowserOS so uBlock Origin can run at full power.
|
Chrome [killed support](https://developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline) for uBlock Origin by phasing out Manifest V2 extensions. The only option left on Chrome is "uBlock Origin Lite," a significantly weaker version that can't use advanced filtering rules.
|
||||||
|
|
||||||
Install it from the Chrome Web Store: [uBlock Origin](https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm)
|
**BrowserOS re-enabled full Manifest V2 support**, so you can install and run the original uBlock Origin at full power — the same extension Chrome no longer allows.
|
||||||
|
|
||||||
|
<Card title="Install uBlock Origin" icon="shield-check" href="https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm">
|
||||||
|
Install the full uBlock Origin extension from the Chrome Web Store. Works on BrowserOS out of the box.
|
||||||
|
</Card>
|
||||||
|
|
||||||
## BrowserOS vs Chrome
|
## BrowserOS vs Chrome
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ Welcome to BrowserOS! Let's get you set up.
|
|||||||
|
|
||||||
## You're all set!
|
## You're all set!
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
**Block ads with uBlock Origin** — Chrome dropped support for the full uBlock Origin extension, but BrowserOS brought it back. [Install it from the Chrome Web Store](https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm) and browse ad-free. [Learn more →](/features/ad-blocking)
|
||||||
|
</Tip>
|
||||||
|
|
||||||
Explore what BrowserOS can do:
|
Explore what BrowserOS can do:
|
||||||
|
|
||||||
<Columns cols={2}>
|
<Columns cols={2}>
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ VITE_PUBLIC_SENTRY_DSN=
|
|||||||
# BrowserOS API URL
|
# BrowserOS API URL
|
||||||
VITE_PUBLIC_BROWSEROS_API=https://api.browseros.com
|
VITE_PUBLIC_BROWSEROS_API=https://api.browseros.com
|
||||||
|
|
||||||
# Launch feature flags
|
|
||||||
VITE_PUBLIC_KIMI_LAUNCH=false
|
|
||||||
|
|
||||||
# GraphQL Schema Path (optional — falls back to schema/schema.graphql)
|
# GraphQL Schema Path (optional — falls back to schema/schema.graphql)
|
||||||
GRAPHQL_SCHEMA_PATH=
|
GRAPHQL_SCHEMA_PATH=
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ import {
|
|||||||
KIMI_API_KEY_GUIDE_CLICKED_EVENT,
|
KIMI_API_KEY_GUIDE_CLICKED_EVENT,
|
||||||
MODEL_SELECTED_EVENT,
|
MODEL_SELECTED_EVENT,
|
||||||
} from '@/lib/constants/analyticsEvents'
|
} from '@/lib/constants/analyticsEvents'
|
||||||
import { useKimiLaunch } from '@/lib/feature-flags/useKimiLaunch'
|
|
||||||
import {
|
import {
|
||||||
getDefaultBaseUrlForProviders,
|
getDefaultBaseUrlForProviders,
|
||||||
getProviderTemplate,
|
getProviderTemplate,
|
||||||
@@ -226,7 +225,6 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
|
|||||||
const modelListRef = useRef<HTMLDivElement>(null)
|
const modelListRef = useRef<HTMLDivElement>(null)
|
||||||
const { supports } = useCapabilities()
|
const { supports } = useCapabilities()
|
||||||
const { baseUrl: agentServerUrl } = useAgentServerUrl()
|
const { baseUrl: agentServerUrl } = useAgentServerUrl()
|
||||||
const kimiLaunch = useKimiLaunch()
|
|
||||||
|
|
||||||
const filteredProviderTypeOptions = providerTypeOptions.filter((opt) => {
|
const filteredProviderTypeOptions = providerTypeOptions.filter((opt) => {
|
||||||
if (opt.value === 'chatgpt-pro')
|
if (opt.value === 'chatgpt-pro')
|
||||||
@@ -234,8 +232,6 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
|
|||||||
if (opt.value === 'github-copilot')
|
if (opt.value === 'github-copilot')
|
||||||
return supports(Feature.GITHUB_COPILOT_SUPPORT)
|
return supports(Feature.GITHUB_COPILOT_SUPPORT)
|
||||||
if (opt.value === 'qwen-code') return supports(Feature.QWEN_CODE_SUPPORT)
|
if (opt.value === 'qwen-code') return supports(Feature.QWEN_CODE_SUPPORT)
|
||||||
if (opt.value === 'moonshot')
|
|
||||||
return kimiLaunch || initialValues?.type === 'moonshot'
|
|
||||||
if (opt.value === 'openai-compatible') {
|
if (opt.value === 'openai-compatible') {
|
||||||
return supports(Feature.OPENAI_COMPATIBLE_SUPPORT)
|
return supports(Feature.OPENAI_COMPATIBLE_SUPPORT)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Check, Loader2, Trash2 } from 'lucide-react'
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { useKimiLaunch } from '@/lib/feature-flags/useKimiLaunch'
|
|
||||||
import { BrowserOSIcon, ProviderIcon } from '@/lib/llm-providers/providerIcons'
|
import { BrowserOSIcon, ProviderIcon } from '@/lib/llm-providers/providerIcons'
|
||||||
import type { LlmProviderConfig } from '@/lib/llm-providers/types'
|
import type { LlmProviderConfig } from '@/lib/llm-providers/types'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
@@ -30,7 +29,6 @@ export const ProviderCard: FC<ProviderCardProps> = ({
|
|||||||
isTesting = false,
|
isTesting = false,
|
||||||
}) => {
|
}) => {
|
||||||
const inputId = `provider-${provider.id}`
|
const inputId = `provider-${provider.id}`
|
||||||
const kimiLaunch = useKimiLaunch()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
@@ -79,30 +77,21 @@ export const ProviderCard: FC<ProviderCardProps> = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isBuiltIn && provider.type === 'browseros' && kimiLaunch && (
|
|
||||||
<span className="mb-1 inline-block rounded-full border border-orange-300/60 bg-orange-100/70 px-3 py-0.5 font-semibold text-orange-700 text-xs dark:border-orange-400/40 dark:bg-orange-500/15 dark:text-orange-300">
|
|
||||||
In partnership with Moonshot AI
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<p className="truncate text-muted-foreground text-sm">
|
<p className="truncate text-muted-foreground text-sm">
|
||||||
{isBuiltIn ? (
|
{isBuiltIn ? (
|
||||||
kimiLaunch ? (
|
<>
|
||||||
'Extended usage limits for the next 2 weeks!'
|
BrowserOS-hosted model with strict rate limits.{' '}
|
||||||
) : (
|
<a
|
||||||
<>
|
href="https://docs.browseros.com/features/bring-your-own-llm"
|
||||||
BrowserOS-hosted model with strict rate limits.{' '}
|
target="_blank"
|
||||||
<a
|
rel="noopener noreferrer"
|
||||||
href="https://docs.browseros.com/features/bring-your-own-llm"
|
className="underline hover:text-foreground"
|
||||||
target="_blank"
|
onClick={(e) => e.stopPropagation()}
|
||||||
rel="noopener noreferrer"
|
>
|
||||||
className="underline hover:text-foreground"
|
Bring your own key
|
||||||
onClick={(e) => e.stopPropagation()}
|
</a>{' '}
|
||||||
>
|
for better performance.
|
||||||
Bring your own key
|
</>
|
||||||
</a>{' '}
|
|
||||||
for better performance.
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
) : provider.baseUrl ? (
|
) : provider.baseUrl ? (
|
||||||
`${provider.modelId} • ${provider.baseUrl}`
|
`${provider.modelId} • ${provider.baseUrl}`
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
} from '@/components/ui/collapsible'
|
} from '@/components/ui/collapsible'
|
||||||
import { Feature } from '@/lib/browseros/capabilities'
|
import { Feature } from '@/lib/browseros/capabilities'
|
||||||
import { useCapabilities } from '@/lib/browseros/useCapabilities'
|
import { useCapabilities } from '@/lib/browseros/useCapabilities'
|
||||||
import { useKimiLaunch } from '@/lib/feature-flags/useKimiLaunch'
|
|
||||||
import {
|
import {
|
||||||
type ProviderTemplate,
|
type ProviderTemplate,
|
||||||
providerTemplates,
|
providerTemplates,
|
||||||
@@ -23,7 +22,6 @@ export const ProviderTemplatesSection: FC<ProviderTemplatesSectionProps> = ({
|
|||||||
onUseTemplate,
|
onUseTemplate,
|
||||||
}) => {
|
}) => {
|
||||||
const { supports } = useCapabilities()
|
const { supports } = useCapabilities()
|
||||||
const kimiLaunch = useKimiLaunch()
|
|
||||||
|
|
||||||
const filteredTemplates = providerTemplates.filter((template) => {
|
const filteredTemplates = providerTemplates.filter((template) => {
|
||||||
if (template.id === 'chatgpt-pro')
|
if (template.id === 'chatgpt-pro')
|
||||||
@@ -31,7 +29,6 @@ export const ProviderTemplatesSection: FC<ProviderTemplatesSectionProps> = ({
|
|||||||
if (template.id === 'github-copilot')
|
if (template.id === 'github-copilot')
|
||||||
return supports(Feature.GITHUB_COPILOT_SUPPORT)
|
return supports(Feature.GITHUB_COPILOT_SUPPORT)
|
||||||
if (template.id === 'qwen-code') return supports(Feature.QWEN_CODE_SUPPORT)
|
if (template.id === 'qwen-code') return supports(Feature.QWEN_CODE_SUPPORT)
|
||||||
if (template.id === 'moonshot') return kimiLaunch
|
|
||||||
if (template.id === 'openai-compatible') {
|
if (template.id === 'openai-compatible') {
|
||||||
return supports(Feature.OPENAI_COMPATIBLE_SUPPORT)
|
return supports(Feature.OPENAI_COMPATIBLE_SUPPORT)
|
||||||
}
|
}
|
||||||
@@ -67,7 +64,6 @@ export const ProviderTemplatesSection: FC<ProviderTemplatesSectionProps> = ({
|
|||||||
<ProviderTemplateCard
|
<ProviderTemplateCard
|
||||||
key={template.id}
|
key={template.id}
|
||||||
template={template}
|
template={template}
|
||||||
highlighted={template.id === 'moonshot'}
|
|
||||||
isNew={isNew}
|
isNew={isNew}
|
||||||
onUseTemplate={onUseTemplate}
|
onUseTemplate={onUseTemplate}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { Globe2, Trash2 } from 'lucide-react'
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { useKimiLaunch } from '@/lib/feature-flags/useKimiLaunch'
|
|
||||||
import { cn } from '@/lib/utils'
|
|
||||||
import { getFaviconUrl, type LlmHubProvider } from './models'
|
import { getFaviconUrl, type LlmHubProvider } from './models'
|
||||||
|
|
||||||
interface HubProviderRowProps {
|
interface HubProviderRowProps {
|
||||||
@@ -20,20 +18,9 @@ export const HubProviderRow: FC<HubProviderRowProps> = ({
|
|||||||
onDelete,
|
onDelete,
|
||||||
}) => {
|
}) => {
|
||||||
const iconUrl = useMemo(() => getFaviconUrl(provider.url), [provider.url])
|
const iconUrl = useMemo(() => getFaviconUrl(provider.url), [provider.url])
|
||||||
const kimiLaunch = useKimiLaunch()
|
|
||||||
const normalizedName = provider.name.trim().toLowerCase()
|
|
||||||
const normalizedUrl = provider.url.trim().toLowerCase()
|
|
||||||
const isKimi = normalizedName === 'kimi' || normalizedUrl.includes('kimi.com')
|
|
||||||
const showKimiFlare = isKimi && kimiLaunch
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="group flex w-full items-center gap-4 rounded-xl border border-border bg-card p-4 transition-all hover:border-[var(--accent-orange)] hover:shadow-md">
|
||||||
className={cn(
|
|
||||||
'group flex w-full items-center gap-4 rounded-xl border border-border bg-card p-4 transition-all hover:border-[var(--accent-orange)] hover:shadow-md',
|
|
||||||
showKimiFlare &&
|
|
||||||
'border-orange-300/80 bg-orange-50/20 shadow-sm ring-1 ring-orange-300/45 dark:bg-orange-500/5',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted">
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted">
|
||||||
{iconUrl ? (
|
{iconUrl ? (
|
||||||
<img
|
<img
|
||||||
@@ -49,16 +36,6 @@ export const HubProviderRow: FC<HubProviderRowProps> = ({
|
|||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="mb-0.5 flex items-center gap-2">
|
<div className="mb-0.5 flex items-center gap-2">
|
||||||
<span className="block truncate font-semibold">{provider.name}</span>
|
<span className="block truncate font-semibold">{provider.name}</span>
|
||||||
{showKimiFlare && (
|
|
||||||
<div className="flex flex-wrap items-center gap-1">
|
|
||||||
<span className="rounded-full border border-orange-300/60 bg-orange-100/70 px-2 py-0.5 font-semibold text-[11px] text-orange-700 dark:border-orange-400/40 dark:bg-orange-500/15 dark:text-orange-300">
|
|
||||||
Recommended
|
|
||||||
</span>
|
|
||||||
<span className="rounded-full border border-orange-300/60 bg-orange-100/60 px-2.5 py-0.5 font-medium text-orange-700 text-xs dark:border-orange-400/40 dark:bg-orange-500/15 dark:text-orange-300">
|
|
||||||
Powered by Moonshot AI
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="truncate text-muted-foreground/70 text-xs">
|
<p className="truncate text-muted-foreground/70 text-xs">
|
||||||
{provider.url}
|
{provider.url}
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
import { AlertCircle, RefreshCw } from 'lucide-react'
|
import { AlertCircle, RefreshCw } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
// import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
||||||
// --- Commented out for Kimi partnership launch (restore after) ---
|
const SURVEY_DIRECTIONS = [
|
||||||
// const SURVEY_DIRECTIONS = [
|
'competitor',
|
||||||
// 'competitor',
|
'switching',
|
||||||
// 'switching',
|
'workflow',
|
||||||
// 'workflow',
|
'activation',
|
||||||
// 'activation',
|
] as const
|
||||||
// ] as const
|
|
||||||
//
|
function pickRandomDirection(): string {
|
||||||
// function pickRandomDirection(): string {
|
return SURVEY_DIRECTIONS[Math.floor(Math.random() * SURVEY_DIRECTIONS.length)]
|
||||||
// return SURVEY_DIRECTIONS[Math.floor(Math.random() * SURVEY_DIRECTIONS.length)]
|
}
|
||||||
// }
|
|
||||||
// --- End commented out survey code ---
|
|
||||||
|
|
||||||
interface ChatErrorProps {
|
interface ChatErrorProps {
|
||||||
error: Error
|
error: Error
|
||||||
@@ -95,13 +93,11 @@ export const ChatError: FC<ChatErrorProps> = ({
|
|||||||
const { text, url, isRateLimit, isCreditsExhausted, isConnectionError } =
|
const { text, url, isRateLimit, isCreditsExhausted, isConnectionError } =
|
||||||
parseErrorMessage(error.message, providerType)
|
parseErrorMessage(error.message, providerType)
|
||||||
|
|
||||||
// --- Commented out for Kimi partnership launch (restore after) ---
|
const surveyUrl = useMemo(
|
||||||
// const surveyUrl = useMemo(
|
() =>
|
||||||
// () =>
|
`/app.html?page=survey&maxTurns=20&experimentId=daily_limit_${pickRandomDirection()}#/settings/survey`,
|
||||||
// `/app.html?page=survey&maxTurns=20&experimentId=daily_limit_${pickRandomDirection()}#/settings/survey`,
|
[],
|
||||||
// [],
|
)
|
||||||
// )
|
|
||||||
// --- End commented out survey code ---
|
|
||||||
|
|
||||||
const getTitle = () => {
|
const getTitle = () => {
|
||||||
if (isRateLimit) return 'Daily limit reached'
|
if (isRateLimit) return 'Daily limit reached'
|
||||||
@@ -126,8 +122,17 @@ export const ChatError: FC<ChatErrorProps> = ({
|
|||||||
View troubleshooting guide
|
View troubleshooting guide
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{/* --- Commented out for Kimi partnership launch (restore after) ---
|
{isCreditsExhausted && url && (
|
||||||
{isRateLimit && (
|
<a
|
||||||
|
href={url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-muted-foreground text-xs underline hover:text-foreground"
|
||||||
|
>
|
||||||
|
View Usage & Billing
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{isRateLimit && !isCreditsExhausted && (
|
||||||
<p className="text-muted-foreground text-xs">
|
<p className="text-muted-foreground text-xs">
|
||||||
<a
|
<a
|
||||||
href={url}
|
href={url}
|
||||||
@@ -148,27 +153,6 @@ export const ChatError: FC<ChatErrorProps> = ({
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
--- End commented out survey code --- */}
|
|
||||||
{isCreditsExhausted && url && (
|
|
||||||
<a
|
|
||||||
href={url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-muted-foreground text-xs underline hover:text-foreground"
|
|
||||||
>
|
|
||||||
View Usage & Billing
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
{isRateLimit && providerType === 'browseros' && (
|
|
||||||
<a
|
|
||||||
href="/app.html#/settings/ai"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="inline-flex items-center gap-1.5 rounded-md border border-[var(--accent-orange)] bg-[var(--accent-orange)]/10 px-3 py-1.5 font-medium text-[var(--accent-orange)] text-xs transition-colors hover:bg-[var(--accent-orange)]/20"
|
|
||||||
>
|
|
||||||
Add your own provider for unlimited usage
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
{onRetry && (
|
{onRetry && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -280,14 +280,6 @@ export const KIMI_API_KEY_CONFIGURED_EVENT = 'settings.kimi.api_key_configured'
|
|||||||
export const KIMI_API_KEY_GUIDE_CLICKED_EVENT =
|
export const KIMI_API_KEY_GUIDE_CLICKED_EVENT =
|
||||||
'settings.kimi.api_key_guide_clicked'
|
'settings.kimi.api_key_guide_clicked'
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export const KIMI_RATE_LIMIT_DOCS_CLICKED_EVENT =
|
|
||||||
'ui.rate_limit.kimi_docs_clicked'
|
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export const KIMI_RATE_LIMIT_PLATFORM_CLICKED_EVENT =
|
|
||||||
'ui.rate_limit.moonshot_platform_clicked'
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export const SIDEPANEL_VOICE_RECORDING_STARTED_EVENT =
|
export const SIDEPANEL_VOICE_RECORDING_STARTED_EVENT =
|
||||||
'sidepanel.voice.recording_started'
|
'sidepanel.voice.recording_started'
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ const EnvSchema = z.object({
|
|||||||
VITE_PUBLIC_POSTHOG_HOST: z.string().optional(),
|
VITE_PUBLIC_POSTHOG_HOST: z.string().optional(),
|
||||||
VITE_PUBLIC_SENTRY_DSN: z.string().optional(),
|
VITE_PUBLIC_SENTRY_DSN: z.string().optional(),
|
||||||
VITE_PUBLIC_BROWSEROS_API: z.string().optional(),
|
VITE_PUBLIC_BROWSEROS_API: z.string().optional(),
|
||||||
VITE_PUBLIC_KIMI_LAUNCH: z.string().optional(),
|
|
||||||
PROD: z.boolean(),
|
PROD: z.boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { env } from '@/lib/env'
|
|
||||||
|
|
||||||
const ENABLED_VALUES = new Set(['1', 'true', 'yes', 'on'])
|
|
||||||
|
|
||||||
function parseKimiLaunchFlag(value: string | undefined): boolean {
|
|
||||||
if (!value) return false
|
|
||||||
return ENABLED_VALUES.has(value.trim().toLowerCase())
|
|
||||||
}
|
|
||||||
|
|
||||||
const kimiLaunchEnabled = parseKimiLaunchFlag(env.VITE_PUBLIC_KIMI_LAUNCH)
|
|
||||||
|
|
||||||
export function isKimiLaunchEnabled(): boolean {
|
|
||||||
return kimiLaunchEnabled
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { isKimiLaunchEnabled } from './kimi-launch'
|
|
||||||
|
|
||||||
export function useKimiLaunch(): boolean {
|
|
||||||
return isKimiLaunchEnabled()
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
|
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
|
||||||
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
|
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
|
||||||
import { isKimiLaunchEnabled } from '@/lib/feature-flags/kimi-launch'
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface LlmHubProvider {
|
export interface LlmHubProvider {
|
||||||
@@ -8,43 +7,15 @@ export interface LlmHubProvider {
|
|||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIMI_PROVIDER: LlmHubProvider = {
|
|
||||||
name: 'Kimi',
|
|
||||||
url: 'https://www.kimi.com',
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureKimiFirst(providers: LlmHubProvider[]): LlmHubProvider[] {
|
|
||||||
if (!isKimiLaunchEnabled()) return providers
|
|
||||||
const hasKimi = providers.some(
|
|
||||||
(p) => p.name === 'Kimi' || p.url.includes('kimi.com'),
|
|
||||||
)
|
|
||||||
return hasKimi ? providers : [KIMI_PROVIDER, ...providers]
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadProviders(): Promise<LlmHubProvider[]> {
|
export async function loadProviders(): Promise<LlmHubProvider[]> {
|
||||||
try {
|
try {
|
||||||
const adapter = getBrowserOSAdapter()
|
const adapter = getBrowserOSAdapter()
|
||||||
const providersPref = await adapter.getPref(
|
const providersPref = await adapter.getPref(
|
||||||
BROWSEROS_PREFS.THIRD_PARTY_LLM_PROVIDERS,
|
BROWSEROS_PREFS.THIRD_PARTY_LLM_PROVIDERS,
|
||||||
)
|
)
|
||||||
const providers = (providersPref?.value as LlmHubProvider[]) || []
|
return (providersPref?.value as LlmHubProvider[]) || []
|
||||||
|
|
||||||
if (providers.length === 0) {
|
|
||||||
if (isKimiLaunchEnabled()) {
|
|
||||||
const defaults = [KIMI_PROVIDER]
|
|
||||||
await saveProviders(defaults)
|
|
||||||
return defaults
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = ensureKimiFirst(providers)
|
|
||||||
if (normalized !== providers) {
|
|
||||||
await saveProviders(normalized)
|
|
||||||
}
|
|
||||||
return normalized
|
|
||||||
} catch {
|
} catch {
|
||||||
return isKimiLaunchEnabled() ? [KIMI_PROVIDER] : []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ import { storage } from '@wxt-dev/storage'
|
|||||||
import { sessionStorage } from '@/lib/auth/sessionStorage'
|
import { sessionStorage } from '@/lib/auth/sessionStorage'
|
||||||
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
|
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
|
||||||
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
|
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
|
||||||
import { isKimiLaunchEnabled } from '@/lib/feature-flags/kimi-launch'
|
|
||||||
import type { LlmProviderConfig, LlmProvidersBackup } from './types'
|
import type { LlmProviderConfig, LlmProvidersBackup } from './types'
|
||||||
import { uploadLlmProvidersToGraphql } from './uploadLlmProvidersToGraphql'
|
import { uploadLlmProvidersToGraphql } from './uploadLlmProvidersToGraphql'
|
||||||
|
|
||||||
/** Default provider ID constant */
|
/** Default provider ID constant */
|
||||||
export const DEFAULT_PROVIDER_ID = 'browseros'
|
export const DEFAULT_PROVIDER_ID = 'browseros'
|
||||||
const DEFAULT_PROVIDER_NAME = 'BrowserOS'
|
const DEFAULT_PROVIDER_NAME = 'BrowserOS'
|
||||||
const KIMI_LAUNCH_PROVIDER_NAME = 'Kimi K2.5'
|
|
||||||
|
|
||||||
/** Storage key for LLM providers array */
|
/** Storage key for LLM providers array */
|
||||||
export const providersStorage = storage.defineItem<LlmProviderConfig[]>(
|
export const providersStorage = storage.defineItem<LlmProviderConfig[]>(
|
||||||
@@ -91,7 +89,7 @@ export function setupLlmProvidersSyncToBackend(): () => void {
|
|||||||
/** Load providers from storage */
|
/** Load providers from storage */
|
||||||
export async function loadProviders(): Promise<LlmProviderConfig[]> {
|
export async function loadProviders(): Promise<LlmProviderConfig[]> {
|
||||||
const providers = (await providersStorage.getValue()) || []
|
const providers = (await providersStorage.getValue()) || []
|
||||||
const normalizedProviders = normalizeProvidersForLaunch(providers)
|
const normalizedProviders = normalizeProviderNames(providers)
|
||||||
|
|
||||||
// Keep storage consistent so every consumer sees the same provider name.
|
// Keep storage consistent so every consumer sees the same provider name.
|
||||||
if (
|
if (
|
||||||
@@ -109,7 +107,7 @@ export function createDefaultBrowserOSProvider(): LlmProviderConfig {
|
|||||||
return {
|
return {
|
||||||
id: DEFAULT_PROVIDER_ID,
|
id: DEFAULT_PROVIDER_ID,
|
||||||
type: 'browseros',
|
type: 'browseros',
|
||||||
name: getBuiltInProviderName(),
|
name: DEFAULT_PROVIDER_NAME,
|
||||||
baseUrl: 'https://api.browseros.com/v1',
|
baseUrl: 'https://api.browseros.com/v1',
|
||||||
modelId: 'browseros-auto',
|
modelId: 'browseros-auto',
|
||||||
supportsImages: true,
|
supportsImages: true,
|
||||||
@@ -125,26 +123,22 @@ export function createDefaultProvidersConfig(): LlmProviderConfig[] {
|
|||||||
return [createDefaultBrowserOSProvider()]
|
return [createDefaultBrowserOSProvider()]
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBuiltInProviderName(): string {
|
/**
|
||||||
return isKimiLaunchEnabled()
|
* Normalize built-in provider names back to "BrowserOS" (e.g. from "Kimi K2.5"
|
||||||
? KIMI_LAUNCH_PROVIDER_NAME
|
* which was set during a previous partnership launch).
|
||||||
: DEFAULT_PROVIDER_NAME
|
*/
|
||||||
}
|
function normalizeProviderNames(
|
||||||
|
|
||||||
function normalizeProvidersForLaunch(
|
|
||||||
providers: LlmProviderConfig[],
|
providers: LlmProviderConfig[],
|
||||||
): LlmProviderConfig[] {
|
): LlmProviderConfig[] {
|
||||||
const builtInProviderName = getBuiltInProviderName()
|
|
||||||
|
|
||||||
return providers.map((provider) => {
|
return providers.map((provider) => {
|
||||||
if (
|
if (
|
||||||
provider.id === DEFAULT_PROVIDER_ID &&
|
provider.id === DEFAULT_PROVIDER_ID &&
|
||||||
provider.type === 'browseros' &&
|
provider.type === 'browseros' &&
|
||||||
provider.name !== builtInProviderName
|
provider.name !== DEFAULT_PROVIDER_NAME
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...provider,
|
...provider,
|
||||||
name: builtInProviderName,
|
name: DEFAULT_PROVIDER_NAME,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return provider
|
return provider
|
||||||
|
|||||||
Reference in New Issue
Block a user