Skip to content

Commit

Permalink
added missing params for add cli command
Browse files Browse the repository at this point in the history
  • Loading branch information
plxity committed Dec 10, 2024
1 parent 7dce021 commit 2e4b647
Showing 1 changed file with 117 additions and 82 deletions.
199 changes: 117 additions & 82 deletions js/src/cli/add.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-console */
import chalk from "chalk";
import { Command } from "commander";
import { Composio } from "../sdk";
Expand All @@ -16,78 +15,92 @@ export default class AddCommand {
.description("Add a new app")
.argument("<app-name>", "The name of the app")
.option("-f, --force", "Force the connection setup")
.option(
"--skip-default-connector",
"Skip the default connector auth prompt"
)
.option("--skip-default-connector", "Skip the default connector auth prompt")
.option("-n, --no-browser", "Don't open browser for verifying connection")
.option("-i, --integration-id <id>", "Specify integration ID to use existing integration")
.option("-a, --auth-mode <mode>", "Specify auth mode for given app")
.option("-s, --scope <scope>", "Specify scopes for the connection", (value, previous: string[]) => previous.concat([value]), [])
.option("-l, --label <label>", "Labels for connected account", (value, previous: string[]) => previous.concat([value]), [])
.action(this.handleAction.bind(this));
}

private async handleAction(
appName: string,
options: { force?: boolean; skipDefaultConnector?: boolean }
options: {
force?: boolean,
skipDefaultConnector?: boolean,
noBrowser?: boolean,
integrationId?: string,
authMode?: string,
scope?: string[],
label?: string[]
},
): Promise<void> {
const composioClient = new Composio();
const integration: GetConnectorListResDTO | undefined =
await composioClient.integrations.list({
// @ts-ignore
let integration: GetConnectorInfoResDTO | GetConnectorListResDTO | undefined;

if (options.integrationId) {
integration = await composioClient.integrations.get({ integrationId: options.integrationId });
} else {
integration = await composioClient.integrations.list({
appName: appName.toLowerCase(),
});
}

let firstIntegration: GetConnectorInfoResDTO | undefined;
if (
integration?.items?.length === 0 ||
options.force ||
options.skipDefaultConnector
) {
firstIntegration = (await this.createIntegration(
if ((integration as GetConnectorListResDTO)?.items?.length === 0 || options.force || options.skipDefaultConnector) {
const integrationResult = await this.createIntegration(
appName,
options.skipDefaultConnector
)) as GetConnectorInfoResDTO;
options.skipDefaultConnector,
options.authMode,
options
);

if (integrationResult) {
firstIntegration = integrationResult as unknown as GetConnectorInfoResDTO;
}
} else {
firstIntegration = (integration as GetConnectorListResDTO)
?.items[0] as GetConnectorInfoResDTO;
firstIntegration = integration as GetConnectorInfoResDTO;
}
if (!firstIntegration) {
console.log(chalk.red("No integration found or created"));

Check warning on line 66 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 66 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
return;
}

const connection = await composioClient.connectedAccounts.list({
// @ts-ignore
integrationId: firstIntegration.id,
});

if (connection.items.length > 0 && !options.force) {
await this.shouldForceConnectionSetup();
}

// @ts-ignore
await this.setupConnections(firstIntegration.id);
if (firstIntegration && firstIntegration.id) {
await this.setupConnections(firstIntegration.id, options);
} else {
console.log(chalk.red("Integration ID is undefined"));

Check warning on line 81 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 81 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
}
}

async shouldForceConnectionSetup() {
const { shouldForce } = await inquirer.prompt([
{
type: "confirm",
name: "shouldForce",
message:
"A connection already exists. Do you want to force a new connection?",
type: 'confirm',
name: 'shouldForce',
message: 'A connection already exists. Do you want to force a new connection?',
default: false,
},
]);

if (!shouldForce) {
console.log(
chalk.yellow("Operation cancelled. Existing connection will be used.")
);
console.log(chalk.yellow('Operation cancelled. Existing connection will be used.'));

Check warning on line 96 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 96 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
process.exit(0);
}
}

private async waitUntilConnected(
connectedAccountId: string,
timeout: number = 30000
timeout: number = 30000,
): Promise<void> {
const composioClient = new Composio();
const startTime = Date.now();
Expand All @@ -110,71 +123,74 @@ export default class AddCommand {
}

throw new Error(
`Connection did not become active within ${timeout / 1000} seconds`
`Connection did not become active within ${timeout / 1000} seconds`,
);
}

private async setupConnections(integrationId: string): Promise<void> {
private async setupConnections(integrationId: string, options: any): Promise<void> {

Check warning on line 130 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected any. Specify a different type

Check warning on line 130 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected any. Specify a different type
const composioClient = new Composio();
const data = await composioClient.integrations.get({ integrationId });
const { expectedInputFields } = data!;

const config = await this.collectInputFields(
expectedInputFields as any,
true
);
const config = await this.collectInputFields(expectedInputFields as any, true);

Check warning on line 135 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected any. Specify a different type

Check warning on line 135 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected any. Specify a different type

if (options.scope) {
config.scopes = options.scope.join(",");
}

const connectionData = await composioClient.connectedAccounts.create({
integrationId,
data: config,
labels: options.label,
});

if (connectionData.connectionStatus === "ACTIVE") {
console.log(chalk.green("Connection created successfully"));

Check warning on line 148 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 148 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
}

if (connectionData.redirectUrl) {
if (connectionData.redirectUrl && !options.noBrowser) {
console.log(

Check warning on line 152 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 152 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
chalk.white("Redirecting to the app"),
chalk.blue(connectionData.redirectUrl)
chalk.blue(connectionData.redirectUrl),
);
open(connectionData.redirectUrl);

await this.waitUntilConnected(connectionData.connectedAccountId);

console.log(chalk.green("Connection is active"));

Check warning on line 160 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 160 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
process.exit(0);
} else if (connectionData.redirectUrl && options.noBrowser) {
console.log(

Check warning on line 163 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement

Check warning on line 163 in js/src/cli/add.ts

View workflow job for this annotation

GitHub Actions / lint-and-prettify

Unexpected console statement
chalk.white("Please authenticate the app by visiting the following URL:"),
chalk.blue(connectionData.redirectUrl),
);
console.log(chalk.green("Waiting for the connection to become active..."));

await this.waitUntilConnected(connectionData.connectedAccountId);

console.log(chalk.green("Connection is active"));
process.exit(0);
}
}

private async createIntegration(
appName: string,
skipDefaultConnectorAuth: boolean = false
) {
private async createIntegration(appName: string, skipDefaultConnectorAuth: boolean = false, userAuthMode?: string, options?: any) {
const composioClient = new Composio();
const app = await composioClient.apps.get({
appKey: appName.toLowerCase(),
});

if (app.no_auth) {
console.log(
chalk.green(
`The app '${appName}' does not require authentication. You can connect it directly.\n`
)
);
console.log(chalk.green(`The app '${appName}' does not require authentication. You can connect it directly.\n`));
process.exit(0);
}

const testConnectors = app.testConnectors || [];

const config: Record<string, any> = {};

let useComposioAuth = true;
if (
!app.no_auth &&
testConnectors.length > 0 &&
!skipDefaultConnectorAuth
) {
const authSchemeExpectOauth = ["bearer_token", "api_key", "basic"];
if (!app.no_auth && testConnectors.length > 0 && !skipDefaultConnectorAuth) {

const { doYouWantToUseComposioAuth } = await inquirer.prompt({
type: "confirm",
name: "doYouWantToUseComposioAuth",
Expand All @@ -190,7 +206,7 @@ export default class AddCommand {
const { integrationName } = await inquirer.prompt({
type: "input",
name: "integrationName",
message: "Enter the app name",
message: "Enter the Integration name",
});

if (!integrationName) {
Expand All @@ -199,35 +215,55 @@ export default class AddCommand {
}

config.name = integrationName;
// @ts-ignore
const authSchema = app.auth_schemes[0]?.auth_mode;
const authSchema: string | undefined = userAuthMode || (app.auth_schemes && app.auth_schemes[0]?.auth_mode as string | undefined);

if (useComposioAuth) {
useComposioAuth = true;
return this.setupIntegration(
app,
authSchema,
useComposioAuth,
config,
integrationName
);
const authModes = (app.auth_schemes || []).reduce((acc: Record<string, any>, scheme: any) => {
acc[scheme.auth_mode] = scheme;
return acc;
}, {});

if (authSchema && typeof authSchema === 'string' && !authModes[authSchema]) {
console.log(chalk.red(`Invalid value for auth_mode, select from ${Object.keys(authModes)}`));
return null;
}

console.log(
"\n\nWe'll require you to enter the credentials for the app manually.\n\n"
);
const selectedAuthMode = authSchema || Object.keys(authModes)[0];
const selectedAuthScheme = authModes[selectedAuthMode];

const authConfig = await this.collectInputFields(
// @ts-ignore
app.auth_schemes[0].fields
);
return this.setupIntegration(
app,
authSchema,
useComposioAuth,
if (authSchemeExpectOauth.includes(selectedAuthMode.toLowerCase())) {
return this.handleBasicAuth(app, selectedAuthMode, selectedAuthScheme, config, integrationName);
}

return this.handleOAuth(app, selectedAuthMode, selectedAuthScheme, config, integrationName, options.noBrowser, options.scope, useComposioAuth);
}

private async handleBasicAuth(app: any, authMode: string, authScheme: any, config: Record<string, any>, integrationName: string) {
const composioClient = new Composio();
const authConfig = await this.collectInputFields(authScheme.fields);

const integration = await composioClient.integrations.create({
appId: app.appId,
authScheme: authMode,
useComposioAuth: false,
name: integrationName,
authConfig,
integrationName
);
});

return integration;
}

private async handleOAuth(app: any, authMode: string, authScheme: any, config: Record<string, any>, integrationName: string, noBrowser: boolean, scopes: string[], useComposioAuth: boolean) {
if (useComposioAuth) {
return this.setupIntegration(app, authMode, useComposioAuth, {}, integrationName);
}

const authConfig = await this.collectInputFields(authScheme.fields);

if (scopes) {
authConfig.scopes = scopes.join(",");
}

return this.setupIntegration(app, authMode, useComposioAuth, authConfig, integrationName);
}

async collectInputFields(
Expand All @@ -239,7 +275,7 @@ export default class AddCommand {
required: boolean;
type: string;
}[],
isConnection = false
isConnection = false,
): Promise<Record<string, any>> {
const config: Record<string, any> = {};

Expand Down Expand Up @@ -267,7 +303,7 @@ export default class AddCommand {
authMode: any,
useComposioAuth: boolean,
config: Record<string, any>,
name: string
name: string,
) {
const composioClient = new Composio();
const integration = await composioClient.integrations.create({
Expand All @@ -277,7 +313,6 @@ export default class AddCommand {
name,
authConfig: config,
});

return integration;
}
}
}

0 comments on commit 2e4b647

Please sign in to comment.