← back to jpdlr__hydra

Function bodies 224 total

All specs Real LLM only Function bodies
HeadlessOrchestrator.start method · typescript · L54-L130 (77 LOC)
electron/headless/HeadlessOrchestrator.ts
  start(payload: StartHeadlessRunPayload): HeadlessRun {
    const runId = randomUUID().slice(0, 8)
    const startedAt = new Date().toISOString()
    const logPath = join(this.baseDir, `${runId}${LOG_EXTENSION}`)
    const metaPath = join(this.baseDir, `${runId}${META_EXTENSION}`)

    const state: HeadlessRun = {
      id: runId,
      prompt: payload.prompt,
      projectDir: payload.projectDir,
      provider: payload.provider,
      model: payload.model,
      reasoningEffort: payload.reasoningEffort,
      resumeSessionId: payload.resumeSessionId ?? null,
      status: 'running',
      startedAt,
      endedAt: null,
      sessionId: payload.resumeSessionId ?? null,
      error: null
    }

    const managed: ManagedHeadlessRun = {
      state,
      process: null,
      logPath,
      metaPath,
      stdoutBuffer: '',
      stderrBuffer: ''
    }
    this.runs.set(runId, managed)
    writeFileSync(logPath, '', 'utf-8')
    this.persistRun(managed)

    const provider = getProvid
HeadlessOrchestrator.writeFileSync method · typescript · L84-L125 (42 LOC)
electron/headless/HeadlessOrchestrator.ts
    writeFileSync(logPath, '', 'utf-8')
    this.persistRun(managed)

    const provider = getProvider(payload.provider)
    const args = provider.buildHeadlessArgs(payload.model, payload.prompt, payload.resumeSessionId ?? null, payload.reasoningEffort)

    try {
      const child = spawn(provider.command, args, {
        cwd: payload.projectDir,
        env: {
          ...process.env,
          FORCE_COLOR: '0'
        }
      })
      managed.process = child

      child.stdout.on('data', (chunk: Buffer | string) => {
        this.handleStdout(managed, chunk.toString('utf-8'))
      })

      child.stderr.on('data', (chunk: Buffer | string) => {
        const text = chunk.toString('utf-8')
        managed.stderrBuffer += text
        this.emitRunEvent({ runId, data: text })
      })

      child.on('exit', (code) => {
        if (managed.state.status === 'canceled') {
          this.finalizeRun(managed, 'canceled', null)
          return
        }
        if (code === 0) {
        
HeadlessOrchestrator.list method · typescript · L132-L157 (26 LOC)
electron/headless/HeadlessOrchestrator.ts
  list(options: ListHeadlessRunsOptions = {}): HeadlessRun[] {
    const query = options.query?.trim().toLowerCase()
    const statusFilter = options.status && options.status !== 'all' ? options.status : null

    const filtered = Array.from(this.runs.values())
      .map((run) => ({ ...run.state }))
      .filter((run) => {
        if (statusFilter && run.status !== statusFilter) return false
        if (!query) return true
        return (
          run.id.toLowerCase().includes(query) ||
          run.prompt.toLowerCase().includes(query) ||
          run.projectDir.toLowerCase().includes(query) ||
          run.model.toLowerCase().includes(query) ||
          run.status.toLowerCase().includes(query) ||
          (run.sessionId ?? '').toLowerCase().includes(query) ||
          (run.error ?? '').toLowerCase().includes(query)
        )
      })
      .sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt))

    if (typeof options.limit === 'number' && options.limit > 0) {
   
HeadlessOrchestrator.getLog method · typescript · L164-L206 (43 LOC)
electron/headless/HeadlessOrchestrator.ts
  getLog(runId: string, options: HeadlessRunLogOptions = {}): HeadlessRunLogPayload | null {
    const managed = this.runs.get(runId)
    if (!managed) return null
    if (!existsSync(managed.logPath)) {
      return {
        runId,
        content: '',
        totalLines: 0,
        returnedLines: 0,
        truncated: false
      }
    }

    const tailLines =
      options.tailLines && options.tailLines > 0
        ? Math.floor(options.tailLines)
        : DEFAULT_LOG_TAIL_LINES
    const maxChars =
      options.maxChars && options.maxChars > 0
        ? Math.floor(options.maxChars)
        : DEFAULT_LOG_MAX_CHARS

    const raw = readFileSync(managed.logPath, 'utf-8')
    const allLines = raw.length === 0 ? [] : raw.split('\n').filter((line) => line.length > 0)
    const totalLines = allLines.length
    const sliced = allLines.slice(Math.max(0, totalLines - tailLines))
    let content = sliced.join('\n')
    let truncated = totalLines > sliced.length

    if (content.length > max
HeadlessOrchestrator.cancel method · typescript · L208-L223 (16 LOC)
electron/headless/HeadlessOrchestrator.ts
  cancel(runId: string): boolean {
    const managed = this.runs.get(runId)
    if (!managed || !managed.process) return false
    managed.state.status = 'canceled'
    this.persistRun(managed)
    try {
      if (process.platform === 'win32') {
        managed.process.kill()
      } else {
        managed.process.kill('SIGTERM')
      }
      return true
    } catch {
      return false
    }
  }
HeadlessOrchestrator.handleStdout method · typescript · L225-L236 (12 LOC)
electron/headless/HeadlessOrchestrator.ts
  private handleStdout(managed: ManagedHeadlessRun, data: string): void {
    managed.stdoutBuffer += data
    const lines = managed.stdoutBuffer.split('\n')
    managed.stdoutBuffer = lines.pop() || ''

    for (const line of lines) {
      if (!line.trim()) continue
      this.appendLog(managed, line)
      this.captureSessionId(managed, line)
      this.emitRunEvent({ runId: managed.state.id, data: line })
    }
  }
HeadlessOrchestrator.captureSessionId method · typescript · L238-L252 (15 LOC)
electron/headless/HeadlessOrchestrator.ts
  private captureSessionId(managed: ManagedHeadlessRun, line: string): void {
    try {
      const parsed = JSON.parse(line) as Record<string, unknown>
      const session =
        (typeof parsed.session_id === 'string' && parsed.session_id) ||
        (typeof parsed.sessionId === 'string' && parsed.sessionId) ||
        null
      if (session) {
        managed.state.sessionId = session
        this.persistRun(managed)
      }
    } catch {
      // Non-JSON line, ignore.
    }
  }
Repobility · code-quality intelligence · https://repobility.com
HeadlessOrchestrator.appendLog method · typescript · L254-L260 (7 LOC)
electron/headless/HeadlessOrchestrator.ts
  private appendLog(managed: ManagedHeadlessRun, line: string): void {
    try {
      appendFileSync(managed.logPath, `${line}\n`, 'utf-8')
    } catch {
      // Best effort log persistence.
    }
  }
HeadlessOrchestrator.finalizeRun method · typescript · L262-L272 (11 LOC)
electron/headless/HeadlessOrchestrator.ts
  private finalizeRun(
    managed: ManagedHeadlessRun,
    status: HeadlessRunStatus,
    error: string | null
  ): void {
    managed.process = null
    managed.state.status = status
    managed.state.error = error
    managed.state.endedAt = new Date().toISOString()
    this.persistRun(managed)
  }
HeadlessOrchestrator.hydrateRunsFromDisk method · typescript · L278-L342 (65 LOC)
electron/headless/HeadlessOrchestrator.ts
  private hydrateRunsFromDisk(): void {
    const entries = readdirSync(this.baseDir, { withFileTypes: true })
      .filter((entry) => entry.isFile())
      .map((entry) => entry.name)

    const metaFiles = entries.filter((name) => name.endsWith(META_EXTENSION))
    for (const metaName of metaFiles) {
      const metaPath = join(this.baseDir, metaName)
      try {
        const raw = readFileSync(metaPath, 'utf-8')
        const parsed = JSON.parse(raw) as Partial<PersistedHeadlessRunFile>
        const run = parsed.run
        if (!run || !this.isHeadlessRun(run)) continue

        if (!run.provider) run.provider = 'claude'
        const logPath = join(this.baseDir, `${run.id}${LOG_EXTENSION}`)
        this.runs.set(run.id, {
          state: run,
          process: null,
          logPath,
          metaPath,
          stdoutBuffer: '',
          stderrBuffer: ''
        })
      } catch {
        // Ignore malformed persisted entries.
      }
    }

    const logFiles = entries.fi
HeadlessOrchestrator.discoverSessionIdFromLog method · typescript · L344-L365 (22 LOC)
electron/headless/HeadlessOrchestrator.ts
  private discoverSessionIdFromLog(logPath: string): string | null {
    try {
      const raw = readFileSync(logPath, 'utf-8')
      const lines = raw.split('\n')
      for (const line of lines) {
        if (!line.trim()) continue
        try {
          const parsed = JSON.parse(line) as Record<string, unknown>
          const session =
            (typeof parsed.session_id === 'string' && parsed.session_id) ||
            (typeof parsed.sessionId === 'string' && parsed.sessionId) ||
            null
          if (session) return session
        } catch {
          continue
        }
      }
      return null
    } catch {
      return null
    }
  }
HeadlessOrchestrator.persistRun method · typescript · L367-L377 (11 LOC)
electron/headless/HeadlessOrchestrator.ts
  private persistRun(managed: ManagedHeadlessRun): void {
    const payload: PersistedHeadlessRunFile = {
      schemaVersion: META_SCHEMA_VERSION,
      run: managed.state
    }
    try {
      writeFileSync(managed.metaPath, JSON.stringify(payload, null, 2), 'utf-8')
    } catch {
      // Best effort persistence.
    }
  }
HeadlessOrchestrator.isHeadlessRun method · typescript · L379-L405 (27 LOC)
electron/headless/HeadlessOrchestrator.ts
  private isHeadlessRun(value: unknown): value is HeadlessRun {
    if (!value || typeof value !== 'object') return false
    const run = value as Record<string, unknown>

    const validStatus =
      run.status === 'running' ||
      run.status === 'completed' ||
      run.status === 'errored' ||
      run.status === 'canceled'

    const validProviders = ['claude', 'codex']
    const validProvider = run.provider === undefined || validProviders.includes(run.provider as string)

    return (
      typeof run.id === 'string' &&
      typeof run.prompt === 'string' &&
      typeof run.projectDir === 'string' &&
      typeof run.model === 'string' &&
      validProvider &&
      (typeof run.resumeSessionId === 'string' || run.resumeSessionId === null) &&
      validStatus &&
      typeof run.startedAt === 'string' &&
      (typeof run.endedAt === 'string' || run.endedAt === null) &&
      (typeof run.sessionId === 'string' || run.sessionId === null) &&
      (typeof run.error === 'string
createWindow function · typescript · L42-L95 (54 LOC)
electron/main.ts
function createWindow(): void {
  const isMac = process.platform === 'darwin'
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    minWidth: 800,
    minHeight: 600,
    show: false,
    ...(isMac
      ? {
          titleBarStyle: 'hiddenInset' as const,
          trafficLightPosition: { x: 16, y: 14 }
        }
      : {}),
    backgroundColor: '#262624',
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: false // Required for node-pty IPC
    }
  })

  mainWindow.on('ready-to-show', () => {
    mainWindow?.show()
  })

  mainWindow.webContents.setWindowOpenHandler(({ url }) => {
    shell.openExternal(url)
    return { action: 'deny' }
  })

  // Load renderer
  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
  } else {
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
  }

  mainW
setupManagerWorkspace function · typescript · L4-L47 (44 LOC)
electron/mcp/manager-workspace.ts
export function setupManagerWorkspace(baseDir: string, port: number): string {
  const workspaceDir = join(baseDir, 'manager-workspace')
  mkdirSync(workspaceDir, { recursive: true })

  const mcpConfig = {
    mcpServers: {
      hydra: {
        type: 'http',
        url: `http://127.0.0.1:${port}/mcp`
      }
    }
  }
  writeFileSync(join(workspaceDir, '.mcp.json'), JSON.stringify(mcpConfig, null, 2))

  const instructions = `# Hydra Manager Agent

You are a **manager agent** with access to orchestrate other Claude agents running inside Hydra.

## Available MCP Tools

| Tool | Purpose |
|------|---------|
| \`hydra_list_agents\` | List all agents with their current status |
| \`hydra_create_agent\` | Create a new agent in a project directory |
| \`hydra_send_prompt\` | Send a prompt to a specific agent |
| \`hydra_get_output\` | Get recent output from an agent's terminal buffer |
| \`hydra_broadcast\` | Send the same prompt to all agents in a project |
| \`hydra_kill_agent\` | Kill
Powered by Repobility — scan your code at https://repobility.com
HydraMcpServer.start method · typescript · L33-L59 (27 LOC)
electron/mcp/McpServer.ts
  async start(): Promise<void> {
    const server = createServer((req, res) => {
      this.handleHttp(req, res).catch((err) => {
        console.error('[MCP] Request handler error:', err)
        if (!res.headersSent) {
          res.writeHead(500, { 'Content-Type': 'application/json' })
          res.end(JSON.stringify({ error: 'Internal server error' }))
        }
      })
    })

    this.port = await new Promise<number>((resolve, reject) => {
      server.listen(0, '127.0.0.1', () => {
        const addr = server.address()
        if (addr && typeof addr === 'object') {
          resolve(addr.port)
        } else {
          reject(new Error('Failed to get server address'))
        }
      })
      server.on('error', reject)
    })

    this.httpServer = server
    this.managerWorkspace = setupManagerWorkspace(this.userDataPath, this.port)
    this.error = null
  }
HydraMcpServer.reject method · typescript · L50-L71 (22 LOC)
electron/mcp/McpServer.ts
          reject(new Error('Failed to get server address'))
        }
      })
      server.on('error', reject)
    })

    this.httpServer = server
    this.managerWorkspace = setupManagerWorkspace(this.userDataPath, this.port)
    this.error = null
  }

  stop(): void {
    for (const [, entry] of this.sessions) {
      entry.transport.close().catch(() => {})
    }
    this.sessions.clear()

    if (this.httpServer) {
      this.httpServer.close()
      this.httpServer = null
    }
  }
HydraMcpServer.getStatus method · typescript · L73-L80 (8 LOC)
electron/mcp/McpServer.ts
  getStatus(): McpServerStatus {
    return {
      running: this.httpServer !== null,
      port: this.port,
      error: this.error,
      managerWorkspace: this.managerWorkspace
    }
  }
HydraMcpServer.handleHttp method · typescript · L82-L154 (73 LOC)
electron/mcp/McpServer.ts
  private async handleHttp(req: IncomingMessage, res: ServerResponse): Promise<void> {
    const url = new URL(req.url ?? '/', `http://127.0.0.1:${this.port}`)

    // ── Notification endpoints ──────────────────────────────────────────────

    if (url.pathname === '/notifications/stream' && req.method === 'GET') {
      this.handleNotificationStream(res)
      return
    }

    if (url.pathname === '/notifications' && req.method === 'GET') {
      const limitParam = url.searchParams.get('limit')
      const limit = limitParam ? Math.min(Math.max(parseInt(limitParam, 10) || 50, 1), 200) : 50
      const recent = this.notificationService?.getRecent(limit) ?? []
      res.writeHead(200, { 'Content-Type': 'application/json' })
      res.end(JSON.stringify(recent))
      return
    }

    if (url.pathname !== '/mcp') {
      res.writeHead(404, { 'Content-Type': 'application/json' })
      res.end(JSON.stringify({ error: 'Not found' }))
      return
    }

    // Handle DELETE for session 
HydraMcpServer.createSession method · typescript · L156-L184 (29 LOC)
electron/mcp/McpServer.ts
  private async createSession(): Promise<SessionEntry> {
    const server = new McpServer(
      { name: 'hydra', version: '0.1.0' },
      { capabilities: { logging: {} } }
    )

    this.registerTools(server)

    const entry: SessionEntry = { server, transport: null as unknown as StreamableHTTPServerTransport }

    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
      enableJsonResponse: true,
      onsessioninitialized: (sid: string) => {
        this.sessions.set(sid, entry)
      }
    })

    transport.onclose = () => {
      if (transport.sessionId) {
        this.sessions.delete(transport.sessionId)
      }
    }

    entry.transport = transport
    await server.connect(transport)

    return entry
  }
HydraMcpServer.async method · typescript · L191-L206 (16 LOC)
electron/mcp/McpServer.ts
      async () => {
        const agents = this.agentManager.list()
        const summary = agents.map((a) => ({
          id: a.id,
          name: a.name,
          status: a.status,
          model: a.model,
          projectDir: a.projectDir,
          isManager: a.isManager,
          yolo: a.yolo,
          sessionId: a.sessionId
        }))
        return {
          content: [{ type: 'text' as const, text: JSON.stringify(summary, null, 2) }]
        }
      }
HydraMcpServer.async method · typescript · L221-L256 (36 LOC)
electron/mcp/McpServer.ts
      async ({ name, projectDir, provider, model, reasoningEffort, initialPrompt, yolo }) => {
        try {
          const state = this.agentManager.create({
            name,
            projectDir,
            provider,
            model,
            reasoningEffort,
            yolo,
            initialPrompt,
            isManager: false // Prevent recursive managers
          })
          return {
            content: [
              {
                type: 'text' as const,
                text: `Agent created successfully:\n${JSON.stringify(
                  { id: state.id, name: state.name, status: state.status, projectDir: state.projectDir },
                  null,
                  2
                )}`
              }
            ]
          }
        } catch (err) {
          return {
            content: [
              {
                type: 'text' as const,
                text: `Failed to create agent: ${err instanceof Error ? err.message : String(err)}`
           
HydraMcpServer.async method · typescript · L266-L282 (17 LOC)
electron/mcp/McpServer.ts
      async ({ agentId, prompt }) => {
        const success = this.agentManager.sendInput(agentId, prompt)
        if (success) {
          return {
            content: [{ type: 'text' as const, text: `Prompt sent to agent ${agentId}` }]
          }
        }
        return {
          content: [
            {
              type: 'text' as const,
              text: `Failed to send prompt to agent ${agentId}. Agent may not exist or not be running.`
            }
          ],
          isError: true
        }
      }
Repobility · MCP-ready · https://repobility.com
HydraMcpServer.async method · typescript · L292-L321 (30 LOC)
electron/mcp/McpServer.ts
      async ({ agentId, lines }) => {
        const buffer = this.agentManager.getBuffer(agentId)
        if (buffer.length === 0) {
          const agent = this.agentManager.get(agentId)
          if (!agent) {
            return {
              content: [{ type: 'text' as const, text: `Agent ${agentId} not found` }],
              isError: true
            }
          }
          return {
            content: [
              {
                type: 'text' as const,
                text: `Agent ${agentId} (${agent.name}) has no output yet. Status: ${agent.status}`
              }
            ]
          }
        }

        const tail = buffer.slice(-lines)
        return {
          content: [
            {
              type: 'text' as const,
              text: `Output from agent ${agentId} (last ${tail.length} of ${buffer.length} lines):\n\n${tail.join('\n')}`
            }
          ]
        }
      }
HydraMcpServer.async method · typescript · L331-L352 (22 LOC)
electron/mcp/McpServer.ts
      async ({ projectDir, prompt }) => {
        const sentTo = this.agentManager.broadcast(projectDir, prompt)
        if (sentTo.length === 0) {
          return {
            content: [
              {
                type: 'text' as const,
                text: `No agents found for project directory: ${projectDir}`
              }
            ],
            isError: true
          }
        }
        return {
          content: [
            {
              type: 'text' as const,
              text: `Broadcast sent to ${sentTo.length} agent(s): ${sentTo.join(', ')}`
            }
          ]
        }
      }
HydraMcpServer.async method · typescript · L361-L372 (12 LOC)
electron/mcp/McpServer.ts
      async ({ agentId }) => {
        const killed = this.agentManager.kill(agentId)
        if (killed) {
          return {
            content: [{ type: 'text' as const, text: `Kill signal sent to agent ${agentId}` }]
          }
        }
        return {
          content: [{ type: 'text' as const, text: `Agent ${agentId} not found` }],
          isError: true
        }
      }
HydraMcpServer.async method · typescript · L381-L397 (17 LOC)
electron/mcp/McpServer.ts
      async ({ agentId }) => {
        const state = this.agentManager.restart(agentId)
        if (state) {
          return {
            content: [
              {
                type: 'text' as const,
                text: `Agent ${agentId} restarted. Status: ${state.status}, Restart count: ${state.restartCount}`
              }
            ]
          }
        }
        return {
          content: [{ type: 'text' as const, text: `Agent ${agentId} not found` }],
          isError: true
        }
      }
HydraMcpServer.async method · typescript · L406-L411 (6 LOC)
electron/mcp/McpServer.ts
      async ({ limit }) => {
        const notifications = this.notificationService?.getRecent(limit) ?? []
        return {
          content: [{ type: 'text' as const, text: JSON.stringify(notifications, null, 2) }]
        }
      }
HydraMcpServer.handleNotificationStream method · typescript · L415-L430 (16 LOC)
electron/mcp/McpServer.ts
  private handleNotificationStream(res: ServerResponse): void {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive'
    })
    res.write(':\n\n') // SSE comment to establish connection

    const unsubscribe = this.notificationService?.subscribe((notification) => {
      res.write(`data: ${JSON.stringify(notification)}\n\n`)
    })

    res.on('close', () => {
      unsubscribe?.()
    })
  }
HydraMcpServer.readBody method · typescript · L432-L446 (15 LOC)
electron/mcp/McpServer.ts
  private readBody(req: IncomingMessage): Promise<unknown> {
    return new Promise((resolve, reject) => {
      const chunks: Buffer[] = []
      req.on('data', (chunk: Buffer) => chunks.push(chunk))
      req.on('end', () => {
        try {
          const raw = Buffer.concat(chunks).toString('utf-8')
          resolve(raw ? JSON.parse(raw) : undefined)
        } catch (err) {
          reject(err)
        }
      })
      req.on('error', reject)
    })
  }
NotificationService.push method · typescript · L17-L46 (30 LOC)
electron/notifications/NotificationService.ts
  push(notification: HydraNotification): void {
    this.recent.push(notification)
    if (this.recent.length > MAX_RECENT) {
      this.recent = this.recent.slice(-MAX_RECENT)
    }

    // Native OS notification
    if (Notification.isSupported()) {
      const native = new Notification({
        title: notification.title,
        body: notification.body,
        silent: true
      })
      native.show()
    }

    // Broadcast to renderer windows
    BrowserWindow.getAllWindows().forEach((win) => {
      win.webContents.send(IPC.NOTIFICATION, notification)
    })

    // Broadcast to SSE subscribers
    for (const sub of this.subscribers) {
      try {
        sub(notification)
      } catch {
        // Best-effort delivery
      }
    }
  }
Source: Repobility analyzer · https://repobility.com
NotificationService.subscribe method · typescript · L53-L58 (6 LOC)
electron/notifications/NotificationService.ts
  subscribe(callback: Subscriber): () => void {
    this.subscribers.add(callback)
    return () => {
      this.subscribers.delete(callback)
    }
  }
NotificationService.connectAgentEvents method · typescript · L60-L150 (91 LOC)
electron/notifications/NotificationService.ts
  connectAgentEvents(
    agentManager: AgentManager,
    headlessOrchestrator: HeadlessOrchestrator
  ): void {
    agentManager.on('agent_waiting', (payload: { agentId: string }) => {
      const agent = agentManager.get(payload.agentId)
      const agentName = agent?.name ?? payload.agentId

      this.push({
        id: randomUUID().slice(0, 12),
        type: 'agent_waiting',
        title: 'Agent Finished',
        body: `${agentName} is waiting for input`,
        agentId: payload.agentId,
        timestamp: new Date().toISOString()
      })
    })

    agentManager.on('status', (payload: AgentStatusPayload) => {
      // Deduplicate: skip if same agent already emitted this status
      const prev = this.lastAgentStatus.get(payload.agentId)
      if (prev === payload.status) return
      this.lastAgentStatus.set(payload.agentId, payload.status)

      const agent = agentManager.get(payload.agentId)
      const agentName = agent?.name ?? payload.agentId

      let type: Notificat
Logger.constructor method · typescript · L34-L39 (6 LOC)
electron/observability/Logger.ts
  constructor(private readonly options: LoggerOptions) {
    mkdirSync(options.logDir, { recursive: true })
    this.logPath = join(options.logDir, 'hydra.log.jsonl')
    this.maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES
    this.maxRotatedFiles = options.maxRotatedFiles ?? DEFAULT_MAX_ROTATED_FILES
  }
Logger.mkdirSync method · typescript · L35-L43 (9 LOC)
electron/observability/Logger.ts
    mkdirSync(options.logDir, { recursive: true })
    this.logPath = join(options.logDir, 'hydra.log.jsonl')
    this.maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES
    this.maxRotatedFiles = options.maxRotatedFiles ?? DEFAULT_MAX_ROTATED_FILES
  }

  getLogPath(): string {
    return this.logPath
  }
Logger.getAllLogPathsNewestFirst method · typescript · L45-L51 (7 LOC)
electron/observability/Logger.ts
  getAllLogPathsNewestFirst(): string[] {
    const files: string[] = [this.logPath]
    for (let i = 1; i <= this.maxRotatedFiles; i++) {
      files.push(`${this.logPath}.${i}`)
    }
    return files.filter((file) => existsSync(file))
  }
Logger.readRecentLines method · typescript · L53-L66 (14 LOC)
electron/observability/Logger.ts
  readRecentLines(maxLines = 3000): string[] {
    const oldestToNewest = this.getAllLogPathsNewestFirst().reverse()
    const lines: string[] = []
    for (const file of oldestToNewest) {
      const chunk = readFileSync(file, 'utf-8')
        .split('\n')
        .filter((line) => line.trim().length > 0)
      lines.push(...chunk)
      if (lines.length > maxLines * 2) {
        lines.splice(0, lines.length - maxLines * 2)
      }
    }
    return lines.slice(-maxLines)
  }
Logger.log method · typescript · L84-L102 (19 LOC)
electron/observability/Logger.ts
  log(input: ObservabilityLogEventPayload): StructuredLogRecord {
    const record: StructuredLogRecord = {
      ts: new Date().toISOString(),
      level: input.level,
      service: this.normalizeService(input.service),
      event: input.event,
      traceId: input.traceId?.trim() || randomUUID().slice(0, 12),
      message: input.message,
      agentId: input.agentId,
      sessionId: input.sessionId,
      projectId: input.projectId,
      meta: this.normalizeMeta(input.meta)
    }

    this.rotateIfNeeded()
    appendFileSync(this.logPath, `${JSON.stringify(record)}\n`, 'utf-8')
    this.mirrorToConsole(record)
    return record
  }
Logger.appendFileSync method · typescript · L99-L109 (11 LOC)
electron/observability/Logger.ts
    appendFileSync(this.logPath, `${JSON.stringify(record)}\n`, 'utf-8')
    this.mirrorToConsole(record)
    return record
  }

  private normalizeService(service: unknown): 'main' | 'renderer' | 'preload' {
    if (service === 'renderer' || service === 'preload' || service === 'main') {
      return service
    }
    return this.options.defaultService
  }
Repobility · code-quality intelligence · https://repobility.com
Logger.normalizeMeta method · typescript · L111-L118 (8 LOC)
electron/observability/Logger.ts
  private normalizeMeta(meta: unknown): Record<string, unknown> | undefined {
    if (!meta || typeof meta !== 'object') return undefined
    try {
      return JSON.parse(JSON.stringify(meta)) as Record<string, unknown>
    } catch {
      return { parseError: 'meta-not-serializable' }
    }
  }
Logger.rotateIfNeeded method · typescript · L120-L139 (20 LOC)
electron/observability/Logger.ts
  private rotateIfNeeded(): void {
    if (!existsSync(this.logPath)) return
    const size = statSync(this.logPath).size
    if (size < this.maxFileBytes) return

    const oldest = `${this.logPath}.${this.maxRotatedFiles}`
    if (existsSync(oldest)) {
      rmSync(oldest, { force: true })
    }

    for (let i = this.maxRotatedFiles - 1; i >= 1; i--) {
      const current = `${this.logPath}.${i}`
      const next = `${this.logPath}.${i + 1}`
      if (existsSync(current)) {
        renameSync(current, next)
      }
    }

    renameSync(this.logPath, `${this.logPath}.1`)
  }
Logger.mirrorToConsole method · typescript · L141-L152 (12 LOC)
electron/observability/Logger.ts
  private mirrorToConsole(record: StructuredLogRecord): void {
    const line = `[Hydra:${record.service}] ${record.event} ${record.message ?? ''}`.trim()
    if (record.level === 'error') {
      console.error(line)
      return
    }
    if (record.level === 'warn') {
      console.warn(line)
      return
    }
    console.log(line)
  }
ObservabilityService.constructor method · typescript · L29-L34 (6 LOC)
electron/observability/ObservabilityService.ts
  constructor(private readonly context: ObservabilityContext) {
    this.logger = new Logger({
      logDir: join(app.getPath('userData'), 'logs'),
      defaultService: 'main'
    })
  }
ObservabilityService.installProcessHandlers method · typescript · L48-L71 (24 LOC)
electron/observability/ObservabilityService.ts
  installProcessHandlers(): void {
    if (this.processHandlersInstalled) return
    this.processHandlersInstalled = true

    process.on('uncaughtException', (error) => {
      this.logMain({
        level: 'error',
        event: 'process.uncaught-exception',
        message: error.message,
        meta: { stack: error.stack }
      })
    })

    process.on('unhandledRejection', (reason) => {
      const message = reason instanceof Error ? reason.message : String(reason)
      const stack = reason instanceof Error ? reason.stack : undefined
      this.logMain({
        level: 'error',
        event: 'process.unhandled-rejection',
        message,
        meta: { stack }
      })
    })
  }
ObservabilityService.exportDiagnostics method · typescript · L73-L133 (61 LOC)
electron/observability/ObservabilityService.ts
  async exportDiagnostics(): Promise<ExportDiagnosticsResult> {
    const defaultPath = join(
      app.getPath('documents'),
      `hydra-diagnostics-${new Date().toISOString().replace(/[:.]/g, '-')}.json`
    )

    const ownerWindow = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]
    const saveResult = await dialog.showSaveDialog(ownerWindow ?? undefined, {
      title: 'Export Hydra Diagnostics',
      defaultPath,
      filters: [{ name: 'JSON', extensions: ['json'] }]
    })

    if (saveResult.canceled || !saveResult.filePath) {
      return { path: null, error: null }
    }

    try {
      const config = this.context.getConfig()
      const includeSensitive = config.includeSensitiveDiagnostics
      const snapshot = {
        generatedAt: new Date().toISOString(),
        runtimeId: this.runtimeId,
        app: {
          version: app.getVersion(),
          electron: process.versions.electron,
          chrome: process.versions.chrome,
          node: 
ObservabilityService.writeFileSync method · typescript · L116-L122 (7 LOC)
electron/observability/ObservabilityService.ts
      writeFileSync(saveResult.filePath, JSON.stringify(snapshot, null, 2), 'utf-8')
      this.logMain({
        level: 'info',
        event: 'diagnostics.exported',
        message: 'Diagnostics bundle exported',
        meta: { filePath: saveResult.filePath, includeSensitive }
      })
ObservabilityService.sanitizeValue method · typescript · L135-L154 (20 LOC)
electron/observability/ObservabilityService.ts
  private sanitizeValue(value: unknown, includeSensitive: boolean, keyHint = ''): unknown {
    if (value === null || value === undefined) return value
    if (typeof value === 'string') {
      if (!includeSensitive && SENSITIVE_KEY_PATTERN.test(keyHint)) {
        return '[redacted]'
      }
      return value
    }
    if (Array.isArray(value)) {
      return value.map((entry) => this.sanitizeValue(entry, includeSensitive, keyHint))
    }
    if (typeof value === 'object') {
      const out: Record<string, unknown> = {}
      for (const [key, next] of Object.entries(value as Record<string, unknown>)) {
        out[key] = this.sanitizeValue(next, includeSensitive, key)
      }
      return out
    }
    return value
  }
Powered by Repobility — scan your code at https://repobility.com
ObservabilityService.reportIfNeeded method · typescript · L156-L203 (48 LOC)
electron/observability/ObservabilityService.ts
  private reportIfNeeded(record: StructuredLogRecord): void {
    if (record.level !== 'error') return
    if (this.isReporting) return

    const config = this.context.getConfig()
    const endpoint =
      config.errorReportingEndpoint.trim() || process.env.HYDRA_ERROR_REPORT_ENDPOINT?.trim() || ''
    if (!config.enableRemoteErrorReporting || !endpoint) return

    const payload = {
      runtimeId: this.runtimeId,
      sentAt: new Date().toISOString(),
      appVersion: app.getVersion(),
      platform: process.platform,
      arch: process.arch,
      record: this.sanitizeValue(record, config.includeSensitiveDiagnostics)
    }

    this.isReporting = true
    const controller = new AbortController()
    const timeout = setTimeout(() => controller.abort(), 5000)

    fetch(endpoint, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(payload),
      signal: controller.signal
    })
      .then((response) => {
        if (!respo
ObservabilityService.fetch method · typescript · L178-L191 (14 LOC)
electron/observability/ObservabilityService.ts
    fetch(endpoint, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(payload),
      signal: controller.signal
    })
      .then((response) => {
        if (!response.ok) {
          this.logger.warn('remote-report.failed', {
            service: 'main',
            message: `Remote reporter returned ${response.status}`
          })
        }
      })
SessionCatalog.listSessions method · typescript · L59-L87 (29 LOC)
electron/sessions/SessionCatalog.ts
  listSessions(options: ListSessionOptions = {}): ClaudeSessionSummary[] {
    const sessions = this.getSessionSnapshot(options.forceRefresh === true)

    const hiddenIds = new Set(options.hiddenSessionIds ?? [])
    const projectPrefix = options.projectPathPrefix?.trim()
    const normalizedProjectPrefix = projectPrefix ? normalizePathForComparison(projectPrefix) : null
    const cutoff =
      typeof options.maxAgeDays === 'number' && options.maxAgeDays > 0
        ? Date.now() - options.maxAgeDays * 86_400_000
        : 0
    const filtered = sessions.filter((session) => {
      if (hiddenIds.has(session.sessionId)) return false
      if (normalizedProjectPrefix) {
        const normalizedSessionPath = normalizePathForComparison(session.projectPath)
        if (!normalizedSessionPath.startsWith(normalizedProjectPrefix)) {
          return false
        }
      }
      if (cutoff > 0 && Date.parse(session.modifiedAt) < cutoff) return false
      return true
    })

    filtered.sort
‹ prevpage 2 / 5next ›