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
85 changes: 65 additions & 20 deletions packages/opencode/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from "path"
import { Global } from "../global"
import fs from "fs/promises"
import z from "zod"
import { Lock } from "../util/lock"

export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key"

Expand Down Expand Up @@ -43,31 +44,75 @@ export namespace Auth {
}

export async function all(): Promise<Record<string, Info>> {
const file = Bun.file(filepath)
const data = await file.json().catch(() => ({}) as Record<string, unknown>)
return Object.entries(data).reduce(
(acc, [key, value]) => {
const parsed = Info.safeParse(value)
if (!parsed.success) return acc
acc[key] = parsed.data
return acc
},
{} as Record<string, Info>,
)
const release = await Lock.read("auth")
try {
const file = Bun.file(filepath)

if (!(await file.exists())) return {}

const data = await file.json()

if (typeof data !== "object" || data === null) {
throw new Error("auth.json contains invalid data")
}

return Object.entries(data).reduce(
(acc, [key, value]) => {
const parsed = Info.safeParse(value)
if (!parsed.success) return acc
acc[key] = parsed.data
return acc
},
{} as Record<string, Info>,
)
} finally {
release[Symbol.dispose]()
}
}

export async function set(key: string, info: Info) {
const file = Bun.file(filepath)
const data = await all()
await Bun.write(file, JSON.stringify({ ...data, [key]: info }, null, 2))
await fs.chmod(file.name!, 0o600)
const release = await Lock.write("auth")
try {
const file = Bun.file(filepath)
const exists = await file.exists()
const rawData = exists ? await file.json() : null
const data: Record<string, Info> = {}

if (typeof rawData === "object" && rawData !== null) {
Object.entries(rawData).forEach(([k, v]) => {
const parsed = Info.safeParse(v)
if (parsed.success) data[k] = parsed.data
})
}

data[key] = info
await Bun.write(file, JSON.stringify(data, null, 2))
await fs.chmod(filepath, 0o600)
} finally {
release[Symbol.dispose]()
}
}

export async function remove(key: string) {
const file = Bun.file(filepath)
const data = await all()
delete data[key]
await Bun.write(file, JSON.stringify(data, null, 2))
await fs.chmod(file.name!, 0o600)
const release = await Lock.write("auth")
try {
const file = Bun.file(filepath)
const exists = await file.exists()
const rawData = exists ? await file.json() : null
const data: Record<string, Info> = {}

if (typeof rawData === "object" && rawData !== null) {
Object.entries(rawData).forEach(([k, v]) => {
const parsed = Info.safeParse(v)
if (parsed.success) data[k] = parsed.data
})
}

delete data[key]
await Bun.write(file, JSON.stringify(data, null, 2))
await fs.chmod(filepath, 0o600)
} finally {
release[Symbol.dispose]()
}
}
}
14 changes: 14 additions & 0 deletions packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,20 @@ function Task(props: ToolProps<typeof TaskTool>) {
const local = useLocal()
const sync = useSync()

// Ensure child session data is loaded before accessing it
createEffect(async () => {
const childSessionId = props.metadata.sessionId
if (!childSessionId) return
const hasMessages = !!sync.data.message[childSessionId]?.length
if (!hasMessages) {
try {
await sync.session.sync(childSessionId)
} catch {
// Silently ignore sync errors - activity will just not show
}
}
})

const current = createMemo(() =>
props.metadata.summary?.findLast(
(x: { id: string; tool: string; state: { status: string; title?: string } }) => x.state.status !== "pending",
Expand Down
Loading