refactor: address the comments

This commit is contained in:
Catriel Müller
2026-05-21 14:38:18 -03:00
parent 1f24a39ae9
commit f2053dc8be
5 changed files with 82 additions and 23 deletions
@@ -176,6 +176,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
// kilocode_change end
const fullSyncedSessions = new Set<string>()
const deleted = new Set<string>() // kilocode_change
let syncedWorkspace = project.workspace.current()
function sessionListQuery(): { scope?: "project"; path?: string } {
@@ -197,6 +198,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
event.subscribe((event) => {
switch (event.type) {
case "server.instance.disposed":
// kilocode_change start
deleted.clear()
setStore("background_process", {})
// kilocode_change end
void bootstrap()
break
case "permission.replied": {
@@ -372,14 +377,17 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
break
}
// kilocode_change start
case "session.status": {
setStore("session_status", event.properties.sessionID, event.properties.status)
break
}
// kilocode_change end
// kilocode_change start
case "background_process.updated": {
const info = event.properties.info
deleted.delete(info.id)
const list = store.background_process[info.sessionID]
if (!list) {
setStore("background_process", info.sessionID, [info])
@@ -401,6 +409,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}
case "background_process.deleted": {
deleted.add(event.properties.processID)
const list = store.background_process[event.properties.sessionID]
if (!list) break
const result = Binary.search(list, event.properties.processID, (item) => item.id)
@@ -562,6 +571,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const workspace = project.workspace.current()
if (workspace !== syncedWorkspace) {
fullSyncedSessions.clear()
// kilocode_change start
deleted.clear()
setStore("background_process", {})
// kilocode_change end
syncedWorkspace = workspace
}
const projectPromise = project.sync()
@@ -635,9 +648,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
sdk.client.command.list({ workspace }).then((x) => setStore("command", reconcile(x.data ?? []))),
sdk.client.lsp.status({ workspace }).then((x) => setStore("lsp", reconcile(x.data ?? []))),
sdk.client.mcp.status({ workspace }).then((x) => setStore("mcp", reconcile(x.data ?? {}))),
// kilocode_change start
sdk.client.experimental.resource
.list({ workspace })
.then((x) => setStore("mcp_resource", reconcile(x.data ?? {}))),
// kilocode_change end
sdk.client.formatter.status({ workspace }).then((x) => setStore("formatter", reconcile(x.data!))), // kilocode_change
// kilocode_change start
sdk.client.network.list().then((x) => {
@@ -655,7 +670,22 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
next[item.sessionID].push(item)
}
for (const list of Object.values(next)) list.sort((a, b) => a.id.localeCompare(b.id))
setStore("background_process", reconcile(next))
setStore(
"background_process",
produce((draft) => {
for (const [sessionID, list] of Object.entries(next)) {
const items = new Map((draft[sessionID] ?? []).map((item) => [item.id, item]))
for (const item of list) {
if (deleted.has(item.id)) continue
const prev = items.get(item.id)
if (!prev || item.time.updated >= prev.time.updated) items.set(item.id, item)
}
const value = Array.from(items.values()).toSorted((a, b) => a.id.localeCompare(b.id))
if (value.length === 0) delete draft[sessionID]
else draft[sessionID] = value
}
}),
)
}),
// kilocode_change end
sdk.client.session.status({ workspace }).then((x) => {
@@ -291,8 +291,9 @@ export function Session() {
}
function stopProcesses(sessionID: string) {
const workspace = project.workspace.current()
for (const id of processSessions(sessionID)) {
void sdk.client.backgroundProcess.stopSession({ sessionID: id }).catch((err) => {
void sdk.client.backgroundProcess.stopSession({ sessionID: id, workspace }).catch((err) => {
Log.Default.warn("failed to stop session background processes", { sessionID: id, err })
})
}
@@ -362,6 +362,23 @@ export namespace BackgroundProcess {
return proc.exitCode !== null || proc.signalCode !== null
}
function code(err: unknown) {
if (!err || typeof err !== "object" || !("code" in err)) return
const value = (err as { code?: unknown }).code
return typeof value === "string" ? value : undefined
}
function group(pid: number) {
try {
process.kill(-pid, 0)
return true
} catch (err) {
if (code(err) === "ESRCH") return false
log.debug("failed to probe process group", { err, pid })
return true
}
}
function waitExit(proc: ChildProcess, ms: number) {
if (stopped(proc)) return Promise.resolve()
return new Promise<void>((resolve) => {
@@ -398,7 +415,7 @@ export namespace BackgroundProcess {
active.proc.kill("SIGTERM")
}
await waitExit(active.proc, KILL_MS)
if (stopped(active.proc)) return
if (stopped(active.proc) && !group(pid)) return
try {
process.kill(-pid, "SIGKILL")
} catch (err) {
@@ -434,6 +451,9 @@ export namespace BackgroundProcess {
const sh = Shell.acceptable()
const cwd = path.resolve(state.dir, input.cwd ?? state.dir)
const readyPattern = pattern(input.ready?.pattern)
if (input.ready?.port && (await connected(input.ready.port))) {
throw new Error(`Ready port is already in use: ${input.ready.port}`)
}
const args = Shell.args(sh, input.command, cwd)
const proc = spawn(sh, args, {
cwd,
@@ -4,6 +4,7 @@ import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
import { useDialog } from "@tui/ui/dialog"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import { useKeybind } from "@tui/context/keybind"
import { useProject } from "@tui/context/project"
import { useRoute } from "@tui/context/route"
import { useSDK } from "@tui/context/sdk"
import { useSync } from "@tui/context/sync"
@@ -58,6 +59,7 @@ function ports(item: Info) {
}
function useActions() {
const project = useProject()
const sdk = useSDK()
const toast = useToast()
const [busy, setBusy] = createSignal<{ id: string; kind: Kind }>()
@@ -67,11 +69,12 @@ function useActions() {
if (kind === "stop" && terminal(item.status)) return
setBusy({ id: item.id, kind })
const workspace = project.workspace.current()
try {
const result =
kind === "stop"
? await sdk.client.backgroundProcess.stop({ processID: item.id })
: await sdk.client.backgroundProcess.restart({ processID: item.id })
? await sdk.client.backgroundProcess.stop({ processID: item.id, workspace })
: await sdk.client.backgroundProcess.restart({ processID: item.id, workspace })
if (result.error) {
toast.show({
@@ -70,6 +70,14 @@ function invalid(action: Action, message: string) {
}
}
function missing(id: BackgroundProcess.ID) {
return {
title: "Background process not found",
output: `Background process not found: ${id}`,
metadata: { processID: id },
}
}
function pattern(ready?: BackgroundProcess.Ready) {
if (!ready?.pattern) return
try {
@@ -100,27 +108,24 @@ export const BackgroundProcessTool = Tool.define<typeof Params, Meta, never, "ba
if (params.action !== "start") {
const id = params.id
if (!id) return invalid(params.action, "Missing id")
const found = yield* Effect.promise(() => BackgroundProcess.get(id))
if (!found || found.sessionID !== ctx.sessionID) return missing(id)
if (params.action === "logs") {
const logs = yield* Effect.promise(() => BackgroundProcess.logs(id))
if (!logs) return missing(id)
return {
title: `Logs: ${title(found)}`,
output: logs.output || "(no output)",
metadata: { processID: found.id, status: found.status },
}
}
const info =
params.action === "stop"
? yield* Effect.promise(() => BackgroundProcess.stop(id))
: params.action === "restart"
? yield* Effect.promise(() => BackgroundProcess.restart(id))
: yield* Effect.promise(() => BackgroundProcess.get(id))
if (!info) {
return {
title: "Background process not found",
output: `Background process not found: ${id}`,
metadata: { processID: id },
}
}
if (params.action === "logs") {
const logs = yield* Effect.promise(() => BackgroundProcess.logs(id))
return {
title: `Logs: ${title(info)}`,
output: logs?.output || "(no output)",
metadata: { processID: info.id, status: info.status },
}
}
: found
if (!info) return missing(id)
return {
title: `${params.action}: ${title(info)}`,
output: format(info),
@@ -128,8 +133,8 @@ export const BackgroundProcessTool = Tool.define<typeof Params, Meta, never, "ba
}
}
const command = params.command
if (!command?.trim()) return invalid(params.action, "Missing command")
const command = params.command?.trim()
if (!command) return invalid(params.action, "Missing command")
const err = pattern(params.ready)
if (err) return invalid(params.action, err)
const inst = yield* InstanceState.context