chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import * as client from '@botpress/client'
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import * as errors from '../errors'
|
||||
import * as utils from '../utils'
|
||||
import * as types from './types'
|
||||
|
||||
export const prepareCreateBotBody = async (bot: sdk.BotDefinition): Promise<types.CreateBotRequestBody> => ({
|
||||
user: bot.user,
|
||||
conversation: bot.conversation,
|
||||
message: bot.message,
|
||||
recurringEvents: bot.recurringEvents,
|
||||
actions: bot.actions
|
||||
? await utils.records.mapValuesAsync(bot.actions, async (action, actionName) => ({
|
||||
...action,
|
||||
input: {
|
||||
...action.input,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.input, {
|
||||
useLegacyZuiTransformer: bot.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: bot.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`Failed to convert ZUI to JSON schema for bot action ${actionName} input`
|
||||
)
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
...action.output,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.output, {
|
||||
useLegacyZuiTransformer: bot.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: bot.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`Failed to convert ZUI to JSON schema for bot action ${actionName} output`
|
||||
)
|
||||
}),
|
||||
},
|
||||
}))
|
||||
: undefined,
|
||||
configuration: bot.configuration
|
||||
? {
|
||||
...bot.configuration,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(bot.configuration, {
|
||||
useLegacyZuiTransformer: bot.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: bot.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, 'Failed to convert ZUI to JSON schema for bot configuration')
|
||||
}),
|
||||
}
|
||||
: undefined,
|
||||
events: bot.events
|
||||
? await utils.records.mapValuesAsync(bot.events, async (event, eventName) => ({
|
||||
...event,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(event, {
|
||||
useLegacyZuiTransformer: bot.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: bot.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`Failed to convert ZUI to JSON schema for bot event ${eventName}`
|
||||
)
|
||||
}),
|
||||
}))
|
||||
: undefined,
|
||||
states: bot.states
|
||||
? (utils.records.filterValues(
|
||||
await utils.records.mapValuesAsync(bot.states, async (state, stateName) => ({
|
||||
...state,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(state, {
|
||||
useLegacyZuiTransformer: bot.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: bot.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`Failed to convert ZUI to JSON schema for bot state ${stateName}`
|
||||
)
|
||||
}),
|
||||
})),
|
||||
({ type }) => type !== 'workflow'
|
||||
) as types.CreateBotRequestBody['states'])
|
||||
: undefined,
|
||||
tags: bot.attributes,
|
||||
})
|
||||
|
||||
export const prepareUpdateBotBody = (
|
||||
localBot: types.UpdateBotRequestBody,
|
||||
remoteBot: client.Bot
|
||||
): types.UpdateBotRequestBody => ({
|
||||
...localBot,
|
||||
states: utils.records.setNullOnMissingValues(localBot.states, remoteBot.states),
|
||||
recurringEvents: utils.records.setNullOnMissingValues(localBot.recurringEvents, remoteBot.recurringEvents),
|
||||
events: utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localBot.events, remoteBot.events),
|
||||
remoteItems: remoteBot.events,
|
||||
}),
|
||||
actions: utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localBot.actions, remoteBot.actions),
|
||||
remoteItems: remoteBot.actions,
|
||||
}),
|
||||
user: {
|
||||
...localBot.user,
|
||||
tags: utils.records.setNullOnMissingValues(localBot.user?.tags, remoteBot.user?.tags),
|
||||
},
|
||||
conversation: {
|
||||
...localBot.conversation,
|
||||
tags: utils.records.setNullOnMissingValues(localBot.conversation?.tags, remoteBot.conversation?.tags),
|
||||
},
|
||||
message: {
|
||||
...localBot.message,
|
||||
tags: utils.records.setNullOnMissingValues(localBot.message?.tags, remoteBot.message?.tags),
|
||||
},
|
||||
integrations: utils.integrations.prepareIntegrationsUpdate(
|
||||
utils.records.setNullOnMissingValues(localBot.integrations, remoteBot.integrations),
|
||||
remoteBot.integrations
|
||||
),
|
||||
plugins: utils.records.setNullOnMissingValues(localBot.plugins, remoteBot.plugins),
|
||||
tags: localBot.tags, // TODO: allow removing bot tags (aka attributes) by setting to null
|
||||
})
|
||||
@@ -0,0 +1,348 @@
|
||||
import * as client from '@botpress/client'
|
||||
import semver from 'semver'
|
||||
import yn from 'yn'
|
||||
import * as errors from '../errors'
|
||||
import type { Logger } from '../logger'
|
||||
import { formatPackageRef, ApiPackageRef, NamePackageRef } from '../package-ref'
|
||||
import * as utils from '../utils'
|
||||
import * as paging from './paging'
|
||||
import * as retry from './retry'
|
||||
|
||||
import {
|
||||
ApiClientProps,
|
||||
PublicOrUnlistedIntegration,
|
||||
PrivateIntegration,
|
||||
PublicOrPrivateIntegration,
|
||||
PublicInterface,
|
||||
PrivateInterface,
|
||||
PublicOrPrivateInterface,
|
||||
PrivatePlugin,
|
||||
PublicPlugin,
|
||||
PublicOrPrivatePlugin,
|
||||
BotSummary,
|
||||
} from './types'
|
||||
export * from './types'
|
||||
|
||||
/**
|
||||
* This class is used to wrap the Botpress API and provide a more convenient way to interact with it.
|
||||
*/
|
||||
export class ApiClient {
|
||||
public readonly client: client.Client
|
||||
public readonly url: string
|
||||
public readonly token: string
|
||||
public readonly workspaceId: string
|
||||
public readonly botId?: string
|
||||
public readonly extraHeaders: Record<string, string>
|
||||
|
||||
public static newClient = (props: ApiClientProps, logger: Logger) => new ApiClient(props, logger)
|
||||
|
||||
public constructor(
|
||||
props: ApiClientProps,
|
||||
private _logger: Logger
|
||||
) {
|
||||
const { apiUrl, token, workspaceId, botId, extraHeaders = {} } = props
|
||||
this.client = new client.Client({
|
||||
apiUrl,
|
||||
token,
|
||||
workspaceId,
|
||||
botId,
|
||||
retry: retry.config,
|
||||
headers: { 'x-multiple-integrations': 'true', ...extraHeaders },
|
||||
})
|
||||
this.url = apiUrl
|
||||
this.token = token
|
||||
this.workspaceId = workspaceId
|
||||
this.botId = botId
|
||||
this.extraHeaders = extraHeaders
|
||||
}
|
||||
|
||||
public get isBotpressWorkspace(): boolean {
|
||||
// this environment variable is undocumented and only used internally for dev purposes
|
||||
const isBotpressWorkspace = yn(process.env.BP_IS_BOTPRESS_WORKSPACE)
|
||||
if (isBotpressWorkspace !== undefined) {
|
||||
return isBotpressWorkspace
|
||||
}
|
||||
return [
|
||||
'6a76fa10-e150-4ff6-8f59-a300feec06c1',
|
||||
'95de33eb-1551-4af9-9088-e5dcb02efd09',
|
||||
'11111111-1111-1111-aaaa-111111111111',
|
||||
].includes(this.workspaceId)
|
||||
}
|
||||
|
||||
public async safeListTables(req: client.ClientInputs['listTables']): Promise<
|
||||
| {
|
||||
success: true
|
||||
tables: client.ClientOutputs['listTables']['tables']
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
error: Error
|
||||
}
|
||||
> {
|
||||
try {
|
||||
const result = await this.client.listTables(req)
|
||||
return { success: true, tables: result.tables }
|
||||
} catch (thrown) {
|
||||
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
|
||||
return { success: false, error }
|
||||
}
|
||||
}
|
||||
|
||||
public async getWorkspace(): Promise<client.ClientOutputs['getWorkspace']> {
|
||||
return this.client.getWorkspace({ id: this.workspaceId })
|
||||
}
|
||||
|
||||
public async findWorkspaceByHandle(handle: string): Promise<client.ClientOutputs['getWorkspace'] | undefined> {
|
||||
const { workspaces } = await this.client.listWorkspaces({ handle })
|
||||
return workspaces[0] // There should be only one workspace with a given handle
|
||||
}
|
||||
|
||||
public withExtraHeaders(headers: Record<string, string>): ApiClient {
|
||||
return ApiClient.newClient(
|
||||
{
|
||||
apiUrl: this.url,
|
||||
token: this.token,
|
||||
workspaceId: this.workspaceId,
|
||||
botId: this.botId,
|
||||
extraHeaders: { ...this.extraHeaders, ...headers },
|
||||
},
|
||||
this._logger
|
||||
)
|
||||
}
|
||||
|
||||
public switchWorkspace(workspaceId: string): ApiClient {
|
||||
return ApiClient.newClient(
|
||||
{ apiUrl: this.url, token: this.token, workspaceId, extraHeaders: this.extraHeaders },
|
||||
this._logger
|
||||
)
|
||||
}
|
||||
|
||||
public switchBot(botId: string): ApiClient {
|
||||
return ApiClient.newClient(
|
||||
{ apiUrl: this.url, token: this.token, botId, workspaceId: this.workspaceId, extraHeaders: this.extraHeaders },
|
||||
this._logger
|
||||
)
|
||||
}
|
||||
|
||||
public async updateWorkspace(
|
||||
props: utils.types.SafeOmit<client.ClientInputs['updateWorkspace'], 'id'>
|
||||
): Promise<client.ClientOutputs['updateWorkspace']> {
|
||||
return this.client.updateWorkspace({ id: this.workspaceId, ...props })
|
||||
}
|
||||
|
||||
public async getPublicOrPrivateIntegration(ref: ApiPackageRef): Promise<PublicOrPrivateIntegration> {
|
||||
const integration = await this.findPublicOrPrivateIntegration(ref)
|
||||
if (!integration) {
|
||||
throw new Error(`Integration "${formatPackageRef(ref)}" not found`)
|
||||
}
|
||||
return integration
|
||||
}
|
||||
|
||||
public async findPublicOrPrivateIntegration(ref: ApiPackageRef): Promise<PublicOrPrivateIntegration | undefined> {
|
||||
const formatted = formatPackageRef(ref)
|
||||
|
||||
const privateIntegration = await this.findPrivateIntegration(ref)
|
||||
if (privateIntegration) {
|
||||
this._logger.debug(`Found integration "${formatted}" in workspace`)
|
||||
return privateIntegration
|
||||
}
|
||||
|
||||
const publicIntegration = await this.findPublicIntegration(ref)
|
||||
if (publicIntegration) {
|
||||
this._logger.debug(`Found integration "${formatted}" in hub`)
|
||||
return publicIntegration
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
public async findPrivateIntegration(ref: ApiPackageRef): Promise<PrivateIntegration | undefined> {
|
||||
const { workspaceId } = this
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getIntegration(ref)
|
||||
.then((r) => ({ ...r.integration, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
return this.client
|
||||
.getIntegrationByName(ref)
|
||||
.then((r) => ({ ...r.integration, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async findPublicIntegration(ref: ApiPackageRef): Promise<PublicOrUnlistedIntegration | undefined> {
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getPublicIntegrationById(ref)
|
||||
.then((r) => ({ ...r.integration, visibility: r.integration.visibility as 'public' | 'unlisted' }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
return this.client
|
||||
.getPublicIntegration(ref)
|
||||
.then((r) => ({ ...r.integration, visibility: r.integration.visibility as 'public' | 'unlisted' }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async findPublicOrPrivateInterface(ref: ApiPackageRef): Promise<PublicOrPrivateInterface | undefined> {
|
||||
const formatted = formatPackageRef(ref)
|
||||
|
||||
const privateInterface = await this.findPrivateInterface(ref)
|
||||
if (privateInterface) {
|
||||
this._logger.debug(`Found interface "${formatted}" in workspace`)
|
||||
return privateInterface
|
||||
}
|
||||
|
||||
const publicInterface = await this.findPublicInterface(ref)
|
||||
if (publicInterface) {
|
||||
this._logger.debug(`Found interface "${formatted}" in hub`)
|
||||
return publicInterface
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
public async findPrivateInterface(ref: ApiPackageRef): Promise<PrivateInterface | undefined> {
|
||||
const { workspaceId } = this
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getInterface(ref)
|
||||
.then((r) => ({ ...r.interface, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
return this.client
|
||||
.getInterfaceByName(ref)
|
||||
.then((r) => ({ ...r.interface, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async getPublicInterface(ref: ApiPackageRef): Promise<PublicInterface> {
|
||||
const intrface = await this.findPublicInterface(ref)
|
||||
if (!intrface) {
|
||||
throw new Error(`Interface "${formatPackageRef(ref)}" not found`)
|
||||
}
|
||||
return intrface
|
||||
}
|
||||
|
||||
public async findPublicInterface(ref: ApiPackageRef): Promise<PublicInterface | undefined> {
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getPublicInterfaceById(ref)
|
||||
.then((r) => ({ ...r.interface, public: true }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
return this.client
|
||||
.getPublicInterface(ref)
|
||||
.then((r) => ({ ...r.interface, public: true }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async findPublicPlugin(ref: ApiPackageRef): Promise<PublicPlugin | undefined> {
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getPublicPluginById(ref)
|
||||
.then((r) => ({ ...r.plugin, public: true }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
return this.client
|
||||
.getPublicPlugin(ref)
|
||||
.then((r) => ({ ...r.plugin, public: true }) as const)
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async getPublicOrPrivatePlugin(ref: ApiPackageRef): Promise<PublicOrPrivatePlugin> {
|
||||
const plugin = await this.findPublicOrPrivatePlugin(ref)
|
||||
if (!plugin) {
|
||||
throw new Error(`Plugin "${formatPackageRef(ref)}" not found`)
|
||||
}
|
||||
return plugin
|
||||
}
|
||||
|
||||
public async findPublicOrPrivatePlugin(ref: ApiPackageRef): Promise<PublicOrPrivatePlugin | undefined> {
|
||||
const formatted = formatPackageRef(ref)
|
||||
|
||||
const privatePlugin = await this.findPrivatePlugin(ref)
|
||||
if (privatePlugin) {
|
||||
this._logger.debug(`Found plugin "${formatted}" in workspace`)
|
||||
return privatePlugin
|
||||
}
|
||||
|
||||
const publicPlugin = await this.findPublicPlugin(ref)
|
||||
if (publicPlugin) {
|
||||
this._logger.debug(`Found plugin "${formatted}" in hub`)
|
||||
return publicPlugin
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
public async findPrivatePlugin(ref: ApiPackageRef): Promise<PrivatePlugin | undefined> {
|
||||
const { workspaceId } = this
|
||||
if (ref.type === 'id') {
|
||||
return this.client
|
||||
.getPlugin(ref)
|
||||
.then((r) => ({ ...r.plugin, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
return this.client
|
||||
.getPluginByName(ref)
|
||||
.then((r) => ({ ...r.plugin, workspaceId }))
|
||||
.catch(this._returnUndefinedOnError('ResourceNotFound'))
|
||||
}
|
||||
|
||||
public async testLogin(): Promise<void> {
|
||||
await this.client.listBots({})
|
||||
}
|
||||
|
||||
public listAllPages = paging.listAllPages
|
||||
|
||||
public async findPreviousIntegrationVersion(ref: NamePackageRef): Promise<PublicOrPrivateIntegration | undefined> {
|
||||
const isValidSemverVersion = semver.valid(ref.version)
|
||||
|
||||
// Sanity check (this should never happen):
|
||||
if (!isValidSemverVersion) {
|
||||
throw new errors.BotpressCLIError(`Invalid version "${ref.version}" for integration "${ref.name}"`)
|
||||
}
|
||||
|
||||
return this.findPublicOrPrivateIntegration({ ...ref, version: `<${ref.version}` })
|
||||
}
|
||||
|
||||
public async findBotByName(name: string): Promise<BotSummary | undefined> {
|
||||
// api does not allow filtering bots by name
|
||||
const allBots = await this.listAllPages(this.client.listBots, (r) => r.bots)
|
||||
return allBots.find((b) => b.name === name)
|
||||
}
|
||||
|
||||
private _returnUndefinedOnError =
|
||||
(type: client.ApiError['type']) =>
|
||||
(thrown: any): undefined => {
|
||||
if (client.isApiError(thrown) && thrown.type === type) {
|
||||
return
|
||||
}
|
||||
throw thrown
|
||||
}
|
||||
|
||||
public async getOrGenerateShareableId(
|
||||
botId: string,
|
||||
integrationId: string,
|
||||
integrationAlias: string
|
||||
): Promise<string> {
|
||||
const { shareableId, isExpired } = await this.client
|
||||
.getIntegrationShareableId({
|
||||
botId,
|
||||
integrationId,
|
||||
integrationInstanceAlias: integrationAlias,
|
||||
})
|
||||
.catch(() => ({ shareableId: undefined, isExpired: true }))
|
||||
if (shareableId && !isExpired) {
|
||||
return shareableId
|
||||
}
|
||||
const { shareableId: newShareableId } = await this.client.createIntegrationShareableId({
|
||||
botId,
|
||||
integrationId,
|
||||
integrationInstanceAlias: integrationAlias,
|
||||
})
|
||||
return newShareableId
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './client'
|
||||
export * from './types'
|
||||
export * from './integration-body'
|
||||
export * from './interface-body'
|
||||
export * from './bot-body'
|
||||
export * from './plugin-body'
|
||||
@@ -0,0 +1,243 @@
|
||||
import * as client from '@botpress/client'
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import * as errors from '../errors'
|
||||
import * as utils from '../utils'
|
||||
import * as types from './types'
|
||||
|
||||
export const prepareCreateIntegrationBody = async (
|
||||
integration: sdk.IntegrationDefinition
|
||||
): Promise<types.CreateIntegrationRequestBody> => {
|
||||
const base = `Failed to convert ZUI to JSON schema for integration ${integration.name}`
|
||||
return {
|
||||
name: integration.name,
|
||||
version: integration.version,
|
||||
title: 'title' in integration ? integration.title : undefined,
|
||||
description: 'description' in integration ? integration.description : undefined,
|
||||
user: integration.user,
|
||||
events: integration.events
|
||||
? await utils.records.mapValuesAsync(integration.events, async (event, eventName) => ({
|
||||
...event,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(event, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for event ${eventName}`)
|
||||
}),
|
||||
}))
|
||||
: undefined,
|
||||
actions: integration.actions
|
||||
? await utils.records.mapValuesAsync(integration.actions, async (action, actionName) => ({
|
||||
...action,
|
||||
input: {
|
||||
...action.input,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.input, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} input`)
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
...action.output,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.output, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} output`)
|
||||
}),
|
||||
},
|
||||
}))
|
||||
: undefined,
|
||||
channels: integration.channels
|
||||
? await utils.records.mapValuesAsync(integration.channels, async (channel, channelName) => ({
|
||||
...channel,
|
||||
messages: await utils.records.mapValuesAsync(channel.messages, async (message, messageName) => ({
|
||||
...message,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(message, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`${base} for channel ${channelName} for message ${messageName}`
|
||||
)
|
||||
}),
|
||||
})),
|
||||
}))
|
||||
: undefined,
|
||||
states: integration.states
|
||||
? await utils.records.mapValuesAsync(integration.states, async (state, stateName) => ({
|
||||
...state,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(state, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for state ${stateName}`)
|
||||
}),
|
||||
}))
|
||||
: undefined,
|
||||
entities: integration.entities
|
||||
? await utils.records.mapValuesAsync(integration.entities, async (entity, entityName) => ({
|
||||
...entity,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(entity, {
|
||||
useLegacyZuiTransformer: integration.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: integration.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for entity ${entityName}`)
|
||||
}),
|
||||
}))
|
||||
: undefined,
|
||||
attributes: integration.attributes,
|
||||
extraOperations: '__advanced' in integration ? integration.__advanced?.extraOperations : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
type UpdateIntegrationChannelsBody = NonNullable<types.UpdateIntegrationRequestBody['channels']>
|
||||
type UpdateIntegrationChannelBody = UpdateIntegrationChannelsBody[string]
|
||||
type Channels = client.Integration['channels']
|
||||
type Channel = client.Integration['channels'][string]
|
||||
export const prepareUpdateIntegrationBody = (
|
||||
localIntegration: types.UpdateIntegrationRequestBody,
|
||||
remoteIntegration: client.Integration
|
||||
): types.UpdateIntegrationRequestBody => {
|
||||
const actions = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localIntegration.actions, remoteIntegration.actions),
|
||||
remoteItems: remoteIntegration.actions,
|
||||
})
|
||||
const events = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localIntegration.events, remoteIntegration.events),
|
||||
remoteItems: remoteIntegration.events,
|
||||
})
|
||||
const states = utils.records.setNullOnMissingValues(localIntegration.states, remoteIntegration.states)
|
||||
const entities = utils.records.setNullOnMissingValues(localIntegration.entities, remoteIntegration.entities)
|
||||
const user = {
|
||||
...localIntegration.user,
|
||||
tags: utils.records.setNullOnMissingValues(localIntegration.user?.tags, remoteIntegration.user?.tags),
|
||||
}
|
||||
|
||||
const channels = _prepareUpdateIntegrationChannelsBody(localIntegration.channels ?? {}, remoteIntegration.channels)
|
||||
|
||||
const interfaces = utils.records.setNullOnMissingValues(localIntegration.interfaces, remoteIntegration.interfaces)
|
||||
|
||||
const configurations = utils.records.setNullOnMissingValues(
|
||||
localIntegration.configurations,
|
||||
remoteIntegration.configurations
|
||||
)
|
||||
|
||||
const readme = localIntegration.readme
|
||||
const icon = localIntegration.icon
|
||||
|
||||
const attributes = utils.records.setNullOnMissingValues(localIntegration.attributes, remoteIntegration.attributes)
|
||||
|
||||
const extraOperations = localIntegration.extraOperations
|
||||
return {
|
||||
..._maybeRemoveVrlScripts(localIntegration, remoteIntegration),
|
||||
actions,
|
||||
events,
|
||||
states,
|
||||
entities,
|
||||
user,
|
||||
channels,
|
||||
interfaces,
|
||||
configurations,
|
||||
readme,
|
||||
icon,
|
||||
attributes,
|
||||
extraOperations,
|
||||
}
|
||||
}
|
||||
|
||||
const _maybeRemoveVrlScripts = (
|
||||
localIntegration: types.UpdateIntegrationRequestBody,
|
||||
remoteIntegration: client.Integration
|
||||
): types.UpdateIntegrationRequestBody => {
|
||||
const newIntegration = structuredClone(localIntegration)
|
||||
|
||||
if (
|
||||
remoteIntegration.configuration?.identifier?.linkTemplateScript &&
|
||||
!localIntegration.configuration?.identifier?.linkTemplateScript
|
||||
) {
|
||||
newIntegration.configuration ??= remoteIntegration.configuration
|
||||
newIntegration.configuration.identifier ??= remoteIntegration.configuration.identifier
|
||||
newIntegration.configuration.identifier.linkTemplateScript = null
|
||||
newIntegration.configuration.identifier.required = false
|
||||
}
|
||||
|
||||
if (remoteIntegration.identifier.extractScript && !localIntegration.identifier?.extractScript) {
|
||||
newIntegration.identifier ??= remoteIntegration.identifier
|
||||
newIntegration.identifier.extractScript = null
|
||||
}
|
||||
|
||||
if (remoteIntegration.identifier.fallbackHandlerScript && !localIntegration.identifier?.fallbackHandlerScript) {
|
||||
newIntegration.identifier ??= remoteIntegration.identifier
|
||||
newIntegration.identifier.fallbackHandlerScript = null
|
||||
}
|
||||
|
||||
for (const configName of Object.keys(localIntegration.configurations ?? {})) {
|
||||
if (
|
||||
remoteIntegration.configurations[configName]?.identifier.linkTemplateScript &&
|
||||
!localIntegration.configurations?.[configName]?.identifier?.linkTemplateScript
|
||||
) {
|
||||
newIntegration.configurations ??= remoteIntegration.configurations
|
||||
newIntegration.configurations[configName] ??= remoteIntegration.configurations[configName]
|
||||
newIntegration.configurations[configName].identifier ??= remoteIntegration.configurations[configName].identifier
|
||||
newIntegration.configurations[configName].identifier.linkTemplateScript = null
|
||||
newIntegration.configurations[configName].identifier.required = false
|
||||
}
|
||||
}
|
||||
|
||||
return newIntegration
|
||||
}
|
||||
|
||||
const _prepareUpdateIntegrationChannelsBody = (
|
||||
localChannels: UpdateIntegrationChannelsBody,
|
||||
remoteChannels: Channels
|
||||
): UpdateIntegrationChannelsBody => {
|
||||
const channelBody: UpdateIntegrationChannelsBody = {}
|
||||
|
||||
const zipped = utils.records.zipObjects(localChannels, remoteChannels)
|
||||
for (const [channelName, [localChannel, remoteChannel]] of Object.entries(zipped)) {
|
||||
if (localChannel && remoteChannel) {
|
||||
// channel has to be updated
|
||||
channelBody[channelName] = _prepareUpdateIntegrationChannelBody(localChannel, remoteChannel)
|
||||
} else if (localChannel) {
|
||||
// channel has to be created
|
||||
channelBody[channelName] = localChannel
|
||||
continue
|
||||
} else if (remoteChannel) {
|
||||
// channel has to be deleted
|
||||
channelBody[channelName] = null
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return channelBody
|
||||
}
|
||||
|
||||
const _prepareUpdateIntegrationChannelBody = (
|
||||
localChannel: UpdateIntegrationChannelBody,
|
||||
remoteChannel: Channel
|
||||
): UpdateIntegrationChannelBody => ({
|
||||
...localChannel,
|
||||
messages: utils.records.setNullOnMissingValues(localChannel?.messages, remoteChannel.messages),
|
||||
message: {
|
||||
...localChannel?.message,
|
||||
tags: utils.records.setNullOnMissingValues(localChannel?.message?.tags, remoteChannel.message.tags),
|
||||
},
|
||||
conversation: {
|
||||
...localChannel?.conversation,
|
||||
tags: utils.records.setNullOnMissingValues(localChannel?.conversation?.tags, remoteChannel.conversation.tags),
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,128 @@
|
||||
import * as client from '@botpress/client'
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import * as errors from '../errors'
|
||||
import * as utils from '../utils'
|
||||
import * as types from './types'
|
||||
|
||||
export const prepareCreateInterfaceBody = async (
|
||||
intrface: sdk.InterfaceDefinition
|
||||
): Promise<types.CreateInterfaceRequestBody> => {
|
||||
const base = `Failed to convert ZUI to JSON schema for interface ${intrface.name}`
|
||||
return {
|
||||
name: intrface.name,
|
||||
version: intrface.version,
|
||||
title: 'title' in intrface ? intrface.title : undefined,
|
||||
description: 'description' in intrface ? intrface.description : undefined,
|
||||
entities: intrface.entities
|
||||
? await utils.records.mapValuesAsync(intrface.entities, async (entity, entityName) => ({
|
||||
...entity,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(entity, {
|
||||
useLegacyZuiTransformer: intrface.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: intrface.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for entity ${entityName}`)
|
||||
}),
|
||||
}))
|
||||
: {},
|
||||
events: intrface.events
|
||||
? await utils.records.mapValuesAsync(intrface.events, async (event, eventName) => ({
|
||||
...event,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(event, {
|
||||
useLegacyZuiTransformer: intrface.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: intrface.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for event ${eventName}`)
|
||||
}),
|
||||
}))
|
||||
: {},
|
||||
actions: intrface.actions
|
||||
? await utils.records.mapValuesAsync(intrface.actions, async (action, actionName) => ({
|
||||
...action,
|
||||
input: {
|
||||
...action.input,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.input, {
|
||||
useLegacyZuiTransformer: intrface.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: intrface.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} input`)
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
...action.output,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.output, {
|
||||
useLegacyZuiTransformer: intrface.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: intrface.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} output`)
|
||||
}),
|
||||
},
|
||||
}))
|
||||
: {},
|
||||
channels: intrface.channels
|
||||
? await utils.records.mapValuesAsync(intrface.channels, async (channel, channelName) => ({
|
||||
...channel,
|
||||
messages: await utils.records.mapValuesAsync(channel.messages, async (message, messageName) => ({
|
||||
...message,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(message, {
|
||||
useLegacyZuiTransformer: intrface.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: intrface.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(
|
||||
thrown,
|
||||
`${base} for channel ${channelName} for message ${messageName}`
|
||||
)
|
||||
}),
|
||||
})),
|
||||
}))
|
||||
: {},
|
||||
attributes: intrface.attributes,
|
||||
}
|
||||
}
|
||||
|
||||
export const prepareUpdateInterfaceBody = (
|
||||
localInterface: types.CreateInterfaceRequestBody & { id: string },
|
||||
remoteInterface: client.Interface
|
||||
): types.UpdateInterfaceRequestBody => {
|
||||
const actions = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localInterface.actions, remoteInterface.actions),
|
||||
remoteItems: remoteInterface.actions,
|
||||
})
|
||||
const events = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localInterface.events, remoteInterface.events),
|
||||
remoteItems: remoteInterface.events,
|
||||
})
|
||||
const entities = utils.records.setNullOnMissingValues(localInterface.entities, remoteInterface.entities)
|
||||
|
||||
const currentChannels: types.UpdateInterfaceRequestBody['channels'] = localInterface.channels
|
||||
? utils.records.mapValues(localInterface.channels, (channel, channelName) => ({
|
||||
...channel,
|
||||
messages: utils.records.setNullOnMissingValues(
|
||||
channel?.messages,
|
||||
remoteInterface.channels[channelName]?.messages
|
||||
),
|
||||
}))
|
||||
: undefined
|
||||
|
||||
const channels = utils.records.setNullOnMissingValues(currentChannels, remoteInterface.channels)
|
||||
|
||||
const attributes = utils.records.setNullOnMissingValues(localInterface.attributes, remoteInterface.attributes)
|
||||
|
||||
return {
|
||||
...localInterface,
|
||||
entities,
|
||||
actions,
|
||||
events,
|
||||
channels,
|
||||
attributes,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export type PageLister<R extends object> = (t: { nextToken?: string }) => Promise<R & { meta: { nextToken?: string } }>
|
||||
|
||||
export async function listAllPages<R extends object>(lister: PageLister<R>): Promise<R[]>
|
||||
export async function listAllPages<R extends object, M>(lister: PageLister<R>, mapper?: (r: R) => M[]): Promise<M[]>
|
||||
export async function listAllPages<R extends object, M>(lister: PageLister<R>, mapper?: (r: R) => M[]) {
|
||||
let nextToken: string | undefined
|
||||
const all: R[] = []
|
||||
|
||||
do {
|
||||
const { meta, ...r } = await lister({ nextToken })
|
||||
all.push(r as R)
|
||||
nextToken = meta.nextToken
|
||||
} while (nextToken)
|
||||
|
||||
if (!mapper) {
|
||||
return all
|
||||
}
|
||||
|
||||
const mapped: M[] = all.flatMap((r) => mapper(r))
|
||||
return mapped
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import * as client from '@botpress/client'
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import * as errors from '../errors'
|
||||
import * as utils from '../utils'
|
||||
import * as types from './types'
|
||||
|
||||
export const prepareCreatePluginBody = async (plugin: sdk.PluginDefinition): Promise<types.CreatePluginRequestBody> => {
|
||||
const base = `Failed to convert ZUI to JSON schema for plugin ${plugin.name}`
|
||||
return {
|
||||
name: plugin.name,
|
||||
version: plugin.version,
|
||||
title: 'title' in plugin ? plugin.title : undefined,
|
||||
description: 'description' in plugin ? plugin.description : undefined,
|
||||
user: {
|
||||
tags: plugin.user?.tags ?? {},
|
||||
},
|
||||
conversation: {
|
||||
tags: plugin.conversation?.tags ?? {},
|
||||
},
|
||||
message: {
|
||||
tags: plugin.message?.tags ?? {},
|
||||
},
|
||||
configuration: plugin.configuration
|
||||
? {
|
||||
...plugin.configuration,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(plugin.configuration, {
|
||||
useLegacyZuiTransformer: plugin.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: plugin.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for configuration`)
|
||||
}),
|
||||
}
|
||||
: undefined,
|
||||
events: plugin.events
|
||||
? await utils.records.mapValuesAsync(plugin.events, async (event, eventName) => ({
|
||||
...event,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(event, {
|
||||
useLegacyZuiTransformer: plugin.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: plugin.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for event ${eventName}`)
|
||||
}),
|
||||
}))
|
||||
: undefined,
|
||||
actions: plugin.actions
|
||||
? await utils.records.mapValuesAsync(plugin.actions, async (action, actionName) => ({
|
||||
...action,
|
||||
input: {
|
||||
...action.input,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.input, {
|
||||
useLegacyZuiTransformer: plugin.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: plugin.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} input`)
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
...action.output,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(action.output, {
|
||||
useLegacyZuiTransformer: plugin.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: plugin.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for action ${actionName} output`)
|
||||
}),
|
||||
},
|
||||
}))
|
||||
: undefined,
|
||||
states: plugin.states
|
||||
? (utils.records.filterValues(
|
||||
await utils.records.mapValuesAsync(plugin.states, async (state, stateName) => ({
|
||||
...state,
|
||||
schema: await utils.schema
|
||||
.mapZodToJsonSchema(state, {
|
||||
useLegacyZuiTransformer: plugin.__advanced?.useLegacyZuiTransformer,
|
||||
toJSONSchemaOptions: plugin.__advanced?.toJSONSchemaOptions,
|
||||
})
|
||||
.catch((thrown) => {
|
||||
throw errors.BotpressCLIError.wrap(thrown, `${base} for state ${stateName}`)
|
||||
}),
|
||||
})),
|
||||
({ type }) => type !== 'workflow'
|
||||
) as types.CreatePluginRequestBody['states'])
|
||||
: undefined,
|
||||
attributes: plugin.attributes,
|
||||
}
|
||||
}
|
||||
|
||||
export const prepareUpdatePluginBody = (
|
||||
localPlugin: types.UpdatePluginRequestBody,
|
||||
remotePlugin: client.Plugin
|
||||
): types.UpdatePluginRequestBody => {
|
||||
const actions = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localPlugin.actions, remotePlugin.actions),
|
||||
remoteItems: remotePlugin.actions,
|
||||
})
|
||||
const events = utils.attributes.prepareAttributeUpdateBody({
|
||||
localItems: utils.records.setNullOnMissingValues(localPlugin.events, remotePlugin.events),
|
||||
remoteItems: remotePlugin.events,
|
||||
})
|
||||
const states = utils.records.setNullOnMissingValues(localPlugin.states, remotePlugin.states)
|
||||
|
||||
const attributes = utils.records.setNullOnMissingValues(localPlugin.attributes, remotePlugin.attributes)
|
||||
|
||||
const dependencies: types.UpdatePluginRequestBody['dependencies'] = {
|
||||
integrations: utils.records.setNullOnMissingValues(
|
||||
localPlugin.dependencies?.integrations,
|
||||
remotePlugin.dependencies?.integrations
|
||||
),
|
||||
interfaces: utils.records.setNullOnMissingValues(
|
||||
localPlugin.dependencies?.interfaces,
|
||||
remotePlugin.dependencies?.interfaces
|
||||
),
|
||||
}
|
||||
|
||||
// TODO: set null to conversation, user and message tags that are removed
|
||||
|
||||
return {
|
||||
...localPlugin,
|
||||
actions,
|
||||
events,
|
||||
states,
|
||||
attributes,
|
||||
dependencies,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import * as client from '@botpress/client'
|
||||
|
||||
// TODO: we probably shouldnt retry on 500 errors, but this is a temporary fix for the botpress repo CI
|
||||
const HTTP_STATUS_TO_RETRY_ON = [429, 500, 502, 503, 504]
|
||||
|
||||
function getRetryAfterMs(error: Parameters<NonNullable<client.RetryConfig['retryDelay']>>[1]): number | undefined {
|
||||
const headers = error?.response?.headers
|
||||
if (!headers) return undefined
|
||||
|
||||
const headerNames = ['retry-after', 'ratelimit-reset', 'x-ratelimit-reset']
|
||||
for (const name of headerNames) {
|
||||
const raw = headers[name]
|
||||
if (!raw) continue
|
||||
const value = String(raw)
|
||||
|
||||
// HTTP-date format (e.g. "Mon, 28 Apr 2026 12:00:00 GMT")
|
||||
if (value.includes(' ')) {
|
||||
const futureDate = new Date(value)
|
||||
if (!isNaN(futureDate.getTime())) {
|
||||
return Math.max(0, futureDate.getTime() - Date.now())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Seconds format (e.g. "120")
|
||||
const seconds = parseInt(value, 10)
|
||||
if (!isNaN(seconds) && seconds >= 0) {
|
||||
return seconds * 1000
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const config: client.RetryConfig = {
|
||||
retries: 3,
|
||||
retryCondition: (err) =>
|
||||
client.axiosRetry.isNetworkOrIdempotentRequestError(err) ||
|
||||
HTTP_STATUS_TO_RETRY_ON.includes(err.response?.status ?? 0),
|
||||
retryDelay: (retryCount, error) => {
|
||||
if (error?.response?.status === 429) {
|
||||
const retryAfterMs = getRetryAfterMs(error)
|
||||
if (retryAfterMs !== undefined) {
|
||||
return retryAfterMs
|
||||
}
|
||||
}
|
||||
return Math.max(retryCount, 1) * 1000
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import * as client from '@botpress/client'
|
||||
import { Logger } from '../logger'
|
||||
import { SafeOmit, Merge } from '../utils/type-utils'
|
||||
import { ApiClient } from './client'
|
||||
|
||||
export type ApiClientProps = {
|
||||
apiUrl: string
|
||||
token: string
|
||||
workspaceId: string
|
||||
botId?: string
|
||||
extraHeaders?: Record<string, string>
|
||||
}
|
||||
|
||||
export type ApiClientFactory = {
|
||||
newClient: (props: ApiClientProps, logger: Logger) => ApiClient
|
||||
}
|
||||
|
||||
export type PublicOrUnlistedIntegration = client.Integration & { visibility: 'public' | 'unlisted' }
|
||||
export type PrivateIntegration = client.Integration & { workspaceId: string }
|
||||
export type PublicOrPrivateIntegration = client.Integration & { workspaceId?: string }
|
||||
export type IntegrationSummary = (
|
||||
| client.ClientOutputs['listIntegrations']
|
||||
| client.ClientOutputs['listPublicIntegrations']
|
||||
)['integrations'][number]
|
||||
export type BotSummary = client.ClientOutputs['listBots']['bots'][number]
|
||||
export type PublicInterface = client.Interface & { public: true }
|
||||
export type PrivateInterface = client.Interface & { workspaceId: string }
|
||||
export type PublicOrPrivateInterface = client.Interface & { workspaceId?: string }
|
||||
export type PublicPlugin = client.Plugin & { public: true }
|
||||
export type PrivatePlugin = client.Plugin & { workspaceId: string }
|
||||
export type PublicOrPrivatePlugin = client.Plugin & { workspaceId?: string }
|
||||
|
||||
export type CreateBotRequestBody = client.ClientInputs['createBot']
|
||||
export type UpdateBotRequestBody = client.ClientInputs['updateBot']
|
||||
|
||||
export type CreateIntegrationRequestBody = client.ClientInputs['createIntegration']
|
||||
export type UpdateIntegrationRequestBody = client.ClientInputs['updateIntegration']
|
||||
|
||||
export type CreateInterfaceRequestBody = client.ClientInputs['createInterface']
|
||||
export type UpdateInterfaceRequestBody = client.ClientInputs['updateInterface']
|
||||
|
||||
type PluginDependency = client.Plugin['dependencies']['integrations'][string]
|
||||
|
||||
export type CreatePluginRequestBody = Merge<
|
||||
SafeOmit<client.ClientInputs['createPlugin'], 'code'>,
|
||||
{
|
||||
dependencies?: {
|
||||
integrations?: Record<string, PluginDependency>
|
||||
interfaces?: Record<string, PluginDependency>
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
export type UpdatePluginRequestBody = Merge<
|
||||
client.ClientInputs['updatePlugin'],
|
||||
{
|
||||
dependencies?: {
|
||||
integrations?: Record<string, PluginDependency | null>
|
||||
interfaces?: Record<string, PluginDependency | null>
|
||||
}
|
||||
}
|
||||
>
|
||||
Reference in New Issue
Block a user