Files
velum-raycast/src/sessions.ts
muena 1843479884 Initial commit: Velum Raycast extension
Pseudonymize-and-AI workflow for handling PII-sensitive text via the
Velum API and Raycast AI. Commands cover end-to-end email summary and
reply, briefing/action-items/structured-data extraction, manual
pseudonymize/depseudonymize on selection or clipboard, and session
management. Includes Raycast 2.0 Beta workarounds for selection capture
and rich-text clipboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 06:46:42 +02:00

193 lines
5.3 KiB
TypeScript

import { LocalStorage } from "@raycast/api";
import { randomUUID } from "node:crypto";
import { getPreferences } from "./preferences";
import type { PlaceholderMapping, SessionMode, VelumSession } from "./types";
const SESSIONS_STORAGE_KEY = "velum.sessions.v1";
const ACTIVE_SESSION_STORAGE_KEY = "velum.sessions.active.v1";
function nowIso(): string {
return new Date().toISOString();
}
function dayKey(date = new Date()): string {
return date.toISOString().slice(0, 10);
}
function sessionSort(a: VelumSession, b: VelumSession): number {
return Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
}
async function saveSessions(sessions: VelumSession[]): Promise<void> {
const maxSessions = getPreferences().maxSessions;
const pruned = [...sessions].sort(sessionSort).slice(0, maxSessions);
await LocalStorage.setItem(SESSIONS_STORAGE_KEY, JSON.stringify(pruned));
const activeId = await getActiveSessionId();
if (activeId && !pruned.some((session) => session.id === activeId)) {
await LocalStorage.setItem(ACTIVE_SESSION_STORAGE_KEY, pruned[0]?.id ?? "");
}
}
export async function listSessions(): Promise<VelumSession[]> {
const raw = await LocalStorage.getItem<string>(SESSIONS_STORAGE_KEY);
if (!raw) {
return [];
}
try {
return (JSON.parse(raw) as VelumSession[]).sort(sessionSort);
} catch {
await LocalStorage.removeItem(SESSIONS_STORAGE_KEY);
return [];
}
}
export async function getActiveSessionId(): Promise<string | undefined> {
const id = await LocalStorage.getItem<string>(ACTIVE_SESSION_STORAGE_KEY);
return id || undefined;
}
export async function setActiveSession(id: string): Promise<void> {
await LocalStorage.setItem(ACTIVE_SESSION_STORAGE_KEY, id);
}
export async function createSession(name?: string): Promise<VelumSession> {
const timestamp = nowIso();
const session: VelumSession = {
id: randomUUID(),
name: name || `Sitzung ${new Date().toLocaleString()}`,
createdAt: timestamp,
updatedAt: timestamp,
mapping: {},
};
const sessions = await listSessions();
await saveSessions([session, ...sessions]);
await setActiveSession(session.id);
return session;
}
export async function getSession(
id: string,
): Promise<VelumSession | undefined> {
const sessions = await listSessions();
return sessions.find((session) => session.id === id);
}
export async function getActiveSession(): Promise<VelumSession> {
const sessions = await listSessions();
const activeId = await getActiveSessionId();
const active = sessions.find((session) => session.id === activeId);
if (active) {
return active;
}
return createSession();
}
export async function resolveSessionForMode(
mode: SessionMode,
): Promise<VelumSession> {
if (mode === "new-each-request") {
return createSession(`Anfrage ${new Date().toLocaleString()}`);
}
if (mode === "daily") {
const name = `Tag ${dayKey()}`;
const existing = (await listSessions()).find(
(session) => session.name === name,
);
if (existing) {
await setActiveSession(existing.id);
return existing;
}
return createSession(name);
}
return getActiveSession();
}
export async function resolveDefaultSession(): Promise<VelumSession> {
return resolveSessionForMode(getPreferences().sessionMode);
}
export async function updateSessionMapping(
id: string,
mapping: PlaceholderMapping,
entityTypes?: string[],
): Promise<VelumSession> {
const sessions = await listSessions();
const timestamp = nowIso();
const index = sessions.findIndex((session) => session.id === id);
if (index === -1) {
const created: VelumSession = {
id,
name: `Sitzung ${new Date().toLocaleString()}`,
createdAt: timestamp,
updatedAt: timestamp,
mapping,
entityTypes,
};
await saveSessions([created, ...sessions]);
return created;
}
const updated: VelumSession = {
...sessions[index],
mapping,
entityTypes,
updatedAt: timestamp,
};
const next = [...sessions];
next[index] = updated;
await saveSessions(next);
return updated;
}
export async function renameSession(id: string, name: string): Promise<void> {
const sessions = await listSessions();
await saveSessions(
sessions.map((session) =>
session.id === id
? { ...session, name: name.trim() || session.name, updatedAt: nowIso() }
: session,
),
);
}
export async function clearSessionMapping(id: string): Promise<void> {
const sessions = await listSessions();
await saveSessions(
sessions.map((session) =>
session.id === id
? { ...session, mapping: {}, updatedAt: nowIso() }
: session,
),
);
}
export async function deleteSession(id: string): Promise<void> {
const sessions = await listSessions();
const next = sessions.filter((session) => session.id !== id);
await saveSessions(next);
const activeId = await getActiveSessionId();
if (activeId === id) {
if (next[0]) {
await setActiveSession(next[0].id);
} else {
await LocalStorage.removeItem(ACTIVE_SESSION_STORAGE_KEY);
}
}
}
export async function deleteAllSessions(): Promise<void> {
await LocalStorage.removeItem(SESSIONS_STORAGE_KEY);
await LocalStorage.removeItem(ACTIVE_SESSION_STORAGE_KEY);
}
export function countMappingEntries(mapping: PlaceholderMapping): number {
return Object.keys(mapping).length;
}