murashit_codex-panel/src/settings/app-server-dynamic-data.ts

75 lines
4.2 KiB
TypeScript

import type { AppServerClient } from "../app-server/connection/client";
import type { AppServerClientAccess } from "../app-server/connection/client-access";
import type { ObservedResultListener } from "../app-server/query/observed-result";
import { listHookCatalog, setHookItemEnabled, trustHookItem } from "../app-server/services/catalog";
import { deleteThread, restoreArchivedThread as restoreArchivedThreadOnAppServer } from "../app-server/services/threads";
import type { HookItem, ModelMetadata } from "../domain/catalog/metadata";
import type { ThreadCatalogArchivedReader, ThreadCatalogEventSink } from "../features/threads/catalog/thread-catalog";
import { createKeyedOperationQueue } from "../shared/runtime/keyed-operation-queue";
import type { SettingsDynamicDataAccess, SettingsHookCatalog } from "./dynamic-data";
interface SettingsAppServerQueries {
modelsSnapshot(): readonly ModelMetadata[] | null;
observeModelsResult(listener: ObservedResultListener<readonly ModelMetadata[]>, options?: { emitCurrent?: boolean }): () => void;
fetchModels(): Promise<readonly ModelMetadata[]>;
refreshModels(): Promise<readonly ModelMetadata[]>;
}
type SettingsArchivedThreadCatalog = ThreadCatalogArchivedReader & ThreadCatalogEventSink;
export interface SettingsAppServerDynamicDataOptions {
vaultPath: string;
clientAccess: AppServerClientAccess;
appServerQueries: SettingsAppServerQueries;
threadCatalog: SettingsArchivedThreadCatalog;
}
export function createSettingsAppServerDynamicData(options: SettingsAppServerDynamicDataOptions): SettingsDynamicDataAccess {
const archivedThreadMutations = createKeyedOperationQueue<string>();
const withSettingsConnection = <T>(operation: (client: AppServerClient) => Promise<T>): Promise<T> =>
options.clientAccess.withClient(operation, {
serverRequests: { kind: "reject", message: "Codex Panel settings does not handle server requests." },
});
const runArchivedThreadMutation = <T>(threadId: string, operation: () => Promise<T>): Promise<T> => {
return archivedThreadMutations.run(threadId, operation);
};
const loadHooks = (client: AppServerClient): Promise<SettingsHookCatalog> => loadSettingsHookCatalog(client, options.vaultPath);
const mutateHook = (hook: HookItem, mutation: (client: AppServerClient, hook: HookItem) => Promise<void>): Promise<SettingsHookCatalog> =>
withSettingsConnection(async (client) => {
await mutation(client, hook);
return loadHooks(client);
});
return {
modelsSnapshot: () => options.appServerQueries.modelsSnapshot(),
observeModelsResult: (listener, observeOptions) => options.appServerQueries.observeModelsResult(listener, observeOptions),
fetchModels: () => options.appServerQueries.fetchModels(),
refreshModels: () => options.appServerQueries.refreshModels(),
archivedThreadsSnapshot: () => options.threadCatalog.archivedSnapshot(),
observeArchivedThreadsResult: (listener, observeOptions) => options.threadCatalog.observeArchived(listener, observeOptions),
refreshArchivedThreads: () => options.threadCatalog.refreshArchived(),
refreshHooks: () => withSettingsConnection(loadHooks),
trustHook: (hook) => mutateHook(hook, trustHookItem),
setHookEnabled: (hook, enabled) => mutateHook(hook, (client, item) => setHookItemEnabled(client, item, enabled)),
restoreArchivedThread: (threadId) =>
runArchivedThreadMutation(threadId, async () => {
const thread = await withSettingsConnection((client) => restoreArchivedThreadOnAppServer(client, threadId));
options.threadCatalog.apply({ type: "thread-restored", thread });
return thread;
}),
deleteArchivedThread: (threadId) =>
runArchivedThreadMutation(threadId, async () => {
await withSettingsConnection((client) => deleteThread(client, threadId));
options.threadCatalog.apply({ type: "thread-deleted", threadId });
}),
};
}
async function loadSettingsHookCatalog(client: AppServerClient, vaultPath: string): Promise<SettingsHookCatalog> {
const catalog = await listHookCatalog(client, vaultPath);
const hookCount = catalog.hooks.length;
return {
...catalog,
status: `Loaded ${String(hookCount)} hook${hookCount === 1 ? "" : "s"}.`,
};
}