Travis Fischer 2025-07-16 13:56:27 -04:00
rodzic b2f5397265
commit b41d4ff1f8
8 zmienionych plików z 140 dodań i 109 usunięć

Wyświetl plik

@ -1,5 +1,5 @@
import { sha256 } from '@agentic/platform-core'
export async function createConsumerApiKey(): Promise<string> {
return `sk-${sha256()}`
return `sk-${await sha256()}`
}

Wyświetl plik

@ -0,0 +1,76 @@
'use client'
import { useState } from 'react'
import { ActiveLink } from '@/components/active-link'
import { useAgentic } from '@/components/agentic-provider'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
export function AuthenticatedHeader() {
const ctx = useAgentic()
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false)
if (!ctx?.isAuthenticated) {
return null
}
return (
<DropdownMenu
open={isDropdownMenuOpen}
onOpenChange={setIsDropdownMenuOpen}
>
<DropdownMenuTrigger asChild>
<Avatar className='cursor-pointer'>
<AvatarImage src={ctx.api.authSession!.user.image} />
<AvatarFallback>
{ctx.api.authSession!.user.username?.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem asChild>
<ActiveLink href='/app' onClick={() => setIsDropdownMenuOpen(false)}>
Dashboard
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<ActiveLink
href='/app/projects'
onClick={() => setIsDropdownMenuOpen(false)}
>
My Projects
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<ActiveLink
href='/app/consumers'
onClick={() => setIsDropdownMenuOpen(false)}
>
My Subscriptions
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<ActiveLink
href='/logout'
onClick={() => setIsDropdownMenuOpen(false)}
>
Logout
</ActiveLink>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}

Wyświetl plik

@ -1,98 +0,0 @@
'use client'
import { useState } from 'react'
import { ActiveLink } from '@/components/active-link'
import { useAgentic } from '@/components/agentic-provider'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { Button } from '../ui/button'
export function DynamicHeader() {
const ctx = useAgentic()
const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false)
return (
<>
{ctx?.isAuthenticated ? (
<DropdownMenu
open={isDropdownMenuOpen}
onOpenChange={setIsDropdownMenuOpen}
>
<DropdownMenuTrigger asChild>
<Avatar className='cursor-pointer'>
<AvatarImage src={ctx.api.authSession!.user.image} />
<AvatarFallback>
{ctx.api.authSession!.user.username?.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem asChild>
<ActiveLink
href='/app'
onClick={() => setIsDropdownMenuOpen(false)}
>
Dashboard
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<ActiveLink
href='/app/projects'
onClick={() => setIsDropdownMenuOpen(false)}
>
My Projects
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<ActiveLink
href='/app/consumers'
onClick={() => setIsDropdownMenuOpen(false)}
>
My Subscriptions
</ActiveLink>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<ActiveLink
href='/logout'
onClick={() => setIsDropdownMenuOpen(false)}
>
Logout
</ActiveLink>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<>
<Button asChild variant='outline'>
<ActiveLink href='/login' className='whitespace-nowrap px-4! py-2!'>
Log in
</ActiveLink>
</Button>
<Button asChild variant='default'>
<ActiveLink
href='/signup'
className='whitespace-nowrap px-4! py-2!'
>
Sign up
</ActiveLink>
</Button>
</>
)}
</>
)
}

Wyświetl plik

@ -1,9 +1,15 @@
import Link from 'next/link'
import { ActiveLink } from '@/components/active-link'
import { DarkModeToggle } from '@/components/dark-mode-toggle'
import { Button } from '@/components/ui/button'
import { GitHubIcon } from '@/icons/github'
import { githubUrl } from '@/lib/config'
import { cn } from '@/lib/utils'
import { DynamicHeader } from './dynamic'
import { AuthenticatedHeader } from './authenticated-header'
import styles from './styles.module.css'
import { UnauthenticatedHeader } from './unauthenticated-header'
export function Header() {
return (
@ -41,9 +47,19 @@ export function Header() {
Docs
</ActiveLink>
<DarkModeToggle />
<div className='flex items-center h-full gap-2'>
<UnauthenticatedHeader />
<DynamicHeader />
<DarkModeToggle />
<Button variant='outline' size='icon' asChild>
<Link href={githubUrl} target='_blank' rel='noopener noreferrer'>
<GitHubIcon className='w-4 h-4' />
</Link>
</Button>
<AuthenticatedHeader />
</div>
</div>
</div>
</header>

Wyświetl plik

@ -0,0 +1,29 @@
'use client'
import { ActiveLink } from '@/components/active-link'
import { useAgentic } from '@/components/agentic-provider'
import { Button } from '@/components/ui/button'
export function UnauthenticatedHeader() {
const ctx = useAgentic()
if (ctx?.isAuthenticated) {
return null
}
return (
<>
<Button asChild variant='outline'>
<ActiveLink href='/login' className='whitespace-nowrap px-4! py-2!'>
Log in
</ActiveLink>
</Button>
<Button asChild variant='default'>
<ActiveLink href='/signup' className='whitespace-nowrap px-4! py-2!'>
Sign up
</ActiveLink>
</Button>
</>
)
}

Wyświetl plik

@ -30,6 +30,7 @@
"@sindresorhus/slugify": "catalog:",
"decircular": "catalog:",
"is-obj": "catalog:",
"ohash": "^2.0.11",
"parse-json": "catalog:",
"sort-keys": "catalog:",
"type-fest": "catalog:",

Wyświetl plik

@ -1426,6 +1426,9 @@ importers:
is-obj:
specifier: 'catalog:'
version: 3.0.0
ohash:
specifier: ^2.0.11
version: 2.0.11
parse-json:
specifier: 'catalog:'
version: 8.3.0

18
todo.md
Wyświetl plik

@ -7,12 +7,6 @@
- **api keys should go beyond 1:1 consumers**
- **currently not obvious how to get api key**
- marketplace public project detail page
- add breadcrumb nav: marketplace > @agentic > search
- add last published date somewhere
- tool input/output schemas; move `$schema` to the top
- break out into a few subcomponents; some can be server components
- mcp inspector
- improve private project page
- link to public page if published
- list deployments
@ -20,7 +14,7 @@
- marketplace index page
- add search / sorting
- replace render for api and/or add turbo for caching (too slow to deploy)
- create slack + notifications
- create slack or discord + notifications
## TODO: Post-MVP
@ -113,6 +107,7 @@
- also add `@agentic/json-schema` to `createJsonSchema` parsing instead of current no-op
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
- currently difficult due to their use of non-standard json schemas
- add support for `crewai`
- validate example args against the tool's input schema during config validation
- add scroll appearance motion to hero animation
- add ts sdk examples to e2e tests
@ -131,3 +126,12 @@
- add support for enterprise / custom / contact us pricing
- consider changing homepage hero CTA to include publishing
- docs: add notes about constraints on mcp origin servers (static tools)
- analytics dashboard
- UX onboarding
- visual pricing plan config + previews
- marketplace public project detail page
- add breadcrumb nav: marketplace > @agentic > search
- add last published date somewhere
- tool input/output schemas; move `$schema` to the top
- break out into a few subcomponents; some can be server components
- mcp inspector