Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hub/src/api/api-keys.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Hono } from 'hono'
import { createApiKey, listApiKeys, revokeApiKey } from '../db/dal'
import { hashToken } from '../ws/channel'
import { hashToken } from '../lib/crypto'
import { generateToken } from '../utils/token'

const apiKeys = new Hono()
Expand Down
2 changes: 1 addition & 1 deletion hub/src/api/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Hono } from 'hono'
import { z } from 'zod'
import { createPluginSession } from '../db/dal'
import { hashToken } from '../ws/channel'
import { hashToken } from '../lib/crypto'
import { generateToken } from '../utils/token'

const plugin = new Hono()
Expand Down
2 changes: 1 addition & 1 deletion hub/src/api/sessions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Hono } from 'hono'
import { z } from 'zod'
import { createSession, listSessions, getSession, deleteSession, updateSessionToken, markSessionDisconnected } from '../db/dal'
import { hashToken } from '../ws/channel'
import { hashToken } from '../lib/crypto'
import { getChannel } from '../ws/registry'
import { generateToken } from '../utils/token'

Expand Down
2 changes: 1 addition & 1 deletion hub/src/auth/api-key-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Context, Next } from 'hono'
import { verifyApiKey } from '../db/dal'
import { hashToken } from '../ws/channel'
import { hashToken } from '../lib/crypto'

export async function apiKeyMiddleware(c: Context, next: Next) {
const authHeader = c.req.header('Authorization')
Expand Down
12 changes: 12 additions & 0 deletions hub/src/lib/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Crypto helpers shared across hub modules.
// Kept dependency-free (Web Crypto only) so it can be imported from API routes,
// WS handlers, and middleware without pulling in channel/session code.

export async function hashToken(token: string): Promise<string> {
const encoder = new TextEncoder()
const data = encoder.encode(token)
const hash = await crypto.subtle.digest('SHA-256', data)
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
2 changes: 1 addition & 1 deletion hub/src/ws/agent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ServerWebSocket } from 'bun'
import { AgentInbound } from './agent-protocol'
import { verifyApiKey, findOrCreateAgentSession, updateSessionStatus as setSessionStatus, insertMessage, insertAssistantPlaceholder, appendToMessage, finalizeMessage, listSessions, getUserSystemPrompt, recentlyDisconnectedForProjectDir, updateSessionAgentInfo } from '../db/dal'
import { hashToken } from './channel'
import { hashToken } from '../lib/crypto'
import { generateToken } from '../utils/token'
import { registerChannel, unregisterChannel, getChannel, broadcastToSubscribers, broadcastToUser } from './registry'
import { verifyApiKeyWithCapability, upsertSupervisor, endRun, replaceSupervisorCommands } from '../db/supervisor-dal'
Expand Down
8 changes: 1 addition & 7 deletions hub/src/ws/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ChannelInbound } from './protocol'
import { verifyChannelToken, updateSessionStatus as setSessionStatus, insertMessage } from '../db/dal'
import { registerChannel, unregisterChannel, broadcastToSubscribers, broadcastToUser } from './registry'
import { listSessions } from '../db/dal'
import { hashToken } from '../lib/crypto'

const AUTH_TIMEOUT_MS = 5_000
const HEARTBEAT_INTERVAL_MS = 30_000
Expand Down Expand Up @@ -32,13 +33,6 @@ export function createChannelWsData(): ChannelWsData {
}
}

export async function hashToken(token: string): Promise<string> {
const encoder = new TextEncoder()
const data = encoder.encode(token)
const hash = await crypto.subtle.digest('SHA-256', data)
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('')
}

export function handleChannelOpen(ws: ServerWebSocket<ChannelWsData>) {
const data = ws.data
console.log('[channel] connection opened')
Expand Down
20 changes: 10 additions & 10 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import type { AuthUser } from './lib/auth.ts'

type Route = 'chat' | 'settings' | 'schedules'

function LoadingScreen() {
return (
<div className="flex items-center justify-center h-screen bg-[var(--bg-primary)]">
<div className="text-[var(--text-muted)]">Loading...</div>
</div>
)
}

function getRoute(): Route {
const hash = window.location.hash
// Legacy /#/supervisor → settings with supervisor tab
Expand Down Expand Up @@ -55,11 +63,7 @@ export default function App() {
}, [])

if (loading || needsSetup === null) {
return (
<div className="flex items-center justify-center h-screen bg-[var(--bg-primary)]">
<div className="text-[var(--text-muted)]">Loading...</div>
</div>
)
return <LoadingScreen />
}

if (needsSetup) {
Expand All @@ -72,11 +76,7 @@ export default function App() {

// Wait for profile to load before rendering gated routes
if (profileLoading || !profile) {
return (
<div className="flex items-center justify-center h-screen bg-[var(--bg-primary)]">
<div className="text-[var(--text-muted)]">Loading...</div>
</div>
)
return <LoadingScreen />
}

return (
Expand Down