# CHANGES

- Added file system write capability for saving content directly 🚀
- Introduced unified API to fetch all named sessions across providers 🎯
- Enhanced session management with cross-provider named session support 📋
- Exposed writeFile method in preload for renderer process access ✍️
- Added getAllNamedSessions handler for aggregating session data 🔄
- Updated TabSwitcherModal to use new unified session API 🔧
- Extended type definitions for better TypeScript support 📝
- Improved session discovery across multiple storage providers 🔍
- Added error handling for multi-provider session aggregation 🛡️
- Streamlined named session retrieval for better performance 
This commit is contained in:
Pedram Amini
2025-12-17 23:48:14 -06:00
parent 16fbe638bb
commit 08fd1c2732
5 changed files with 84 additions and 1 deletions

View File

@@ -885,6 +885,15 @@ function setupIpcHandlers() {
}
});
ipcMain.handle('fs:writeFile', async (_, filePath: string, content: string) => {
try {
await fs.writeFile(filePath, content, 'utf-8');
return { success: true };
} catch (error) {
throw new Error(`Failed to write file: ${error}`);
}
});
// Live session management - toggle sessions as live/offline in web interface
ipcMain.handle('live:toggle', async (_, sessionId: string, agentSessionId?: string) => {
if (!webServer) {

View File

@@ -478,6 +478,51 @@ export function registerAgentSessionsHandlers(deps?: AgentSessionsHandlerDepende
})
);
// ============ Get All Named Sessions ============
ipcMain.handle(
'agentSessions:getAllNamedSessions',
withIpcErrorLogging(
handlerOpts('getAllNamedSessions'),
async (): Promise<
Array<{
agentSessionId: string;
projectPath: string;
sessionName: string;
starred?: boolean;
lastActivityAt?: number;
}>
> => {
// Aggregate named sessions from all providers that support it
const allNamedSessions: Array<{
agentSessionId: string;
projectPath: string;
sessionName: string;
starred?: boolean;
lastActivityAt?: number;
}> = [];
const storages = getAllSessionStorages();
for (const storage of storages) {
if ('getAllNamedSessions' in storage && typeof storage.getAllNamedSessions === 'function') {
try {
const sessions = await storage.getAllNamedSessions();
allNamedSessions.push(...sessions);
} catch (error) {
logger.warn(
`Failed to get named sessions from ${storage.agentId}: ${error}`,
LOG_CONTEXT
);
}
}
}
logger.info(`Found ${allNamedSessions.length} named sessions across all providers`, LOG_CONTEXT);
return allNamedSessions;
}
)
);
// ============ Get Global Stats (All Providers) ============
ipcMain.handle(

View File

@@ -353,6 +353,8 @@ contextBridge.exposeInMainWorld('maestro', {
homeDir: () => ipcRenderer.invoke('fs:homeDir') as Promise<string>,
readDir: (dirPath: string) => ipcRenderer.invoke('fs:readDir', dirPath),
readFile: (filePath: string) => ipcRenderer.invoke('fs:readFile', filePath),
writeFile: (filePath: string, content: string) =>
ipcRenderer.invoke('fs:writeFile', filePath, content) as Promise<{ success: boolean }>,
stat: (filePath: string) => ipcRenderer.invoke('fs:stat', filePath),
},
@@ -637,6 +639,15 @@ contextBridge.exposeInMainWorld('maestro', {
// Get global stats aggregated from all providers
getGlobalStats: () =>
ipcRenderer.invoke('agentSessions:getGlobalStats'),
// Get all named sessions across all providers
getAllNamedSessions: () =>
ipcRenderer.invoke('agentSessions:getAllNamedSessions') as Promise<Array<{
agentSessionId: string;
projectPath: string;
sessionName: string;
starred?: boolean;
lastActivityAt?: number;
}>>,
// Subscribe to global stats updates (streaming)
onGlobalStatsUpdate: (callback: (stats: {
totalSessions: number;
@@ -1027,6 +1038,7 @@ export interface MaestroAPI {
homeDir: () => Promise<string>;
readDir: (dirPath: string) => Promise<DirectoryEntry[]>;
readFile: (filePath: string) => Promise<string>;
writeFile: (filePath: string, content: string) => Promise<{ success: boolean }>;
stat: (filePath: string) => Promise<{
size: number;
createdAt: string;
@@ -1297,6 +1309,13 @@ export interface MaestroAPI {
deleteMessagePair: (agentId: string, projectPath: string, sessionId: string, userMessageUuid: string, fallbackContent?: string) => Promise<{ success: boolean; linesRemoved?: number; error?: string }>;
hasStorage: (agentId: string) => Promise<boolean>;
getAvailableStorages: () => Promise<string[]>;
getAllNamedSessions: () => Promise<Array<{
agentSessionId: string;
projectPath: string;
sessionName: string;
starred?: boolean;
lastActivityAt?: number;
}>>;
registerSessionOrigin: (projectPath: string, agentSessionId: string, origin: 'user' | 'auto', sessionName?: string) => Promise<boolean>;
updateSessionName: (projectPath: string, agentSessionId: string, sessionName: string) => Promise<boolean>;
};

View File

@@ -208,7 +208,7 @@ export function TabSwitcherModal({
)
);
// Then load all named sessions (including the ones we just synced)
const sessions = await window.maestro.claude.getAllNamedSessions();
const sessions = await window.maestro.agentSessions.getAllNamedSessions();
setNamedSessions(sessions);
setNamedSessionsLoaded(true);
};

View File

@@ -201,6 +201,7 @@ interface MaestroAPI {
fs: {
readDir: (dirPath: string) => Promise<DirectoryEntry[]>;
readFile: (filePath: string) => Promise<string>;
writeFile: (filePath: string, content: string) => Promise<{ success: boolean }>;
};
webserver: {
getUrl: () => Promise<string>;
@@ -328,6 +329,15 @@ interface MaestroAPI {
hasCostData: boolean;
}>;
}) => void) => () => void;
getAllNamedSessions: () => Promise<Array<{
agentSessionId: string;
projectPath: string;
sessionName: string;
starred?: boolean;
lastActivityAt?: number;
}>>;
registerSessionOrigin: (projectPath: string, agentSessionId: string, origin: 'user' | 'auto', sessionName?: string) => Promise<boolean>;
updateSessionName: (projectPath: string, agentSessionId: string, sessionName: string) => Promise<boolean>;
};
dialog: {
selectFolder: () => Promise<string | null>;