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>
This commit is contained in:
192
src/sessions.ts
Normal file
192
src/sessions.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user