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
6 changes: 6 additions & 0 deletions backend/src/lib/api-docs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2729,6 +2729,9 @@ export const AppConnections = {
FLYIO: {
accessToken: "The Access Token used to access fly.io."
},
DEVIN: {
apiKey: "The Devin service-user API key used to authenticate against the Devin v3 API."
},
GITLAB: {
instanceUrl: "The GitLab instance URL to connect with.",
accessToken: "The Access Token used to access GitLab.",
Expand Down Expand Up @@ -3053,6 +3056,9 @@ export const SecretSyncs = {
FLYIO: {
appId: "The ID of the Fly.io app to sync secrets to."
},
DEVIN: {
orgId: "The Devin organization ID to sync secrets to."
},
GITLAB: {
projectId: "The GitLab Project ID to sync secrets to.",
projectName: "The GitLab Project Name to sync secrets to.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
SanitizedDatabricksConnectionSchema
} from "@app/services/app-connection/databricks";
import { DbtConnectionListItemSchema, SanitizedDbtConnectionSchema } from "@app/services/app-connection/dbt";
import { DevinConnectionListItemSchema, SanitizedDevinConnectionSchema } from "@app/services/app-connection/devin";
import {
DigiCertConnectionListItemSchema,
SanitizedDigiCertConnectionSchema
Expand Down Expand Up @@ -242,6 +243,7 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedDbtConnectionSchema.options,
...SanitizedOpenRouterConnectionSchema.options,
...SanitizedAnthropicConnectionSchema.options,
...SanitizedDevinConnectionSchema.options,
...SanitizedAzureEntraIdConnectionSchema.options,
...SanitizedVenafiConnectionSchema.options,
...SanitizedVenafiTppConnectionSchema.options,
Expand Down Expand Up @@ -312,6 +314,7 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
DopplerConnectionListItemSchema,
NetScalerConnectionListItemSchema,
AnthropicConnectionListItemSchema,
DevinConnectionListItemSchema,
OnaConnectionListItemSchema,
DigiCertConnectionListItemSchema,
TravisCIConnectionListItemSchema
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
CreateDevinConnectionSchema,
SanitizedDevinConnectionSchema,
UpdateDevinConnectionSchema
} from "@app/services/app-connection/devin";

import { registerAppConnectionEndpoints } from "./app-connection-endpoints";

export const registerDevinConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({
app: AppConnection.Devin,
server,
sanitizedResponseSchema: SanitizedDevinConnectionSchema,
createSchema: CreateDevinConnectionSchema,
updateSchema: UpdateDevinConnectionSchema
});
};
2 changes: 2 additions & 0 deletions backend/src/server/routes/v1/app-connection-routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { registerCircleCIConnectionRouter } from "./circleci-connection-router";
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
import { registerDbtConnectionRouter } from "./dbt-connection-router";
import { registerDevinConnectionRouter } from "./devin-connection-router";
import { registerDigiCertConnectionRouter } from "./digicert-connection-router";
import { registerDigitalOceanConnectionRouter } from "./digital-ocean-connection-router";
import { registerDNSMadeEasyConnectionRouter } from "./dns-made-easy-connection-router";
Expand Down Expand Up @@ -124,6 +125,7 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.Doppler]: registerDopplerConnectionRouter,
[AppConnection.NetScaler]: registerNetScalerConnectionRouter,
[AppConnection.Anthropic]: registerAnthropicConnectionRouter,
[AppConnection.Devin]: registerDevinConnectionRouter,
[AppConnection.Ona]: registerOnaConnectionRouter,
[AppConnection.DigiCert]: registerDigiCertConnectionRouter,
[AppConnection.TravisCI]: registerTravisCIConnectionRouter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CreateDevinSyncSchema, DevinSyncSchema, UpdateDevinSyncSchema } from "@app/services/secret-sync/devin";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";

import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";

export const registerDevinSyncRouter = async (server: FastifyZodProvider) =>
registerSyncSecretsEndpoints({
destination: SecretSync.Devin,
server,
responseSchema: DevinSyncSchema,
createSchema: CreateDevinSyncSchema,
updateSchema: UpdateDevinSyncSchema
});
2 changes: 2 additions & 0 deletions backend/src/server/routes/v1/secret-sync-routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { registerCircleCISyncRouter } from "./circleci-sync-router";
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
import { registerDevinSyncRouter } from "./devin-sync-router";
import { registerDigitalOceanAppPlatformSyncRouter } from "./digital-ocean-app-platform-sync-router";
import { registerExternalInfisicalSyncRouter } from "./external-infisical-sync-router";
import { registerFlyioSyncRouter } from "./flyio-sync-router";
Expand Down Expand Up @@ -80,6 +81,7 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
[SecretSync.CircleCI]: registerCircleCISyncRouter,
[SecretSync.AzureEntraIdScim]: registerAzureEntraIdScimSyncRouter,
[SecretSync.ExternalInfisical]: registerExternalInfisicalSyncRouter,
[SecretSync.Devin]: registerDevinSyncRouter,
[SecretSync.Ona]: registerOnaSyncRouter,
[SecretSync.TravisCI]: registerTravisCISyncRouter
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
CloudflareWorkersSyncSchema
} from "@app/services/secret-sync/cloudflare-workers/cloudflare-workers-schemas";
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
import { DevinSyncListItemSchema, DevinSyncSchema } from "@app/services/secret-sync/devin";
import {
DigitalOceanAppPlatformSyncListItemSchema,
DigitalOceanAppPlatformSyncSchema
Expand Down Expand Up @@ -107,6 +108,7 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
CircleCISyncSchema,
AzureEntraIdScimSyncSchema,
ExternalInfisicalSyncSchema,
DevinSyncSchema,
OnaSyncSchema,
TravisCISyncSchema
]);
Expand Down Expand Up @@ -149,6 +151,7 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
CircleCISyncListItemSchema,
AzureEntraIdScimSyncListItemSchema,
ExternalInfisicalSyncListItemSchema,
DevinSyncListItemSchema,
OnaSyncListItemSchema,
TravisCISyncListItemSchema
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export enum AppConnection {
Doppler = "doppler",
NetScaler = "netscaler",
Anthropic = "anthropic",
Devin = "devin",
Ona = "ona",
DigiCert = "digicert",
TravisCI = "travis-ci"
Expand Down
5 changes: 5 additions & 0 deletions backend/src/services/app-connection/app-connection-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import {
validateDatabricksConnectionCredentials
} from "./databricks/databricks-connection-fns";
import { DbtConnectionMethod, getDbtConnectionListItem, validateDbtConnectionCredentials } from "./dbt";
import { DevinConnectionMethod, getDevinConnectionListItem, validateDevinConnectionCredentials } from "./devin";
import {
DigiCertConnectionMethod,
getDigiCertConnectionListItem,
Expand Down Expand Up @@ -308,6 +309,7 @@ export const listAppConnectionOptions = (projectType?: ProjectType) => {
getSmbConnectionListItem(),
getOpenRouterConnectionListItem(),
getAnthropicConnectionListItem(),
getDevinConnectionListItem(),
getCircleCIConnectionListItem(),
getAzureEntraIdConnectionListItem(),
getVenafiConnectionListItem(),
Expand Down Expand Up @@ -460,6 +462,7 @@ export const validateAppConnectionCredentials = async (
[AppConnection.SMB]: validateSmbConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.OpenRouter]: validateOpenRouterConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Anthropic]: validateAnthropicConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Devin]: validateDevinConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.CircleCI]: validateCircleCIConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.AzureEntraId]: validateAzureEntraIdConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Venafi]: validateVenafiConnectionCredentials as TAppConnectionCredentialsValidator,
Expand Down Expand Up @@ -565,6 +568,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
case OctopusDeployConnectionMethod.ApiKey:
case OpenRouterConnectionMethod.ApiKey:
case AnthropicConnectionMethod.ApiKey:
case DevinConnectionMethod.ApiKey:
case DigiCertConnectionMethod.ApiKey:
return "API Key";
case ChefConnectionMethod.UserKey:
Expand Down Expand Up @@ -682,6 +686,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.SMB]: platformManagedCredentialsNotSupported,
[AppConnection.OpenRouter]: platformManagedCredentialsNotSupported,
[AppConnection.Anthropic]: platformManagedCredentialsNotSupported,
[AppConnection.Devin]: platformManagedCredentialsNotSupported,
[AppConnection.CircleCI]: platformManagedCredentialsNotSupported,
[AppConnection.AzureEntraId]: platformManagedCredentialsNotSupported,
[AppConnection.Venafi]: platformManagedCredentialsNotSupported,
Expand Down
2 changes: 2 additions & 0 deletions backend/src/services/app-connection/app-connection-maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.Doppler]: "Doppler",
[AppConnection.NetScaler]: "NetScaler",
[AppConnection.Anthropic]: "Anthropic",
[AppConnection.Devin]: "Devin",
[AppConnection.Ona]: "Ona",
[AppConnection.DigiCert]: "DigiCert",
[AppConnection.TravisCI]: "Travis CI"
Expand Down Expand Up @@ -123,6 +124,7 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
[AppConnection.Doppler]: AppConnectionPlanType.Regular,
[AppConnection.NetScaler]: AppConnectionPlanType.Regular,
[AppConnection.Anthropic]: AppConnectionPlanType.Regular,
[AppConnection.Devin]: AppConnectionPlanType.Regular,
[AppConnection.Ona]: AppConnectionPlanType.Regular,
[AppConnection.DigiCert]: AppConnectionPlanType.Regular,
[AppConnection.TravisCI]: AppConnectionPlanType.Regular
Expand Down
2 changes: 2 additions & 0 deletions backend/src/services/app-connection/app-connection-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
import { databricksConnectionService } from "./databricks/databricks-connection-service";
import { ValidateDbtConnectionCredentialsSchema } from "./dbt";
import { dbtConnectionService } from "./dbt/dbt-connection-service";
import { ValidateDevinConnectionCredentialsSchema } from "./devin";
import { ValidateDigiCertConnectionCredentialsSchema } from "./digicert/digicert-connection-schemas";
import { digicertConnectionService } from "./digicert/digicert-connection-service";
import { ValidateDigitalOceanConnectionCredentialsSchema } from "./digital-ocean";
Expand Down Expand Up @@ -231,6 +232,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.Doppler]: ValidateDopplerConnectionCredentialsSchema,
[AppConnection.NetScaler]: ValidateNetScalerConnectionCredentialsSchema,
[AppConnection.Anthropic]: ValidateAnthropicConnectionCredentialsSchema,
[AppConnection.Devin]: ValidateDevinConnectionCredentialsSchema,
[AppConnection.Ona]: ValidateOnaConnectionCredentialsSchema,
[AppConnection.DigiCert]: ValidateDigiCertConnectionCredentialsSchema,
[AppConnection.TravisCI]: ValidateTravisCIConnectionCredentialsSchema
Expand Down
10 changes: 10 additions & 0 deletions backend/src/services/app-connection/app-connection-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ import {
TDbtConnectionInput,
TValidateDbtConnectionCredentialsSchema
} from "./dbt";
import {
TDevinConnection,
TDevinConnectionConfig,
TDevinConnectionInput,
TValidateDevinConnectionCredentialsSchema
} from "./devin";
import {
TDigiCertConnection,
TDigiCertConnectionConfig,
Expand Down Expand Up @@ -419,6 +425,7 @@ export type TAppConnection = { id: string } & (
| TDopplerConnection
| TNetScalerConnection
| TAnthropicConnection
| TDevinConnection
| TOnaConnection
| TDigiCertConnection
| TTravisCIConnection
Expand Down Expand Up @@ -491,6 +498,7 @@ export type TAppConnectionInput = { id: string } & (
| TDopplerConnectionInput
| TNetScalerConnectionInput
| TAnthropicConnectionInput
| TDevinConnectionInput
| TOnaConnectionInput
| TDigiCertConnectionInput
| TTravisCIConnectionInput
Expand Down Expand Up @@ -592,6 +600,7 @@ export type TAppConnectionConfig =
| TDopplerConnectionConfig
| TNetScalerConnectionConfig
| TAnthropicConnectionConfig
| TDevinConnectionConfig
| TOnaConnectionConfig
| TDigiCertConnectionConfig
| TTravisCIConnectionConfig;
Expand Down Expand Up @@ -655,6 +664,7 @@ export type TValidateAppConnectionCredentialsSchema =
| TValidateDopplerConnectionCredentialsSchema
| TValidateNetScalerConnectionCredentialsSchema
| TValidateAnthropicConnectionCredentialsSchema
| TValidateDevinConnectionCredentialsSchema
| TValidateOnaConnectionCredentialsSchema
| TValidateDigiCertConnectionCredentialsSchema
| TValidateTravisCIConnectionCredentialsSchema;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum DevinConnectionMethod {
ApiKey = "api-key"
}
41 changes: 41 additions & 0 deletions backend/src/services/app-connection/devin/devin-connection-fns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { AxiosError } from "axios";

import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";

import { DevinConnectionMethod } from "./devin-connection-enums";
import { TDevinConnectionConfig } from "./devin-connection-types";

export const getDevinConnectionListItem = () => {
return {
name: "Devin" as const,
app: AppConnection.Devin as const,
methods: Object.values(DevinConnectionMethod) as [DevinConnectionMethod.ApiKey]
};
};

export const validateDevinConnectionCredentials = async (config: TDevinConnectionConfig) => {
const { apiKey } = config.credentials;

try {
await request.get(`${IntegrationUrls.DEVIN_API_URL}/v3/self`, {
Comment thread
x032205 marked this conversation as resolved.
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json"
}
});
} catch (error: unknown) {
if (error instanceof AxiosError) {
throw new BadRequestError({
message: `Failed to validate credentials: ${error.message || "Unknown error"}`
});
}
throw new BadRequestError({
message: "Unable to validate connection: verify credentials"
});
}

return config.credentials;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import z from "zod";

import { AppConnections } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
BaseAppConnectionSchema,
GenericCreateAppConnectionFieldsSchema,
GenericUpdateAppConnectionFieldsSchema
} from "@app/services/app-connection/app-connection-schemas";

import { APP_CONNECTION_NAME_MAP } from "../app-connection-maps";
import { DevinConnectionMethod } from "./devin-connection-enums";

export const DevinConnectionApiKeyCredentialsSchema = z.object({
apiKey: z
.string()
.trim()
.min(1, "API Key required")
.max(1000)
.startsWith("cog_", "API Key must start with 'cog_'")
.describe(AppConnections.CREDENTIALS.DEVIN.apiKey)
});

const BaseDevinConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Devin) });

export const DevinConnectionSchema = BaseDevinConnectionSchema.extend({
method: z.literal(DevinConnectionMethod.ApiKey),
credentials: DevinConnectionApiKeyCredentialsSchema
});

export const SanitizedDevinConnectionSchema = z.discriminatedUnion("method", [
BaseDevinConnectionSchema.extend({
method: z.literal(DevinConnectionMethod.ApiKey),
credentials: DevinConnectionApiKeyCredentialsSchema.pick({})
}).describe(JSON.stringify({ title: `${APP_CONNECTION_NAME_MAP[AppConnection.Devin]} (API Key)` }))
]);

export const ValidateDevinConnectionCredentialsSchema = z.discriminatedUnion("method", [
z.object({
method: z.literal(DevinConnectionMethod.ApiKey).describe(AppConnections.CREATE(AppConnection.Devin).method),
credentials: DevinConnectionApiKeyCredentialsSchema.describe(AppConnections.CREATE(AppConnection.Devin).credentials)
})
]);

export const CreateDevinConnectionSchema = ValidateDevinConnectionCredentialsSchema.and(
GenericCreateAppConnectionFieldsSchema(AppConnection.Devin)
);

export const UpdateDevinConnectionSchema = z
.object({
credentials: DevinConnectionApiKeyCredentialsSchema.optional().describe(
AppConnections.UPDATE(AppConnection.Devin).credentials
)
})
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Devin));

export const DevinConnectionListItemSchema = z
.object({
name: z.literal("Devin"),
app: z.literal(AppConnection.Devin),
methods: z.nativeEnum(DevinConnectionMethod).array()
})
.describe(JSON.stringify({ title: APP_CONNECTION_NAME_MAP[AppConnection.Devin] }));
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import z from "zod";

import { DiscriminativePick } from "@app/lib/types";

import { AppConnection } from "../app-connection-enums";
import {
CreateDevinConnectionSchema,
DevinConnectionSchema,
ValidateDevinConnectionCredentialsSchema
} from "./devin-connection-schemas";

export type TDevinConnection = z.infer<typeof DevinConnectionSchema>;

export type TDevinConnectionInput = z.infer<typeof CreateDevinConnectionSchema> & {
app: AppConnection.Devin;
};

export type TValidateDevinConnectionCredentialsSchema = typeof ValidateDevinConnectionCredentialsSchema;

export type TDevinConnectionConfig = DiscriminativePick<TDevinConnectionInput, "method" | "app" | "credentials"> & {
orgId: string;
};
4 changes: 4 additions & 0 deletions backend/src/services/app-connection/devin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./devin-connection-enums";
export * from "./devin-connection-fns";
export * from "./devin-connection-schemas";
export * from "./devin-connection-types";
Loading
Loading