Skip to content
Open
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
7 changes: 5 additions & 2 deletions packages/opencode/src/cli/cmd/tui/context/sync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { createSimpleContext } from "./helper"
import type { Snapshot } from "@/snapshot"
import { useExit } from "./exit"
import { useArgs } from "./args"
import { batch, onMount } from "solid-js"
import { batch, onCleanup, onMount } from "solid-js"
import { Log } from "@/util/log"
import type { Path } from "@opencode-ai/sdk"

Expand Down Expand Up @@ -104,7 +104,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({

const sdk = useSDK()

sdk.event.listen((e) => {
const unsubscribe = sdk.event.listen((e) => {
const event = e.details
switch (event.type) {
case "server.instance.disposed":
Expand Down Expand Up @@ -307,6 +307,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}
})

// Clean up event listener on unmount to prevent memory leak
onCleanup(unsubscribe)

const exit = useExit()
const args = useArgs()

Expand Down
37 changes: 35 additions & 2 deletions packages/slack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@ const opencode = await createOpencode({
})
console.log("✅ Opencode server ready")

const sessions = new Map<string, { client: any; server: any; sessionId: string; channel: string; thread: string }>()
const sessions = new Map<string, { client: any; server: any; sessionId: string; channel: string; thread: string; lastUsed: number }>()

// Session cleanup: remove sessions older than 1 hour
const SESSION_TIMEOUT_MS = 60 * 60 * 1000
const MAX_SESSIONS = 100

function cleanupOldSessions() {
const now = Date.now()
for (const [key, session] of sessions.entries()) {
if (now - session.lastUsed > SESSION_TIMEOUT_MS || sessions.size > MAX_SESSIONS) {
sessions.delete(key)
console.log("🧹 Cleaned up session:", key)
}
}
}

// Run cleanup periodically
setInterval(cleanupOldSessions, 5 * 60 * 1000) // Every 5 minutes

;(async () => {
const events = await opencode.client.event.subscribe()
for await (const event of events.stream) {
Expand All @@ -29,6 +47,7 @@ const sessions = new Map<string, { client: any; server: any; sessionId: string;
// Find the session for this tool update
for (const [sessionKey, session] of sessions.entries()) {
if (session.sessionId === part.sessionID) {
session.lastUsed = Date.now()
handleToolUpdate(part, session.channel, session.thread)
break
}
Expand Down Expand Up @@ -90,7 +109,7 @@ app.message(async ({ message, say }) => {

console.log("✅ Created opencode session:", createResult.data.id)

session = { client, server, sessionId: createResult.data.id, channel, thread }
session = { client, server, sessionId: createResult.data.id, channel, thread, lastUsed: Date.now() }
sessions.set(sessionKey, session)

const shareResult = await client.session.share({ path: { id: createResult.data.id } })
Expand All @@ -102,6 +121,7 @@ app.message(async ({ message, say }) => {
}

console.log("📝 Sending to opencode:", message.text)
session.lastUsed = Date.now()
const result = await session.client.session.prompt({
path: { id: session.sessionId },
body: { parts: [{ type: "text", text: message.text }] },
Expand Down Expand Up @@ -143,3 +163,16 @@ app.command("/test", async ({ command, ack, say }) => {

await app.start()
console.log("⚡️ Slack bot is running!")

// Graceful shutdown handler
process.on("SIGINT", () => {
console.log("\n🛑 Shutting down...")
sessions.clear()
process.exit(0)
})

process.on("SIGTERM", () => {
console.log("\n🛑 Shutting down...")
sessions.clear()
process.exit(0)
})
27 changes: 22 additions & 5 deletions packages/ui/src/components/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Tooltip as KobalteTooltip } from "@kobalte/core/tooltip"
import { children, createSignal, Match, onMount, splitProps, Switch, type JSX } from "solid-js"
import { children, createSignal, Match, onCleanup, onMount, splitProps, Switch, type JSX } from "solid-js"
import type { ComponentProps } from "solid-js"

export interface TooltipProps extends ComponentProps<typeof KobalteTooltip> {
Expand Down Expand Up @@ -36,17 +36,34 @@ export function Tooltip(props: TooltipProps) {

onMount(() => {
const childElements = c()
const cleanupFns: (() => void)[] = []

const addListeners = (el: HTMLElement) => {
const focusHandler = () => setOpen(true)
const blurHandler = () => setOpen(false)
el.addEventListener("focus", focusHandler)
el.addEventListener("blur", blurHandler)
cleanupFns.push(() => {
el.removeEventListener("focus", focusHandler)
el.removeEventListener("blur", blurHandler)
})
}

if (childElements instanceof HTMLElement) {
childElements.addEventListener("focus", () => setOpen(true))
childElements.addEventListener("blur", () => setOpen(false))
addListeners(childElements)
} else if (Array.isArray(childElements)) {
for (const child of childElements) {
if (child instanceof HTMLElement) {
child.addEventListener("focus", () => setOpen(true))
child.addEventListener("blur", () => setOpen(false))
addListeners(child)
}
}
}

onCleanup(() => {
for (const cleanup of cleanupFns) {
cleanup()
}
})
})

return (
Expand Down