| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- import { Binary } from "@kirincode-ai/core/util/binary"
- import type { Message, Part, Session } from "@kirincode-ai/sdk/v2/client"
- import { createMemo } from "solid-js"
- import { produce, reconcile, type SetStoreFunction } from "solid-js/store"
- import type { createServerSdkContext } from "./server-sdk"
- import type { createServerSyncContextInner } from "./server-sync"
- import type { State } from "./global-sync/types"
- const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
- const sessionFields = new Set([
- "session_status",
- "session_working",
- "session_diff",
- "todo",
- "permission",
- "question",
- "message",
- "part",
- "part_text_accum_delta",
- ])
- export const createDirSyncContext = (
- directory: string,
- serverSync: ReturnType<typeof createServerSyncContextInner>,
- serverSDK: ReturnType<typeof createServerSdkContext>,
- ) => {
- const client = serverSDK.createClient({ directory, throwOnError: true })
- const current = createMemo(() => serverSync.child(directory, { mcp: true }))
- const absolute = (path: string) => (current()[0].path.directory + "/" + path).replace("//", "/")
- const data = new Proxy({} as State, {
- get(_, property: keyof State) {
- if (property === "session_working") return serverSync.session.data.session_working.bind(serverSync.session.data)
- if (sessionFields.has(property)) return serverSync.session.data[property as keyof typeof serverSync.session.data]
- return current()[0][property]
- },
- })
- const set = ((...input: unknown[]) => {
- if (typeof input[0] === "string" && sessionFields.has(input[0])) {
- return (serverSync.session.set as (...args: unknown[]) => unknown)(...input)
- }
- const result = (current()[1] as (...args: unknown[]) => unknown)(...input)
- if (input[0] === "session") current()[0].session.forEach(serverSync.session.remember)
- return result
- }) as SetStoreFunction<State>
- const index = (sessionID: string) => {
- const session = serverSync.session.get(sessionID)
- if (!session || session.directory !== directory) return
- const [store, setStore] = current()
- const result = Binary.search(store.session, session.id, (item) => item.id)
- if (result.found) {
- setStore("session", result.index, reconcile(session))
- return
- }
- setStore(
- "session",
- produce((draft) => void draft.splice(result.index, 0, session)),
- )
- }
- return {
- data,
- set,
- get status() {
- return current()[0].status
- },
- get ready() {
- return current()[0].status !== "loading"
- },
- get project() {
- const store = current()[0]
- const match = Binary.search(serverSync.data.project, store.project, (project) => project.id)
- if (match.found) return serverSync.data.project[match.index]
- },
- session: {
- remember(session: Session) {
- serverSync.session.remember(session)
- index(session.id)
- },
- get(sessionID: string) {
- const session = serverSync.session.get(sessionID)
- if (session?.directory === directory) return session
- },
- optimistic: {
- add(input: { directory?: string; sessionID: string; message: Message; parts: Part[] }) {
- serverSync.session.optimistic.add(input)
- },
- remove(input: { directory?: string; sessionID: string; messageID: string }) {
- serverSync.session.optimistic.remove(input)
- },
- },
- addOptimisticMessage(input: {
- sessionID: string
- messageID: string
- parts: Part[]
- agent: string
- model: { providerID: string; modelID: string }
- variant?: string
- }) {
- serverSync.session.optimistic.add({
- sessionID: input.sessionID,
- message: {
- id: input.messageID,
- sessionID: input.sessionID,
- role: "user",
- time: { created: Date.now() },
- agent: input.agent,
- model: { ...input.model, variant: input.variant },
- },
- parts: input.parts,
- })
- },
- async sync(sessionID: string, options?: { force?: boolean }) {
- await serverSync.session.sync(sessionID, options)
- index(sessionID)
- },
- diff: serverSync.session.diff,
- todo: serverSync.session.todo,
- history: serverSync.session.history,
- evict(sessionID: string) {
- serverSync.session.evict(sessionID)
- },
- fetch: async (count = 10) => {
- const [store, setStore] = current()
- setStore("limit", (value) => value + count)
- const response = await client.session.list()
- const sessions = (response.data ?? [])
- .filter((session) => !!session?.id)
- .sort((a, b) => cmp(a.id, b.id))
- .slice(0, store.limit)
- sessions.forEach(serverSync.session.remember)
- setStore("session", reconcile(sessions, { key: "id" }))
- },
- more: createMemo(() => current()[0].session.length >= current()[0].limit),
- archive: async (sessionID: string) => {
- await serverSDK.client.session.update({ sessionID, time: { archived: Date.now() } })
- current()[1](
- "session",
- produce((draft) => {
- const match = Binary.search(draft, sessionID, (session) => session.id)
- if (match.found) draft.splice(match.index, 1)
- }),
- )
- },
- },
- mcp: {
- toggle: (name: string) => serverSync.mcp.toggle(directory, name),
- },
- absolute,
- get directory() {
- return current()[0].path.directory
- },
- }
- }
|