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>
193 lines
5.3 KiB
TypeScript
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;
|
|
}
|