diff --git a/apps/supercode-cli/server/package.json b/apps/supercode-cli/server/package.json index 60a5f02..5ce2ab7 100644 --- a/apps/supercode-cli/server/package.json +++ b/apps/supercode-cli/server/package.json @@ -1,6 +1,6 @@ { "name": "supercode-cli", - "version": "0.1.77", + "version": "0.1.79", "description": "AI-powered coding agent CLI", "main": "dist/main.js", "bin": { diff --git a/apps/supercode-cli/server/src/cli/ai/context-windows.ts b/apps/supercode-cli/server/src/cli/ai/context-windows.ts index 15b6e2e..87efb94 100644 --- a/apps/supercode-cli/server/src/cli/ai/context-windows.ts +++ b/apps/supercode-cli/server/src/cli/ai/context-windows.ts @@ -21,6 +21,12 @@ const CONTEXT_WINDOWS: Record = { "anthropic/claude-opus-4-8": 1_000_000, "anthropic/claude-opus-4-7": 1_000_000, "openai/gpt-5.5": 1_100_000, + "anthropic/claude-sonnet-4.6": 200_000, + "anthropic/claude-opus-4.7": 200_000, + "deepseek/deepseek-chat": 128_000, + "deepseek/deepseek-reasoner": 128_000, + "grok/grok-4-fast-reasoning": 131_072, + "orcarouter/auto": 128_000, } const FALLBACK_CONTEXT_WINDOW = 128_000 diff --git a/apps/supercode-cli/server/src/cli/ai/orcarouter-service.ts b/apps/supercode-cli/server/src/cli/ai/orcarouter-service.ts new file mode 100644 index 0000000..cbaf779 --- /dev/null +++ b/apps/supercode-cli/server/src/cli/ai/orcarouter-service.ts @@ -0,0 +1,269 @@ +import { createOpenAICompatible } from "@ai-sdk/openai-compatible" +import { streamText, stepCountIs, type ModelMessage, type LanguageModel, type LanguageModelUsage } from "ai" +import chalk from "chalk" +import { orcarouterConfig } from "../../config/orcarouter.config.ts" +import { recordUsage } from "../../lib/track-usage" +import { computeCost } from "../../lib/pricing" +import { isEmptyToolResult, isDeniedToolResult, summarizeToolResult, tcName } from "./tool-result" + +const HIGH_VALUE_MODELS: string[] = [] + +export class OrcaRouterService { + model: LanguageModel + readonly modelName: string + + constructor(modelName?: string) { + if (!orcarouterConfig.apiKey) { + throw new Error( + "OrcaRouter is not configured.\n\n Set ORCAROUTER_API_KEY in your environment:\n" + + " export ORCAROUTER_API_KEY=\n\n" + + " Get a key at: https://orcarouter.ai", + ) + } + + this.modelName = modelName || orcarouterConfig.model + + const client = createOpenAICompatible({ + name: "orcarouter", + baseURL: orcarouterConfig.baseUrl, + headers: { Authorization: `Bearer ${orcarouterConfig.apiKey}` }, + }) + + this.model = client.chatModel(this.modelName) + } + + async sendMessage( + messages: ModelMessage[], + onChunk?: (chunk: string) => void, + tools?: any, + onToolCall?: any, + signal?: AbortSignal, + onReasoning?: (chunk: string) => void, + onToolResult?: (params: { toolName: string; args: unknown; result: string }) => void, + onStepFinish?: (params: { stepNumber: number; toolCalls: Array<{ toolName: string; args: unknown }>; toolResults: Array<{ toolName: string; args: unknown; result: string }> }) => void, + ) { + const streamAbortController = new AbortController() + const streamTimeout = setTimeout(() => streamAbortController.abort(), 120_000) + const signalHandler = signal ? () => streamAbortController.abort() : undefined + signalHandler && signal!.addEventListener("abort", signalHandler, { once: true }) + + try { + const systemMessages = messages.filter(m => m.role === "system") + const nonSystemMessages = messages.filter(m => m.role !== "system") + const system = systemMessages.map(m => m.content).join("\n") + + const hasTools = tools && Object.keys(tools).length > 0 + + if (!hasTools) { + const result = streamText({ + model: this.model, + messages: nonSystemMessages, + system, + abortSignal: streamAbortController.signal, + ...(!HIGH_VALUE_MODELS.includes(this.modelName) ? { maxOutputTokens: 8192 } : {}), + }) + + let fullResponse = "" + for await (const chunk of result.textStream) { + fullResponse += chunk + onChunk?.(chunk) + } + + const [finishReason, usage] = await Promise.all([ + result.finishReason, + result.usage, + ]) + + recordUsage({ + provider: "orcarouter", + model: this.modelName, + inputTokens: usage.inputTokens ?? 0, + outputTokens: usage.outputTokens ?? 0, + cachedInputTokens: usage.inputTokenDetails?.cacheReadTokens ?? 0, + totalTokens: usage.totalTokens ?? 0, + costUsd: computeCost(this.modelName, usage.inputTokens ?? 0, usage.outputTokens ?? 0, usage.inputTokenDetails?.cacheReadTokens ?? 0), + durationMs: null, + }) + + return { + content: fullResponse, + finishReason, + usage, + } + } + + let fullResponse = "" + const seenStepResults: Array<{ toolName: string; result: string }> = [] + const deniedCounts = new Map() + let stopForDenialLoop = false + const toolCallHistory: Array<{ toolName: string; argsKey: string }> = [] + let stopForRepetition = false + + const result = streamText({ + model: this.model, + messages: nonSystemMessages, + system, + tools, + ...(!HIGH_VALUE_MODELS.includes(this.modelName) ? { maxOutputTokens: 8192 } : {} as Record), + stopWhen: stepCountIs(8), + abortSignal: streamAbortController.signal, + prepareStep: async ({ messages }) => { + if (stopForRepetition) { + return { + messages: [ + ...messages, + { + role: "system" as const, + content: + "SYSTEM NOTICE: You have called the same tools with the same arguments " + + "multiple times without making progress. Stop repeating yourself. " + + "Analyze what you already have and respond to the user.", + }, + ], + } + } + if (stopForDenialLoop) { + return { + messages: [ + ...messages, + { + role: "system" as const, + content: + "SYSTEM NOTICE: You have called the same permission-protected tool multiple " + + "times after the user denied it. Stop calling it. Respond to the user with " + + "what you have so far and ask for guidance.", + }, + ], + } + } + if (seenStepResults.length === 0) return undefined + const allEmpty = seenStepResults.every((r) => isEmptyToolResult(r.result)) + if (!allEmpty) return undefined + const summary = seenStepResults + .map((r) => `- ${r.toolName}: ${summarizeToolResult(r.result)}`) + .join("\n") + return { + messages: [ + ...messages, + { + role: "system" as const, + content: + "SYSTEM NOTICE: All tool calls so far have returned empty or error results. " + + "You have NO source material to answer with. Do NOT invent specifications, pricing, " + + "dates, leaderboard rankings, or any factual claims. Tell the user which tools failed " + + "and what you would need to proceed.\n\nTool outcomes:\n" + summary, + }, + ], + } + }, + onStepFinish: async (event) => { + if (event.toolCalls?.length) { + for (const tc of event.toolCalls) { + onToolCall?.({ toolName: tc.toolName, args: (tc as any).input as Record }) + const args = (tc as any).input ?? {} + const argsKey = JSON.stringify(args, Object.keys(args).sort()) + toolCallHistory.push({ toolName: tc.toolName, argsKey }) + let repCount = 0 + for (const h of toolCallHistory) { + if (h.toolName === tc.toolName && h.argsKey === argsKey) repCount++ + } + if (repCount >= 3) stopForRepetition = true + if (toolCallHistory.length > 12) { + toolCallHistory.splice(0, toolCallHistory.length - 12) + } + } + } + const inputByCallId = new Map() + if (event.toolCalls?.length) { + for (const tc of event.toolCalls) { + const id = (tc as any).toolCallId + if (typeof id === "string") { + inputByCallId.set(id, (tc as any).input) + } + } + } + const toolResults = (event as any).toolResults as + | Array<{ toolName?: string; toolCallId?: string; input?: unknown; output?: unknown }> + | undefined + if (toolResults?.length) { + for (const tr of toolResults) { + const name = tcName(tr.toolName) ?? "unknown" + const out = (tr as any).output + const text = + typeof out === "string" + ? out + : out === undefined || out === null + ? "" + : JSON.stringify(out) + seenStepResults.push({ toolName: name, result: text }) + const args = tr.input ?? (tr.toolCallId ? inputByCallId.get(tr.toolCallId) : undefined) + if (onToolResult) { + onToolResult({ toolName: name, args, result: text }) + } + if (isDeniedToolResult(text)) { + const prev = deniedCounts.get(name) ?? 0 + const next = prev + 1 + deniedCounts.set(name, next) + if (next >= 2) stopForDenialLoop = true + } else { + deniedCounts.set(name, 0) + } + } + } + }, + }) + + for await (const chunk of result.textStream) { + fullResponse += chunk + onChunk?.(chunk) + } + + const [finishReason, usage] = await Promise.all([ + result.finishReason, + result.usage, + ]) + + recordUsage({ + provider: "orcarouter", + model: this.modelName, + inputTokens: usage.inputTokens ?? 0, + outputTokens: usage.outputTokens ?? 0, + cachedInputTokens: usage.inputTokenDetails?.cacheReadTokens ?? 0, + totalTokens: usage.totalTokens ?? 0, + costUsd: computeCost(this.modelName, usage.inputTokens ?? 0, usage.outputTokens ?? 0, usage.inputTokenDetails?.cacheReadTokens ?? 0), + durationMs: null, + }) + + return { + content: fullResponse, + finishReason, + usage, + } + } catch (error: any) { + if (error?.name === "AbortError") throw error + const msg = error instanceof Error ? error.message : String(error) + const is5xx = /OrcaRouter (?:API )?5\d\d/.test(msg) || /status code 5\d\d/i.test(msg) + if (is5xx) { + const friendly = new Error( + `OrcaRouter gateway error (HTTP 5xx). This is upstream — not your request. ` + + `Try again, or run /model to switch providers.\n ${msg}`, + ) + console.error(chalk.red("OrcaRouter Service Error:"), friendly.message) + throw friendly + } + console.error(chalk.red("OrcaRouter Service Error:"), msg) + throw error + } finally { + clearTimeout(streamTimeout) + if (signalHandler) signal!.removeEventListener("abort", signalHandler as any) + } + } + + async getMessage(messages: ModelMessage[], tools?: any) { + let fullResponse = "" + const result = await this.sendMessage(messages, (chunk) => { + fullResponse += chunk + }) + return result.content + } +} diff --git a/apps/supercode-cli/server/src/cli/ai/provider.ts b/apps/supercode-cli/server/src/cli/ai/provider.ts index 49913e1..5184f65 100644 --- a/apps/supercode-cli/server/src/cli/ai/provider.ts +++ b/apps/supercode-cli/server/src/cli/ai/provider.ts @@ -5,14 +5,16 @@ import { OpenRouterService } from "./openrouter-service.ts" import { NvidiaService } from "./nvidia-service.ts" import { ConcentrateService } from "./concentrate-service.ts" import { MergeDevService } from "./mergedev-service.ts" +import { OrcaRouterService } from "./orcarouter-service.ts" import { ServerProxyService } from "./server-proxy-service.ts" import { config } from "../../config/google.config.ts" import { minimaxConfig } from "../../config/minimax.config.ts" import { openRouterConfig } from "../../config/openrouter.config.ts" import { nvidiaConfig } from "../../config/nvidia.config.ts" import { mergedevConfig } from "../../config/mergedev.config.ts" +import { orcarouterConfig } from "../../config/orcarouter.config.ts" -export type ModelProvider = "supercode" | "google" | "minimax" | "openrouter" | "nvidia" | "concentrateai" | "mergedev" +export type ModelProvider = "supercode" | "google" | "minimax" | "openrouter" | "nvidia" | "concentrateai" | "mergedev" | "orcarouter" export type ConnectionType = "direct" | "proxy" @@ -47,6 +49,7 @@ export const providerMeta: Record string> = { @@ -57,6 +60,7 @@ const providerConfigs: Record string> = { nvidia: () => process.env.NVIDIA_BYOK_PROD_KEY || process.env.NVIDIA_BYOK_DEV_KEY || nvidiaConfig.apiKey, concentrateai: () => process.env.CONCENTRATE_BYOK_PROD_KEY || process.env.CONCENTRATE_BYOK_DEV_KEY || process.env.CONCENTRATEAI_API_KEY || "", mergedev: () => process.env.MERGE_DEV_BYOK_PROD_KEY || process.env.MERGE_DEV_BYOK_DEV_KEY || mergedevConfig.apiKey, + orcarouter: () => process.env.ORCAROUTER_BYOK_PROD_KEY || process.env.ORCAROUTER_BYOK_DEV_KEY || orcarouterConfig.apiKey, } export function createProvider(provider: ModelProvider, model?: string): AIProvider { @@ -142,6 +146,17 @@ export function createProvider(provider: ModelProvider, model?: string): AIProvi svc.sendMessage(messages, onChunk, tools, onToolCall, signal, onReasoning, onToolResult, onStepFinish), } } + case "orcarouter": { + const svc = new OrcaRouterService(model) + return { + name: "orcarouter", + modelName: svc.modelName, + connectionType: "direct", + model: svc.model, + sendMessage: (messages, onChunk, tools, onToolCall, signal, onReasoning, onToolResult, onStepFinish) => + svc.sendMessage(messages, onChunk, tools, onToolCall, signal, onReasoning, onToolResult, onStepFinish), + } + } default: { throw new Error(`Provider "${provider}" is paused or unavailable`) } diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/connect.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/connect.ts index 3294e9e..a58651e 100644 --- a/apps/supercode-cli/server/src/cli/commands/slashCommands/connect.ts +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/connect.ts @@ -15,6 +15,7 @@ const PROVIDERS: Array<{ value: ModelProvider; label: string; hint: string; link { value: "google", label: "Google Gemini", hint: "gemini-2.5 models", link: "https://aistudio.google.com/apikey" }, { value: "openrouter", label: "OpenRouter", hint: "multi-provider access", link: "https://openrouter.ai/keys" }, { value: "nvidia", label: "NVIDIA NIM", hint: "free NVIDIA hosted models", link: "https://build.nvidia.com/explore/discover" }, + { value: "orcarouter", label: "OrcaRouter", hint: "multi-provider router", link: "https://orcarouter.ai" }, ] export async function connectProvider(): Promise<{ type: "connect"; provider?: ModelProvider; model?: string }> { diff --git a/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts b/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts index 2b7ba6d..94f24c9 100644 --- a/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts +++ b/apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts @@ -21,12 +21,14 @@ const SECTION_GOOGLE = "__section_google__" const SECTION_MINIMAX = "__section_minimax__" const SECTION_NVIDIA = "__section_nvidia__" const SECTION_OPENROUTER = "__section_openrouter__" +const SECTION_ORCAROUTER = "__section_orcarouter__" export const ALL_SECTIONS = new Set([ SECTION_CLOUD, SECTION_BYOK, SECTION_CONCENTRATEAI, SECTION_MERGEDEV, SECTION_GOOGLE, SECTION_MINIMAX, SECTION_NVIDIA, SECTION_OPENROUTER, + SECTION_ORCAROUTER, ]) const SECTION_LABELS: Record = { @@ -38,6 +40,7 @@ const SECTION_LABELS: Record = { [SECTION_MINIMAX]: "MiniMax", [SECTION_NVIDIA]: "NVIDIA NIM", [SECTION_OPENROUTER]: "OpenRouter", + [SECTION_ORCAROUTER]: "OrcaRouter", } const isMainSection = (v: string) => v === SECTION_CLOUD || v === SECTION_BYOK @@ -164,6 +167,31 @@ export const BYOK_MODELS: ModelEntry[] = [ { value: "minimax/minimax-m3", label: "MiniMax M3", provider: "openrouter", cost: "3.0x", desc: "Via OpenRouter" }, { value: "z-ai/glm-5.1", label: "GLM 5.1", provider: "openrouter", cost: "1.0x", desc: "Via OpenRouter" }, { value: "moonshotai/kimi-k2.6", label: "Kimi K2.6", provider: "openrouter", cost: "1.5x", desc: "Via OpenRouter" }, + + // ── OrcaRouter ────────────────────────────────────────────── + { value: SECTION_ORCAROUTER, label: "OrcaRouter", provider: "orcarouter", cost: "", desc: "" }, + { value: "anthropic/claude-opus-4.8", label: "Claude Opus 4.8", provider: "orcarouter", cost: "", desc: "Deep reasoning" }, + { value: "anthropic/claude-opus-4", label: "Claude Opus 4", provider: "orcarouter", cost: "", desc: "Top-tier reasoning" }, + { value: "anthropic/claude-sonnet-4.5", label: "Claude Sonnet 4.5", provider: "orcarouter", cost: "", desc: "Latest sonnet" }, + { value: "anthropic/claude-sonnet-4", label: "Claude Sonnet 4", provider: "orcarouter", cost: "", desc: "Balanced" }, + { value: "anthropic/claude-3.5-haiku", label: "Claude 3.5 Haiku", provider: "orcarouter", cost: "", desc: "Fast & cheap" }, + { value: "openai/gpt-4o", label: "GPT-4o", provider: "orcarouter", cost: "", desc: "OpenAI flagship" }, + { value: "openai/gpt-4o-mini", label: "GPT-4o Mini", provider: "orcarouter", cost: "", desc: "Cheap & fast" }, + { value: "openai/gpt-4.1", label: "GPT-4.1", provider: "orcarouter", cost: "", desc: "Latest GPT" }, + { value: "openai/o3-mini", label: "o3-mini", provider: "orcarouter", cost: "", desc: "Reasoning mini" }, + { value: "openai/o4-mini", label: "o4-mini", provider: "orcarouter", cost: "", desc: "Reasoning v4 mini" }, + { value: "grok/grok-4.5", label: "Grok 4.5", provider: "orcarouter", cost: "", desc: "xAI latest" }, + { value: "grok/grok-3", label: "Grok 3", provider: "orcarouter", cost: "", desc: "xAI flagship" }, + { value: "grok/grok-3-mini", label: "Grok 3 Mini", provider: "orcarouter", cost: "", desc: "Compact Grok" }, + { value: "deepseek/deepseek-chat", label: "DeepSeek Chat", provider: "orcarouter", cost: "", desc: "Fast & capable" }, + { value: "deepseek/deepseek-v3", label: "DeepSeek V3", provider: "orcarouter", cost: "", desc: "DeepSeek flagship" }, + { value: "deepseek/deepseek-reasoner", label: "DeepSeek Reasoner", provider: "orcarouter", cost: "", desc: "Reasoning model" }, + { value: "meta-llama/llama-4-maverick", label: "Llama 4 Maverick", provider: "orcarouter", cost: "", desc: "Latest Llama" }, + { value: "z-ai/glm-5.2", label: "GLM 5.2", provider: "orcarouter", cost: "", desc: "Latest GLM" }, + { value: "kimi/kimi-k3", label: "Kimi K3", provider: "orcarouter", cost: "", desc: "Moonshot latest" }, + { value: "kimi/kimi-k2.6", label: "Kimi K2.6", provider: "orcarouter", cost: "", desc: "Long context" }, + { value: "minimax/minimax-m3", label: "MiniMax M3", provider: "orcarouter", cost: "", desc: "Fast & smart" }, + { value: "orcarouter/auto", label: "OrcaRouter Auto", provider: "orcarouter", cost: "0x", desc: "Auto-pick cheapest" }, ] export const MODELS: ModelEntry[] = [ @@ -608,6 +636,6 @@ export function formatModelChange(p: ModelProvider, m?: string): string { p === "concentrateai" ? "ConcentrateAI" : p === "google" ? "Gemini" : p === "nvidia" ? "NVIDIA" : p === "minimax" ? "MiniMax" : - p === "mergedev" ? "Merge Dev" : "OpenRouter" + p === "mergedev" ? "Merge Dev" : p === "orcarouter" ? "OrcaRouter" : "OpenRouter" return `${label}${m ? ` · ${m}` : ""}` } diff --git a/apps/supercode-cli/server/src/config/orcarouter.config.ts b/apps/supercode-cli/server/src/config/orcarouter.config.ts new file mode 100644 index 0000000..7b8c3d8 --- /dev/null +++ b/apps/supercode-cli/server/src/config/orcarouter.config.ts @@ -0,0 +1,6 @@ +export const orcarouterConfig = { + get apiKey() { return process.env.ORCAROUTER_API_KEY || "" }, + get model() { return process.env.ORCAROUTER_MODEL || "openai/gpt-4o-mini" }, + baseUrl: "https://api.orcarouter.ai/v1", + get maxTokens() { return Number(process.env.ORCAROUTER_MAX_TOKENS) || 128000 }, +} diff --git a/apps/supercode-cli/server/src/lib/cli-config.ts b/apps/supercode-cli/server/src/lib/cli-config.ts index 28dc5a5..2cc2985 100644 --- a/apps/supercode-cli/server/src/lib/cli-config.ts +++ b/apps/supercode-cli/server/src/lib/cli-config.ts @@ -63,6 +63,7 @@ const API_KEY_ENV_MAP: Record = { nvidia: "NVIDIA_API_KEY", concentrateai: "CONCENTRATEAI_API_KEY", mergedev: "MERGE_DEV_API_KEY", + orcarouter: "ORCAROUTER_API_KEY", } export function getProviderApiKeys(): Partial> { @@ -103,6 +104,7 @@ const BYOK_ENV_OVERRIDES: Partial = { "anthropic/claude-opus-4-8": { inputPrice: 5.00, outputPrice: 25.00, cachedPrice: 0.50 }, "anthropic/claude-opus-4-7": { inputPrice: 5.00, outputPrice: 25.00, cachedPrice: 0.50 }, "openai/gpt-5.5": { inputPrice: 5.00, outputPrice: 30.00, cachedPrice: 0.50 }, + "anthropic/claude-sonnet-4.6": { inputPrice: 3.00, outputPrice: 15.00, cachedPrice: 0.30 }, + "anthropic/claude-opus-4.7": { inputPrice: 5.00, outputPrice: 25.00, cachedPrice: 0.50 }, + "deepseek/deepseek-chat": { inputPrice: 0.15, outputPrice: 0.60, cachedPrice: 0 }, + "deepseek/deepseek-reasoner": { inputPrice: 0.50, outputPrice: 2.00, cachedPrice: 0 }, + "grok/grok-4-fast-reasoning": { inputPrice: 1.00, outputPrice: 5.00, cachedPrice: 0 }, + "orcarouter/auto": { inputPrice: 0, outputPrice: 0, cachedPrice: 0 }, } export function computeCost(model: string, inputTokens: number, outputTokens: number, cachedInputTokens: number): number { @@ -58,6 +64,7 @@ export function getProviderDisplayNameFromRaw(raw: string): string { nvidia: "NVIDIA", minimax: "MiniMax", mergedev: "Merge Dev", + orcarouter: "OrcaRouter", } return map[raw] ?? raw } @@ -69,6 +76,7 @@ export const PROVIDER_COLORS: Record = { nvidia: "#75e02e", minimax: "#06b6d4", mergedev: "#f59e0b", + orcarouter: "#2563eb", other: "#6b7280", } diff --git a/apps/web/app/(pages)/partnerships/[company]/page.tsx b/apps/web/app/(pages)/partnerships/[company]/page.tsx index 90988d6..27e3c35 100644 --- a/apps/web/app/(pages)/partnerships/[company]/page.tsx +++ b/apps/web/app/(pages)/partnerships/[company]/page.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next" import Link from "next/link" import { notFound } from "next/navigation" +import { Suspense } from "react" +import { Tweet } from "react-tweet" import { ArrowLeft, ArrowUpRight, Quote } from "lucide-react" import Navbar from "@/components/homepage/navbar" import Footer from "@/components/homepage/footer" @@ -62,16 +64,24 @@ export default async function PartnershipDetailPage({ params }: Props) { {/* Hero */}
-
+
{partner.logoSrc ? ( - {partner.name} + {partner.name} ) : ( partner.logo )}

- {partner.name} + + {partner.name} + +

{partner.tagline} @@ -126,6 +136,20 @@ export default async function PartnershipDetailPage({ params }: Props) {

+ {/* Tweet */} + {partner.tweetId && ( +
+

+ $ Public Launch +

+ Loading tweet...
}> +
+ +
+ + + )} + {/* Results */}

@@ -154,8 +178,7 @@ export default async function PartnershipDetailPage({ params }: Props) { Ready to ship faster?

diff --git a/apps/web/app/(pages)/partnerships/page.tsx b/apps/web/app/(pages)/partnerships/page.tsx index 835f7b8..3ec11ff 100644 --- a/apps/web/app/(pages)/partnerships/page.tsx +++ b/apps/web/app/(pages)/partnerships/page.tsx @@ -51,9 +51,9 @@ export default function PartnershipsPage() { className="group relative border border-border rounded-lg p-8 hover:border-primary/40 transition-colors duration-200 bg-card/50 hover:bg-card/80" >
-
+
{partner.logoSrc ? ( - {partner.name} + {partner.name} ) : ( partner.logo )} diff --git a/apps/web/app/not-found.tsx b/apps/web/app/not-found.tsx new file mode 100644 index 0000000..2a35b1f --- /dev/null +++ b/apps/web/app/not-found.tsx @@ -0,0 +1,88 @@ +"use client" + +import React, { useState, useEffect } from "react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { motion } from "framer-motion" +import Navbar from "@/components/homepage/navbar" +import Footer from "@/components/homepage/footer" + +const EASE = [0.23, 1, 0.32, 1] as const + +export default function NotFound() { + const pathname = usePathname() + const [showCursor, setShowCursor] = useState(true) + const [showHome, setShowHome] = useState(false) + + useEffect(() => { + const cursor = setInterval(() => setShowCursor((c) => !c), 530) + const home = setTimeout(() => setShowHome(true), 800) + return () => { + clearInterval(cursor) + clearTimeout(home) + } + }, []) + + return ( +
+
+
+ + + +
+ +
+ $ Error 404 +
+ +
+

+ $ + find --path="{pathname}" +

+

+ $ + + error: no resource at " + {pathname} + " + +

+
+ $ + cd + +
+
+ + + + + → + + + Return to home + + + +
+
+ +
+
+ ) +} diff --git a/apps/web/components/homepage/hero.tsx b/apps/web/components/homepage/hero.tsx index eb37dfe..c3e7924 100644 --- a/apps/web/components/homepage/hero.tsx +++ b/apps/web/components/homepage/hero.tsx @@ -45,7 +45,7 @@ const HeroSection = () => { {/* Version tag */}
- v0.1.77-beta + v0.1.77-beta live
diff --git a/apps/web/components/homepage/partnerships-section.tsx b/apps/web/components/homepage/partnerships-section.tsx index 31684d5..3ed5c65 100644 --- a/apps/web/components/homepage/partnerships-section.tsx +++ b/apps/web/components/homepage/partnerships-section.tsx @@ -17,7 +17,7 @@ const PartnershipsSection = () => { $ Partnerships

- Real teams shipping with Supercode + Built together

{ href={`/partnerships/${partner.slug}`} className="group border text-[#A1A1AA] border-border rounded-lg p-6 hover:border-primary/40 transition-colors duration-200 bg-card/30 hover:bg-card/60" > -
+
{partner.logoSrc ? ( - {partner.name} + {partner.name} ) : ( partner.logo )} diff --git a/apps/web/data/partnerships.ts b/apps/web/data/partnerships.ts index 50b3b6d..6290d56 100644 --- a/apps/web/data/partnerships.ts +++ b/apps/web/data/partnerships.ts @@ -1,6 +1,8 @@ export interface Partner { slug: string name: string + website: string + tweetId?: string logo: string logoSrc?: string tagline: string @@ -14,8 +16,10 @@ export interface Partner { export const partners: Partner[] = [ { + tweetId: "2074153899997745276", slug: "concentrateai", name: "Concentrate AI", + website: "https://concentrate.ai", logo: "CA", logoSrc: "/concentrate-ai-black-symbol.svg", description: "A strategic partnership bringing together Supercode's AI code generation with Concentrate AI's LLM API gateway infrastructure.", @@ -32,12 +36,102 @@ export const partners: Partner[] = [ ], solution: [ "Supercode partnered with Concentrate AI to integrate their enterprise-grade LLM API gateway as the backbone of Supercode's AI infrastructure. Concentrate AI's gateway handles request routing across multiple LLM providers (OpenAI, Anthropic, Groq), automatic failover, token usage optimization, and real-time cost tracking — all abstracted behind a single, unified API.", - "In return, Concentrate AI adopted Supercode across their own engineering team to accelerate development of their gateway platform. Their engineers use Supercode to rapidly prototype new routing logic, generate integration tests for provider APIs, and maintain consistent code quality across their growing codebase. It's a true partnership — each company empowers the other's core product.", + "This partnership means Concentrate AI's gateway infrastructure powers Supercode's AI requests, while Concentrate AI benefits from close collaboration on the gateway features their LLM-forward customers need most. It's a true partnership — each company empowers the other's core product.", ], results: [ { text: "Supercode achieved enterprise-grade LLM infrastructure without building it in-house, powered by Concentrate AI's gateway", metric: "Zero-infra AI" }, - { text: "Concentrate AI's engineering team ships gateway features 3x faster using Supercode for development", metric: "3x faster shipping" }, + { text: "Tight collaboration with Concentrate AI ensures our gateway integration stays ahead of LLM API changes", metric: "Always in sync" }, { text: "Users get reliable, low-latency code generation with automatic failover across multiple LLM providers", metric: "99.9% uptime" }, ], }, + { + tweetId: "", + slug: "mergedev", + name: "Merge", + website: "https://merge.dev", + logo: "MD", + logoSrc: "/mergedev.jpeg", + description: + "Supercode's AI coding agents connect to 200+ developer tools through Merge's Agent Handler, giving AI governed access to the entire development workflow.", + tagline: "Connective infrastructure for production AI", + stat: { value: "200+", label: "Integrations available through Merge's Unified API" }, + quote: { + text: "Supercode's AI agents needed to interact with the same tools developers use every day — GitHub, Jira, Linear, Slack. Merge's Agent Handler gave us governed, authenticated access to hundreds of integrations out of the box. It's the infrastructure layer that makes AI-assisted development actually practical.", + author: "Yash Dewasthale", + role: "Founder, Supercode", + }, + challenge: [ + "For Supercode's AI coding agents to be truly useful, they needed to interact with the tools developers already use — opening PRs on GitHub, creating tickets in Linear or Jira, posting updates in Slack, managing deployments on Vercel. Building and maintaining individual integrations for every tool would have been a massive, ongoing engineering effort.", + "Each integration required OAuth flows, API versioning, rate limit handling, pagination, error handling, and ongoing maintenance as APIs evolved. What Supercode needed was a unified connectivity layer that abstracted away the complexity of individual tool integrations — so agents could focus on being helpful, not on plumbing.", + ], + solution: [ + "Supercode integrated Merge's Agent Handler and Unified API as the connectivity backbone for AI agent tool access. Merge handles authentication, rate limiting, pagination, and API versioning across 200+ developer tools — all behind a single, consistent API. Supercode agents can now read issues, create PRs, review code, post messages, and trigger deployments across any supported tool without handling a single integration detail.", + "This partnership means Supercode agents natively support Merge's 200+ integrations out of the box, while Merge gains deep insight into how AI coding agents consume their API — driving improvements that benefit their entire ecosystem. Each company builds what the other needs — Merge owns the connectivity infrastructure, Supercode owns the AI agent experience.", + ], + results: [ + { text: "Supercode agents access 200+ developer tools through a single integration, not 200 individual ones", metric: "One integration" }, + { text: "Deep integration with Merge's Unified API removes the need to build and maintain individual tool connectors", metric: "Zero connector maintenance" }, + { text: "Users get governed, authenticated agent actions with full audit trails — no security compromises", metric: "Full audit trail" }, + ], + }, + { + tweetId: "", + slug: "dodopayments", + name: "Dodo Payments", + website: "https://dodopayments.com", + logo: "DP", + logoSrc: "/ddp-logo.png", + description: + "Supercode uses Dodo Payments for subscription billing — powering checkout, recurring payments, and license fulfillment for Pro and Ultra tiers.", + tagline: "Developer-friendly payment infrastructure for digital products", + stat: { value: "<$1M", label: "Processed in subscription revenue through Dodo Payments" }, + quote: { + text: "We needed a payment stack that just worked — subscriptions, licensing, global tax compliance — without building a fintech company inside a dev tool company. Dodo Payments gave us a single API for checkout, recurring billing, and webhook-driven license fulfillment. It turned a months-long project into a weekend integration.", + author: "Yash Dewasthale", + role: "Founder, Supercode", + }, + challenge: [ + "Supercode needed to monetize its Pro and Ultra tiers with subscription billing — but payment infrastructure is a project that never ends. Beyond basic checkout, there were recurring billing cycles, dunning management, license key generation and validation, global tax compliance (VAT, GST, sales tax), multi-currency support, and customer portal access for users to manage their own subscriptions.", + "Building this in-house would have diverted months of engineering time away from Supercode's core AI product. What was needed was a payment partner that understood SaaS billing for developer tools — with a clean API, webhook-driven architecture, and built-in compliance for selling software globally.", + ], + solution: [ + "Supercode integrated Dodo Payments as its billing infrastructure. Dodo's Checkout Sessions handle one-time and subscription purchases with a hosted, localized checkout experience. Webhooks drive the entire post-payment workflow — on `payment.succeeded`, Supercode generates and delivers license keys; on `subscription.renewed`, it extends access; on `subscription.cancelled`, it gracefully degrades the tier. Dodo's Better-Auth plugin ties user authentication to subscription status seamlessly.", + "This partnership means Dodo Payments handles all the payment complexity — checkout, recurring billing, tax compliance — while Supercode focuses on AI. Dodo benefits from being the payment backbone for a fast-growing AI dev tool, and their team gets direct feedback on what AI-native SaaS businesses need from their payment infrastructure. It's a partnership where Supercode handles the AI, Dodo handles the payments, and both teams win.", + ], + results: [ + { text: "Supercode launched subscription billing in days instead of months with Dodo's checkout sessions and webhooks", metric: "Days to launch" }, + { text: "Automatic VAT, GST, and sales tax handling for global customers — zero compliance overhead", metric: "Global compliance" }, + { text: "Seamless webhook-driven billing flow — from checkout to license key delivery to subscription management", metric: "End-to-end billing" }, + ], + }, + { + tweetId: "", + slug: "orcarouter", + name: "OrcaRouter", + website: "https://orcarouter.ai", + logo: "OR", + logoSrc: "/orcarouter-logo.jpg", + description: + "Supercode integrates OrcaRouter's intelligent LLM routing to deliver users the best model for every task — balancing speed, cost, and capability automatically.", + tagline: "Intelligent routing for the AI-powered developer", + stat: { value: "50+", label: "Models available through OrcaRouter's routing layer" }, + quote: { + text: "Not every coding task needs the same model. Simple edits should go through a cheap, fast model — complex reasoning deserves the best. OrcaRouter's intelligent routing lets Supercode automatically match each user request to the optimal model, giving users the best experience without manual model selection.", + author: "Yash Dewasthale", + role: "Founder, Supercode", + }, + challenge: [ + "Supercode's AI coding agents support a wide range of tasks — from quick code completions and inline edits to deep architectural reasoning and multi-file refactors. Each type of task has different requirements: simple completions need low latency and low cost, while complex reasoning tasks need the most capable models available. Asking users to manually pick the right model for each task creates friction and leads to suboptimal choices — too expensive for simple tasks, or not capable enough for hard ones.", + "What Supercode needed was an intelligent routing layer that could automatically dispatch each request to the optimal model based on the task's complexity, the user's latency expectations, and cost considerations — without the user ever needing to think about which model is running underneath.", + ], + solution: [ + "Supercode integrated OrcaRouter as its intelligent model routing layer. OrcaRouter evaluates each incoming request and routes it to the most appropriate model across a portfolio of 50+ models from OpenAI, Anthropic, Google, Meta, DeepSeek, and more. Simple code completions go through fast, economical models; complex refactoring and architectural tasks are routed to the most capable reasoning models — all automatically, with zero user configuration.", + "This partnership means OrcaRouter's intelligent routing becomes the brain behind Supercode's model selection — automatically dispatching each request to the optimal model. OrcaRouter gains a demanding AI coding use case that pushes their routing algorithms to handle diverse task types, from quick completions to deep reasoning. It's a mutually reinforcing partnership — Supercode provides the AI agent experience, OrcaRouter provides the intelligence layer that makes it work optimally for every task.", + ], + results: [ + { text: "Users get optimal model selection automatically — fast models for simple tasks, capable models for complex ones", metric: "Auto-optimized" }, + { text: "OrcaRouter handles model routing so Supercode never has to — automatic selection based on task complexity and cost", metric: "Routing handled" }, + { text: "Cost-efficient AI assistance with intelligent routing that matches model capability to task complexity", metric: "Smart cost allocation" }, + ], + }, ] diff --git a/apps/web/package.json b/apps/web/package.json index 04047d9..577f93e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -78,6 +78,7 @@ "react-hook-form": "^7.70.0", "react-resizable-panels": "^4.2.0", "react-simple-maps": "^3.0.0", + "react-tweet": "^3.3.1", "recharts": "2.15.4", "resend": "^6.14.0", "sonner": "^2.0.7", diff --git a/apps/web/public/ddp-logo.png b/apps/web/public/ddp-logo.png new file mode 100644 index 0000000..f97fb33 Binary files /dev/null and b/apps/web/public/ddp-logo.png differ diff --git a/apps/web/public/mergedev.jpeg b/apps/web/public/mergedev.jpeg new file mode 100644 index 0000000..b81a1ab Binary files /dev/null and b/apps/web/public/mergedev.jpeg differ diff --git a/apps/web/public/orcarouter-logo.jpg b/apps/web/public/orcarouter-logo.jpg new file mode 100644 index 0000000..53e39e9 Binary files /dev/null and b/apps/web/public/orcarouter-logo.jpg differ diff --git a/bun.lock b/bun.lock index e8bf2c8..434c0b0 100644 --- a/bun.lock +++ b/bun.lock @@ -91,7 +91,7 @@ }, "apps/supercode-cli/server": { "name": "supercode-cli", - "version": "0.1.66", + "version": "0.1.79", "bin": { "supercode": "dist/main.js", }, @@ -238,6 +238,7 @@ "react-hook-form": "^7.70.0", "react-resizable-panels": "^4.2.0", "react-simple-maps": "^3.0.0", + "react-tweet": "^3.3.1", "recharts": "2.15.4", "resend": "^6.14.0", "sonner": "^2.0.7", @@ -3051,6 +3052,8 @@ "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + "react-tweet": ["react-tweet@3.3.1", "", { "dependencies": { "@swc/helpers": "^0.5.3", "clsx": "^2.0.0", "swr": "^2.2.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-0Gj1YgBTe1K85NBMoWBlUc17Rdc67gIJLlacrVgj+3jWIoXpFfxPdZ1U5OnWkkF9zxhphqs2pY1i//JBfP3Qog=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], @@ -3287,6 +3290,8 @@ "swagger2openapi": ["swagger2openapi@7.0.8", "", { "dependencies": { "call-me-maybe": "^1.0.1", "node-fetch": "^2.6.1", "node-fetch-h2": "^2.3.0", "node-readfiles": "^0.2.0", "oas-kit-common": "^1.0.8", "oas-resolver": "^2.5.6", "oas-schema-walker": "^1.1.5", "oas-validator": "^5.0.8", "reftools": "^1.1.9", "yaml": "^1.10.0", "yargs": "^17.0.1" }, "bin": { "swagger2openapi": "swagger2openapi.js", "oas-validate": "oas-validate.js", "boast": "boast.js" } }, "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g=="], + "swr": ["swr@2.4.2", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-ej644Y2bvkIajfR32KGeSSdBXQW+ScjGjkybZgSE7kFpk9eGnV44XY9FJylXi+W75pavSX1PVNB57W5EbhGIYw=="], + "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],