Ship Composio as a built-in integration and trim Settings

- Resolve the API key from cred.txt at the project root in development
  and the packaged resources directory in production; the file is the
  source of truth, so a rotated key replaces the stored one. Fixes the
  401 packaged builds hit when a stale database key won.
- Remove the user-facing Composio key editor; the key ships with the
  product and is not configurable.
- Remove the Migration console from Settings.
- Remove the Connectors and Home refresh buttons; connector statuses
  sync when the page opens.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-07 01:20:21 -04:00
1 parent 947cd8605b
commit 256e0680c6
16 files changed
+53 -396

No files matched your search

@@ -16,7 +16,7 @@ let catalogCache: { fetchedAt: number; toolkits: ComposioToolkitSummary[] } | nu
* Connectors page read local state instead of calling out on every frame.
*/
export class ComposioConnectionService {
constructor(private readonly db: DatabaseSync, private readonly credentialFileDir: string) {}
constructor(private readonly db: DatabaseSync) {}
listToolkits(orgId: string): Promise<ComposioToolkitSummaryDto[]> {
const client = this.requireClient(orgId);
@@ -81,7 +81,7 @@ export class ComposioConnectionService {
}
private requireClient(orgId: string): ComposioClient {
const apiKey = resolveComposioApiKey(this.db, orgId, this.credentialFileDir);
const apiKey = resolveComposioApiKey(this.db, orgId);
if (!apiKey) throw new Error("Composio API key is not configured for this company.");
return new ComposioClient(apiKey);
}
@@ -3,18 +3,19 @@ import { join } from "node:path";
import type { DatabaseSync } from "node:sqlite";
/**
* The org's Composio API key. A developer credential file next to the app
* (cred.txt, first line "Composio apikey: ak_...", gitignored) is the source
* of truth whenever it exists: rotating the key in the file takes effect on
* the next resolution, replacing whatever the database held. Packaged
* installs have no credential file and manage the key in Settings instead.
* The Composio integration key ships with the product: cred.txt (first line
* "Composio apikey: ak_...", gitignored) sits at the project root in
* development and inside the packaged app's resources directory in
* production. Wherever the file exists it is the source of truth — rotating
* the key in the file replaces whatever the database held on the next
* resolution. It is not user-configurable.
*/
export function resolveComposioApiKey(db: DatabaseSync, orgId: string, credentialFileDir: string = process.cwd()): string | null {
export function resolveComposioApiKey(db: DatabaseSync, orgId: string, credentialFileDirs: string[] = defaultCredentialDirs()): string | null {
const stored = db.prepare(
"select value from composio_settings where org_id = ? and key = 'api_key' limit 1",
).get(orgId) as { value: string } | undefined;
const fileKey = readCredentialFile(credentialFileDir);
const fileKey = readCredentialFile(credentialFileDirs);
if (!fileKey) return stored?.value ?? null;
if (stored?.value === fileKey) return fileKey;
@@ -28,10 +29,20 @@ export function resolveComposioApiKey(db: DatabaseSync, orgId: string, credentia
return fileKey;
}
function readCredentialFile(dir: string): string | null {
const path = join(dir, "cred.txt");
if (!existsSync(path)) return null;
const firstLine = readFileSync(path, "utf8").split("\n")[0] ?? "";
const key = firstLine.split(":").slice(1).join(":").trim();
return key || null;
/** Project root in dev; the app's resources directory when packaged. */
export function defaultCredentialDirs(): string[] {
const dirs = [process.cwd()];
if (process.resourcesPath) dirs.push(process.resourcesPath);
return dirs;
}
function readCredentialFile(dirs: string[]): string | null {
for (const dir of dirs) {
const path = join(dir, "cred.txt");
if (!existsSync(path)) continue;
const firstLine = readFileSync(path, "utf8").split("\n")[0] ?? "";
const key = firstLine.split(":").slice(1).join(":").trim();
if (key) return key;
}
return null;
}