Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 12 additions & 10 deletions packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
await webAdapter.start();
persistence.startPeriodicFlush();

// Mutable — pushed to as each adapter starts, read by the /api/health endpoint.
// Must be a live reference because Telegram starts after the HTTP listener begins
// accepting requests, so a snapshot taken at registration time would miss it.
const activePlatforms: string[] = ['Web'];

// Platform adapters (skipped in CLI serve mode or when not configured)
let github: GitHubAdapter | null = null;
let gitea: GiteaAdapter | null = null;
Expand Down Expand Up @@ -284,6 +289,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
botMention
);
await github.start();
activePlatforms.push('GitHub');
} else {
getLog().info('github_adapter_skipped');
}
Expand All @@ -300,6 +306,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
giteaBotMention
);
await gitea.start();
activePlatforms.push('Gitea');
} else {
getLog().info('gitea_adapter_skipped');
}
Expand All @@ -316,6 +323,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
gitlabBotMention
);
await gitlab.start();
activePlatforms.push('GitLab');
} else {
getLog().info('gitlab_adapter_skipped');
}
Expand Down Expand Up @@ -378,6 +386,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
});

await discord.start();
activePlatforms.push('Discord');
} else {
getLog().info('discord_adapter_skipped');
}
Expand Down Expand Up @@ -433,6 +442,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
});

await slack.start();
activePlatforms.push('Slack');
} else {
getLog().info('slack_adapter_skipped');
}
Expand All @@ -451,7 +461,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
});

// Register Web UI API routes
registerApiRoutes(app, webAdapter, lockManager);
registerApiRoutes(app, webAdapter, lockManager, activePlatforms);

// GitHub webhook endpoint
if (github) {
Expand Down Expand Up @@ -607,6 +617,7 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {

try {
await telegramAdapter.start();
activePlatforms.push('Telegram');
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
getLog().error({ err: error, errorType: error.constructor.name }, 'telegram.start_failed');
Expand Down Expand Up @@ -668,15 +679,6 @@ export async function startServer(opts: ServerOptions = {}): Promise<void> {
// the try/catch in claude.ts). These are SDK cleanup races, not fatal app errors.
process.on('unhandledRejection', handleUnhandledRejection);

// Show active platforms
const activePlatforms = ['Web'];
if (telegram) activePlatforms.push('Telegram');
if (discord) activePlatforms.push('Discord');
if (slack) activePlatforms.push('Slack');
if (github) activePlatforms.push('GitHub');
if (gitea) activePlatforms.push('Gitea');
if (gitlab) activePlatforms.push('GitLab');

getLog().info({ activePlatforms, port }, 'server_ready');

// Non-blocking: warn at startup if gh CLI auth is unavailable
Expand Down
5 changes: 4 additions & 1 deletion packages/server/src/routes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ const getHealthRoute = createRoute({
runningWorkflows: z.number(),
version: z.string().optional(),
is_docker: z.boolean(),
activePlatforms: z.array(z.string()).optional(),
})
.openapi('HealthResponse'),
},
Expand Down Expand Up @@ -851,7 +852,8 @@ const getUpdateCheckRoute = createRoute({
export function registerApiRoutes(
app: OpenAPIHono,
webAdapter: WebAdapter,
lockManager: ConversationLockManager
lockManager: ConversationLockManager,
activePlatforms?: readonly string[]
): void {
function apiError(
c: Context,
Expand Down Expand Up @@ -2548,6 +2550,7 @@ export function registerApiRoutes(
runningWorkflows: runningWorkflowRows.length,
version: appVersion,
is_docker: isDocker(),
activePlatforms: activePlatforms ? [...activePlatforms] : ['Web'],
});
});

Expand Down
1 change: 1 addition & 0 deletions packages/web/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface HealthResponse {
runningWorkflows: number;
version?: string;
is_docker: boolean;
activePlatforms?: string[];
}

async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {
Expand Down
19 changes: 11 additions & 8 deletions packages/web/src/routes/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -607,16 +607,19 @@ function AssistantConfigSection({ config }: { config: SafeConfigResponse }): Rea
}

function PlatformConnectionsSection({
adapter,
activePlatforms,
}: {
adapter: string | undefined;
activePlatforms: string[] | undefined;
}): React.ReactElement {
const active = new Set(activePlatforms ?? []);
const platforms = [
{ name: 'Web', connected: adapter === 'web' },
{ name: 'Slack', connected: false },
{ name: 'Telegram', connected: false },
{ name: 'Discord', connected: false },
{ name: 'GitHub', connected: false },
{ name: 'Web', connected: active.has('Web') },
{ name: 'Slack', connected: active.has('Slack') },
{ name: 'Telegram', connected: active.has('Telegram') },
{ name: 'Discord', connected: active.has('Discord') },
{ name: 'GitHub', connected: active.has('GitHub') },
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{ name: 'Gitea', connected: active.has('Gitea') },
{ name: 'GitLab', connected: active.has('GitLab') },
];

Comment on lines 609 to 624
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid showing “Not configured” before health data exists.

When activePlatforms is still undefined (initial load or health fetch failure), this renders every integration as Not configured. That reintroduces a false status during the exact window this PR is trying to fix. Render an Unknown/Loading state until the health payload arrives.

💡 Suggested tweak
 function PlatformConnectionsSection({
   activePlatforms,
 }: {
   activePlatforms: string[] | undefined;
 }): React.ReactElement {
+  const hasHealthData = activePlatforms !== undefined;
   const active = new Set(activePlatforms ?? []);
   const platforms = [
     { name: 'Web', connected: active.has('Web') },
     { name: 'Slack', connected: active.has('Slack') },
     { name: 'Telegram', connected: active.has('Telegram') },
@@
           {platforms.map(p => (
             <div key={p.name} className="flex items-center justify-between text-sm">
               <span>{p.name}</span>
-              <Badge variant={p.connected ? 'default' : 'secondary'}>
-                {p.connected ? 'Connected' : 'Not configured'}
+              <Badge variant={hasHealthData && p.connected ? 'default' : 'secondary'}>
+                {!hasHealthData ? 'Unknown' : p.connected ? 'Connected' : 'Not configured'}
               </Badge>
             </div>
           ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/routes/SettingsPage.tsx` around lines 608 - 623, The
platforms list currently marks integrations as not connected whenever
activePlatforms is undefined; change the connected value computation in
PlatformConnectionsSection so it is tri-state (true/false/undefined) by setting
connected: activePlatforms ? active.has('Web') : undefined (and likewise for
each platform name) instead of active.has(...), and update the component
rendering logic that consumes platforms (the code reading platforms[].connected)
to display an "Unknown"/"Loading" state when connected is undefined rather than
showing "Not configured".

return (
Expand Down Expand Up @@ -717,7 +720,7 @@ export function SettingsPage(): React.ReactElement {

<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
{configData && <AssistantConfigSection config={configData.config} />}
<PlatformConnectionsSection adapter={health?.adapter} />
<PlatformConnectionsSection activePlatforms={health?.activePlatforms} />
</div>

<ProjectsSection />
Expand Down
Loading