Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a483188
Decouple from Rowboat cloud: make app fully self-hosted/BYOK
Apr 26, 2026
7edc977
Composio key fixture, remove Fireflies, perf optimizations
Apr 26, 2026
0e36640
Fix white screen crash: undefined hasUnconnectedNotes + React.lazy na…
Apr 26, 2026
0b1faa7
Remove direct Google OAuth — Gmail/Calendar now connect via Composio …
Apr 26, 2026
99e5eb7
Add Supermemory integration — Memory tab replaces Connected Accounts
Apr 26, 2026
0ecc17c
Merge pull request #1 from gokulb20/feature/decouple-rowboat-cloud
gokulb20 Apr 26, 2026
755ce70
Merge pull request #2 from gokulb20/feature/remove-fireflies-composio…
gokulb20 Apr 26, 2026
afc5247
Merge pull request #3 from gokulb20/feature/bugfix-white-screen
gokulb20 Apr 26, 2026
f972980
Merge pull request #4 from gokulb20/feature/super-memory
gokulb20 Apr 26, 2026
67684a4
Rebrand: remove Rowboat branding, replace with generic Assistant
Apr 26, 2026
d86ff3e
Merge pull request #5 from gokulb20/feature/rebrand-assistant
gokulb20 Apr 26, 2026
8ec8e82
Replace Suggested Topics with Meeting Notepad
Apr 26, 2026
85dfb95
Merge pull request #6 from gokulb20/feature/meeting-notepad
gokulb20 Apr 26, 2026
1709014
Make browser the default file/artifact viewer
Apr 26, 2026
9b45704
Merge pull request #7 from gokulb20/feature/browser-file-viewer
gokulb20 Apr 26, 2026
acf77b9
Browser universal viewer + customizable homepage
Apr 27, 2026
034b7a5
Fix file opens in browser + add Home sidebar button
Apr 27, 2026
f7caee4
Add /local-file endpoint for viewing external files in browser
Apr 27, 2026
4816447
Fix Home URL to gokuls.vision and fix /local-file tilde resolution
Apr 27, 2026
dfea6e4
Add Vault: unified file access with alias-based routing
Apr 27, 2026
61f8a12
Add error fallback for browser:newTab in SystemFileCard
Apr 27, 2026
3521032
Unified header, multi-pane left pane, remove meetings, fix search/bro…
Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions apps/x/apps/main/forge.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ const pkg = require('./package.json');

module.exports = {
packagerConfig: {
executableName: 'rowboat',
executableName: 'assistant',
icon: './icons/icon', // .icns extension added automatically
appBundleId: 'com.rowboat.app',
appBundleId: 'com.assistant.app',
appCategoryType: 'public.app-category.productivity',
extendInfo: {
NSAudioCaptureUsageDescription: 'Rowboat needs access to system audio to transcribe meetings from other apps (Zoom, Meet, etc.)',
NSAudioCaptureUsageDescription: 'This app needs access to system audio to transcribe meetings from other apps (Zoom, Meet, etc.)',
},
osxSign: {
batchCodesignCalls: true,
Expand Down Expand Up @@ -43,38 +43,38 @@ module.exports = {
name: '@electron-forge/maker-dmg',
config: (arch) => ({
format: 'ULFO',
name: `Rowboat-darwin-${arch}-${pkg.version}`, // Architecture-specific name to avoid conflicts
name: `Assistant-darwin-${arch}-${pkg.version}`,
})
},
{
name: '@electron-forge/maker-squirrel',
config: (arch) => ({
authors: 'rowboatlabs',
authors: 'gokulb20',
description: 'AI coworker with memory',
name: `Rowboat-win32-${arch}`,
setupExe: `Rowboat-win32-${arch}-${pkg.version}-setup.exe`,
name: `Assistant-win32-${arch}`,
setupExe: `Assistant-win32-${arch}-${pkg.version}-setup.exe`,
})
},
{
name: '@electron-forge/maker-deb',
config: (arch) => ({
options: {
name: `Rowboat-linux`,
bin: "rowboat",
name: `Assistant-linux`,
bin: "assistant",
description: 'AI coworker with memory',
maintainer: 'rowboatlabs',
homepage: 'https://rowboatlabs.com'
maintainer: 'gokulb20',
homepage: 'https://github.com/gokulb20/rowboat'
}
})
},
{
name: '@electron-forge/maker-rpm',
config: {
options: {
name: `Rowboat-linux`,
bin: "rowboat",
name: `Assistant-linux`,
bin: "assistant",
description: 'AI coworker with memory',
homepage: 'https://rowboatlabs.com'
homepage: 'https://github.com/gokulb20/rowboat'
}
}
},
Expand All @@ -88,7 +88,7 @@ module.exports = {
name: '@electron-forge/publisher-github',
config: {
repository: {
owner: 'rowboatlabs',
owner: 'gokulb20',
name: 'rowboat'
},
prerelease: true
Expand Down
4 changes: 2 additions & 2 deletions apps/x/apps/main/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rowboat",
"productName": "Rowboat",
"name": "assistant",
"productName": "Assistant",
"description": "AI coworker with memory",
"type": "module",
"version": "0.1.0",
Expand Down
16 changes: 15 additions & 1 deletion apps/x/apps/main/src/browser/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const BROWSER_PARTITION = 'persist:rowboat-browser';
const SPOOF_UA =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36';

const HOME_URL = 'https://www.google.com';
const HOME_URL = 'http://localhost:3210/sites/homepage/';
const NAVIGATION_TIMEOUT_MS = 10000;
const POST_ACTION_IDLE_MS = 400;
const POST_ACTION_MAX_ELEMENTS = 25;
Expand Down Expand Up @@ -231,6 +231,20 @@ export class BrowserViewManager extends EventEmitter {
}
return { action: 'deny' };
});

// Intercept rowboat:// protocol URLs for internal actions (e.g. md-edit)
wc.on('will-navigate', (event, url) => {
if (url.startsWith('rowboat://')) {
event.preventDefault();
const parsed = new URL(url);
if (parsed.host === 'md-edit') {
const filePath = parsed.searchParams.get('path');
if (filePath && this.window && !this.window.isDestroyed()) {
this.window.webContents.send('browser:mdEdit', { path: filePath });
}
}
}
});
}

private snapshotTabState(tab: BrowserTab): BrowserTabState {
Expand Down
43 changes: 22 additions & 21 deletions apps/x/apps/main/src/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import container from '@x/core/dist/di/container.js';
import { listOnboardingModels } from '@x/core/dist/models/models-dev.js';
import { testModelConnection } from '@x/core/dist/models/models.js';
import { isSignedIn } from '@x/core/dist/account/account.js';
import { listGatewayModels } from '@x/core/dist/models/gateway.js';
import type { IModelConfigRepo } from '@x/core/dist/models/repo.js';
import type { IOAuthRepo } from '@x/core/dist/auth/repo.js';
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
Expand All @@ -40,10 +39,8 @@ import { triggerRun as triggerAgentScheduleRun } from '@x/core/dist/agent-schedu
import { search } from '@x/core/dist/search/search.js';
import { versionHistory, voice } from '@x/core';
import { classifySchedule, processRowboatInstruction } from '@x/core/dist/knowledge/inline_tasks.js';
import { getBillingInfo } from '@x/core/dist/billing/billing.js';
import { summarizeMeeting } from '@x/core/dist/knowledge/summarize_meeting.js';
import { getAccessToken } from '@x/core/dist/auth/tokens.js';
import { getRowboatConfig } from '@x/core/dist/config/rowboat.js';
import { triggerTrackUpdate } from '@x/core/dist/knowledge/track/runner.js';
import { trackBus } from '@x/core/dist/knowledge/track/bus.js';
import {
Expand Down Expand Up @@ -480,9 +477,6 @@ export function setupIpcHandlers() {
return { success: true };
},
'models:list': async () => {
if (await isSignedIn()) {
return await listGatewayModels();
}
return await listOnboardingModels();
},
'models:test': async (_event, args) => {
Expand Down Expand Up @@ -511,19 +505,8 @@ export function setupIpcHandlers() {
return { config };
},
'account:getRowboat': async () => {
const signedIn = await isSignedIn();
if (!signedIn) {
return { signedIn: false, accessToken: null, config: null };
}

const config = await getRowboatConfig();

try {
const accessToken = await getAccessToken();
return { signedIn: true, accessToken, config };
} catch {
return { signedIn: true, accessToken: null, config };
}
// Rowboat cloud sign-in has been removed
return { signedIn: false, accessToken: null, config: null };
},
'granola:getConfig': async () => {
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');
Expand Down Expand Up @@ -606,6 +589,24 @@ export function setupIpcHandlers() {
'composio:use-composio-for-google-calendar': async () => {
return composioHandler.useComposioForGoogleCalendar();
},
// Supermemory integration handlers
'supermemory:is-configured': async () => {
const { isConfigured } = await import('@x/core/dist/supermemory/client.js');
return { configured: await isConfigured() };
},
'supermemory:set-api-key': async (_event, args) => {
try {
const { setApiKey } = await import('@x/core/dist/supermemory/client.js');
setApiKey(args.apiKey);
return { success: true };
} catch (e) {
return { success: false, error: e instanceof Error ? e.message : 'Failed to save' };
}
},
'supermemory:test-connection': async () => {
const { testConnection } = await import('@x/core/dist/supermemory/client.js');
return { success: await testConnection() };
},
// Agent schedule handlers
'agent-schedule:getConfig': async () => {
const repo = container.resolve<IAgentScheduleRepo>('agentScheduleRepo');
Expand Down Expand Up @@ -822,9 +823,9 @@ export function setupIpcHandlers() {
return { success: false, error: err instanceof Error ? err.message : String(err) };
}
},
// Billing handler
// Billing handler — no longer available (Rowboat cloud removed)
'billing:getInfo': async () => {
return await getBillingInfo();
return { userEmail: null, userId: null, subscriptionPlan: null, subscriptionStatus: null, trialExpiresAt: null, sanctionedCredits: 0, availableCredits: 0 };
},
// Embedded browser handlers (WebContentsView + navigation)
...browserIpcHandlers,
Expand Down
66 changes: 47 additions & 19 deletions apps/x/apps/main/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { dirname } from "node:path";
import { updateElectronApp, UpdateSourceType } from "update-electron-app";
import { init as initGmailSync } from "@x/core/dist/knowledge/sync_gmail.js";
import { init as initCalendarSync } from "@x/core/dist/knowledge/sync_calendar.js";
import { init as initFirefliesSync } from "@x/core/dist/knowledge/sync_fireflies.js";

import { init as initGranolaSync } from "@x/core/dist/knowledge/granola/sync.js";
import { init as initGraphBuilder } from "@x/core/dist/knowledge/build_graph.js";
import { init as initEmailLabeling } from "@x/core/dist/knowledge/label_emails.js";
Expand All @@ -28,6 +28,8 @@ import { init as initTrackEventProcessor } from "@x/core/dist/knowledge/track/ev
import { init as initLocalSites, shutdown as shutdownLocalSites } from "@x/core/dist/local-sites/server.js";

import { initConfigs } from "@x/core/dist/config/initConfigs.js";
import { WorkDir } from "@x/core/dist/config/config.js";
import fs from "node:fs";
import started from "electron-squirrel-startup";
import { execSync, exec, execFileSync } from "node:child_process";
import { promisify } from "node:util";
Expand Down Expand Up @@ -203,16 +205,9 @@ app.whenReady().then(async () => {
registerAppProtocol();
}

// Initialize auto-updater (only in production)
if (app.isPackaged) {
updateElectronApp({
updateSource: {
type: UpdateSourceType.ElectronPublicUpdateService,
repo: "rowboatlabs/rowboat",
},
notifyUser: true, // Shows native dialog when update is available
});
}
// Auto-updater disabled (Rowboat cloud dependencies removed)
// To re-enable, point this to your own fork:
// repo: "gokulb20/rowboat"

// Ensure agent-slack CLI is available
try {
Expand Down Expand Up @@ -259,17 +254,50 @@ app.whenReady().then(async () => {
// start track event processor (consumes events/pending/, triggers matching tracks)
initTrackEventProcessor();

// start gmail sync
initGmailSync();
// start gmail sync — only if configured
if (fs.existsSync(path.join(WorkDir, 'config', 'composio.json'))) {
initGmailSync();
} else {
console.log('[main] Gmail sync disabled — no Composio config');
}

// start calendar sync
initCalendarSync();
// start calendar sync — only if configured
if (fs.existsSync(path.join(WorkDir, 'config', 'composio.json'))) {
initCalendarSync();
} else {
console.log('[main] Calendar sync disabled — no Composio config');
}

// start fireflies sync
initFirefliesSync();
// fireflies sync removed — not used

// start granola sync
initGranolaSync();
// seed Composio API key from environment if not already configured
try {
const { getApiKey, setApiKey } = await import('@x/core/dist/composio/client.js');
if (!getApiKey() && process.env.COMPOSIO_API_KEY) {
setApiKey(process.env.COMPOSIO_API_KEY);
console.log('[main] Seeded Composio API key from environment');
}
} catch (e) {
console.error('[main] Failed to seed Composio API key:', e);
}

// seed Supermemory API key from environment if not already configured
try {
const sm = await import('@x/core/dist/supermemory/client.js');
if (!sm.getApiKey() && process.env.SUPERMEMORY_API_KEY) {
sm.setApiKey(process.env.SUPERMEMORY_API_KEY);
console.log('[main] Seeded Supermemory API key from environment');
}
} catch (e) {
console.error('[main] Failed to seed Supermemory API key:', e);
}

// start granola sync — only if configured
if (fs.existsSync(path.join(WorkDir, 'config', 'granola.json'))) {
initGranolaSync();
} else {
console.log('[main] Granola sync disabled — no config');
}

// start knowledge graph builder
initGraphBuilder();
Expand Down
15 changes: 0 additions & 15 deletions apps/x/apps/main/src/oauth-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import { IOAuthRepo } from '@x/core/dist/auth/repo.js';
import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js';
import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js';
import { triggerSync as triggerFirefliesSync } from '@x/core/dist/knowledge/sync_fireflies.js';
import { emitOAuthEvent } from './ipc.js';
import { getBillingInfo } from '@x/core/dist/billing/billing.js';

const REDIRECT_URI = 'http://localhost:8080/oauth/callback';

Expand Down Expand Up @@ -268,19 +266,6 @@ export async function connectProvider(provider: string, credentials?: { clientId
if (provider === 'google') {
triggerGmailSync();
triggerCalendarSync();
} else if (provider === 'fireflies-ai') {
triggerFirefliesSync();
}

// For Rowboat sign-in, ensure user + Stripe customer exist before
// notifying the renderer. Without this, parallel API calls from
// multiple renderer hooks race to create the user, causing duplicates.
if (provider === 'rowboat') {
try {
await getBillingInfo();
} catch (meError) {
console.error('[OAuth] Failed to initialize user via /v1/me:', meError);
}
}

// Emit success event to renderer
Expand Down
4 changes: 2 additions & 2 deletions apps/x/apps/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rowboat</title>
<title>Assistant</title>
<style>
/* Prevent flash of white background before CSS loads */
html, body { margin: 0; padding: 0; }
Expand All @@ -14,7 +14,7 @@
<script>
// Apply theme class immediately before render
(function() {
var stored = localStorage.getItem('rowboat-theme');
var stored = localStorage.getItem('app-theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
var theme = stored || 'system';
var resolved = theme === 'system' ? (prefersDark ? 'dark' : 'light') : theme;
Expand Down
Loading