Skip to content

Commit 728857d

Browse files
committed
Merge branch 'dev' of github.com:rowboatlabs/rowboat into dev
2 parents c107d7c + 9d4f258 commit 728857d

File tree

15 files changed

+1290
-204
lines changed

15 files changed

+1290
-204
lines changed

apps/x/apps/main/src/ipc.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import os from 'node:os';
55
import {
66
connectProvider,
77
disconnectProvider,
8-
isConnected,
9-
getConnectedProviders,
108
listProviders,
119
} from './oauth-handler.js';
1210
import { watcher as watcherCore, workspace } from '@x/core';
@@ -24,6 +22,7 @@ import container from '@x/core/dist/di/container.js';
2422
import { listOnboardingModels } from '@x/core/dist/models/models-dev.js';
2523
import { testModelConnection } from '@x/core/dist/models/models.js';
2624
import type { IModelConfigRepo } from '@x/core/dist/models/repo.js';
25+
import type { IOAuthRepo } from '@x/core/dist/auth/repo.js';
2726
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
2827
import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granola/sync.js';
2928
import { isOnboardingComplete, markOnboardingComplete } from '@x/core/dist/config/note_creation_config.js';
@@ -364,19 +363,18 @@ export function setupIpcHandlers() {
364363
return { success: true };
365364
},
366365
'oauth:connect': async (_event, args) => {
367-
return await connectProvider(args.provider, args.clientId);
366+
return await connectProvider(args.provider, args.clientId?.trim());
368367
},
369368
'oauth:disconnect': async (_event, args) => {
370369
return await disconnectProvider(args.provider);
371370
},
372-
'oauth:is-connected': async (_event, args) => {
373-
return await isConnected(args.provider);
374-
},
375371
'oauth:list-providers': async () => {
376372
return listProviders();
377373
},
378-
'oauth:get-connected-providers': async () => {
379-
return await getConnectedProviders();
374+
'oauth:getState': async () => {
375+
const repo = container.resolve<IOAuthRepo>('oauthRepo');
376+
const config = await repo.getClientFacingConfig();
377+
return { config };
380378
},
381379
'granola:getConfig': async () => {
382380
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');

apps/x/apps/main/src/oauth-handler.ts

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ import { createAuthServer } from './auth-server.js';
44
import * as oauthClient from '@x/core/dist/auth/oauth-client.js';
55
import type { Configuration } from '@x/core/dist/auth/oauth-client.js';
66
import { getProviderConfig, getAvailableProviders } from '@x/core/dist/auth/providers.js';
7-
import {
8-
clearProviderClientIdOverride,
9-
getProviderClientIdOverride,
10-
hasProviderClientIdOverride,
11-
setProviderClientIdOverride,
12-
} from '@x/core/dist/auth/provider-client-id.js';
137
import container from '@x/core/dist/di/container.js';
148
import { IOAuthRepo } from '@x/core/dist/auth/repo.js';
159
import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
@@ -80,24 +74,28 @@ function getClientRegistrationRepo(): IClientRegistrationRepo {
8074
/**
8175
* Get or create OAuth configuration for a provider
8276
*/
83-
async function getProviderConfiguration(provider: string): Promise<Configuration> {
77+
async function getProviderConfiguration(provider: string, clientIdOverride?: string): Promise<Configuration> {
8478
const config = getProviderConfig(provider);
85-
const resolveClientId = (): string => {
86-
const override = getProviderClientIdOverride(provider);
87-
if (override) {
88-
return override;
89-
}
79+
const resolveClientId = async (): Promise<string> => {
9080
if (config.client.mode === 'static' && config.client.clientId) {
9181
return config.client.clientId;
9282
}
83+
if (clientIdOverride) {
84+
return clientIdOverride;
85+
}
86+
const oauthRepo = getOAuthRepo();
87+
const clientId = await oauthRepo.getClientId(provider);
88+
if (clientId) {
89+
return clientId;
90+
}
9391
throw new Error(`${provider} client ID not configured. Please provide a client ID.`);
9492
};
9593

9694
if (config.discovery.mode === 'issuer') {
9795
if (config.client.mode === 'static') {
9896
// Discover endpoints, use static client ID
9997
console.log(`[OAuth] ${provider}: Discovery from issuer with static client ID`);
100-
const clientId = resolveClientId();
98+
const clientId = await resolveClientId();
10199
return await oauthClient.discoverConfiguration(
102100
config.discovery.issuer,
103101
clientId
@@ -137,7 +135,7 @@ async function getProviderConfiguration(provider: string): Promise<Configuration
137135
}
138136

139137
console.log(`[OAuth] ${provider}: Using static endpoints (no discovery)`);
140-
const clientId = resolveClientId();
138+
const clientId = await resolveClientId();
141139
return oauthClient.createStaticConfiguration(
142140
config.discovery.authorizationEndpoint,
143141
config.discovery.tokenEndpoint,
@@ -161,15 +159,13 @@ export async function connectProvider(provider: string, clientId?: string): Prom
161159
const providerConfig = getProviderConfig(provider);
162160

163161
if (provider === 'google') {
164-
const trimmedClientId = clientId?.trim();
165-
if (!trimmedClientId) {
162+
if (!clientId) {
166163
return { success: false, error: 'Google client ID is required to connect.' };
167164
}
168-
setProviderClientIdOverride(provider, trimmedClientId);
169165
}
170166

171167
// Get or create OAuth configuration
172-
const config = await getProviderConfiguration(provider);
168+
const config = await getProviderConfiguration(provider, clientId);
173169

174170
// Generate PKCE codes
175171
const { verifier: codeVerifier, challenge: codeChallenge } = await oauthClient.generatePKCE();
@@ -217,6 +213,10 @@ export async function connectProvider(provider: string, clientId?: string): Prom
217213
// Save tokens
218214
console.log(`[OAuth] Token exchange successful for ${provider}`);
219215
await oauthRepo.saveTokens(provider, tokens);
216+
if (provider === 'google' && clientId) {
217+
await oauthRepo.setClientId(provider, clientId);
218+
}
219+
await oauthRepo.clearError(provider);
220220

221221
// Trigger immediate sync for relevant providers
222222
if (provider === 'google') {
@@ -282,33 +282,13 @@ export async function disconnectProvider(provider: string): Promise<{ success: b
282282
try {
283283
const oauthRepo = getOAuthRepo();
284284
await oauthRepo.clearTokens(provider);
285-
if (provider === 'google') {
286-
clearProviderClientIdOverride(provider);
287-
}
288285
return { success: true };
289286
} catch (error) {
290287
console.error('OAuth disconnect failed:', error);
291288
return { success: false };
292289
}
293290
}
294291

295-
/**
296-
* Check if a provider is connected
297-
*/
298-
export async function isConnected(provider: string): Promise<{ isConnected: boolean }> {
299-
try {
300-
const oauthRepo = getOAuthRepo();
301-
if (provider === 'google' && !hasProviderClientIdOverride(provider)) {
302-
return { isConnected: false };
303-
}
304-
const connected = await oauthRepo.isConnected(provider);
305-
return { isConnected: connected };
306-
} catch (error) {
307-
console.error('OAuth connection check failed:', error);
308-
return { isConnected: false };
309-
}
310-
}
311-
312292
/**
313293
* Get access token for a provider (internal use only)
314294
* Refreshes token if expired
@@ -326,6 +306,7 @@ export async function getAccessToken(provider: string): Promise<string | null> {
326306
if (oauthClient.isTokenExpired(tokens)) {
327307
if (!tokens.refresh_token) {
328308
// No refresh token, need to reconnect
309+
await oauthRepo.setError(provider, 'Missing refresh token. Please reconnect.');
329310
return null;
330311
}
331312

@@ -338,6 +319,8 @@ export async function getAccessToken(provider: string): Promise<string | null> {
338319
tokens = await oauthClient.refreshTokens(config, tokens.refresh_token, existingScopes);
339320
await oauthRepo.saveTokens(provider, tokens);
340321
} catch (error) {
322+
const message = error instanceof Error ? error.message : 'Token refresh failed';
323+
await oauthRepo.setError(provider, message);
341324
console.error('Token refresh failed:', error);
342325
return null;
343326
}
@@ -350,23 +333,6 @@ export async function getAccessToken(provider: string): Promise<string | null> {
350333
}
351334
}
352335

353-
/**
354-
* Get list of connected providers
355-
*/
356-
export async function getConnectedProviders(): Promise<{ providers: string[] }> {
357-
try {
358-
const oauthRepo = getOAuthRepo();
359-
const providers = await oauthRepo.getConnectedProviders();
360-
const filteredProviders = providers.filter((provider) =>
361-
provider === 'google' ? hasProviderClientIdOverride(provider) : true
362-
);
363-
return { providers: filteredProviders };
364-
} catch (error) {
365-
console.error('Get connected providers failed:', error);
366-
return { providers: [] };
367-
}
368-
}
369-
370336
/**
371337
* Get list of available providers
372338
*/

apps/x/apps/renderer/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"motion": "^12.23.26",
4444
"nanoid": "^5.1.6",
4545
"posthog-js": "^1.332.0",
46+
"radix-ui": "^1.4.3",
4647
"react": "^19.2.0",
4748
"react-dom": "^19.2.0",
4849
"sonner": "^2.0.7",

0 commit comments

Comments
 (0)