import * as sdk from '@botpress/sdk' const { z } = sdk type ActionDefinitions = NonNullable type ActionDef = ActionDefinitions[string] const MAJOR_DIMENSION = ['ROWS', 'COLUMNS'] as const export type MajorDimension = (typeof MAJOR_DIMENSION)[number] const _sheetsValue = z .string() .title('Stringified value') .describe('Represents the value of a single cell. This is a stringified number, string or boolean value') const _commonFields = { range: z.string().title('Range').placeholder("'Sheet name'!A1:F8"), majorDimension: z.enum(MAJOR_DIMENSION).title('Major Dimension').default('ROWS'), values: z .array( z .array(_sheetsValue) .title('Row or column') .describe('Represents a major dimension (a row or column) of a values range') ) .title('Values'), } as const const getValues = { title: 'Get Values', description: 'Returns the values of a range in the spreadsheet.', input: { schema: z.object({ range: _commonFields.range.describe('The A1 notation of the range to retrieve. (e.g. "Sheet1!A1:B2")'), majorDimension: _commonFields.majorDimension .optional() .describe( 'If it equals "ROWS", then the values are returned as rows. If it equals "COLUMNS", then the values are returned as columns.' ), }), }, output: { schema: z.object({ range: _commonFields.range.describe( 'The range the values cover, in A1 notation. This range indicates the entire requested range, even though the values will exclude trailing rows and columns. (e.g. "Sheet1!A1:B2")' ), majorDimension: _commonFields.majorDimension.describe( 'If it equals "ROWS", then the values are returned in rows. If it equals "COLUMNS", then the values are returned in columns.' ), values: _commonFields.values.describe( 'The data that was read. This is an array of arrays, the outer array representing all the data and each inner array representing a major dimension (a row or column). Each item in the inner array corresponds with one cell. (e.g. [["a", "b"], ["c", "d"]])' ), }), }, } as const satisfies ActionDef const setValues = { title: 'Set Values', description: 'Sets values in a range in the spreadsheet.', input: { schema: z.object({ range: _commonFields.range.describe('The A1 notation of the range to update. (e.g. "Sheet1!A1:B2")'), majorDimension: _commonFields.majorDimension .optional() .describe( 'If it equals "ROWS", then the values are inserted as rows. If it equals "COLUMNS", then the values are inserted as columns.' ), values: _commonFields.values.describe( 'The values to write to the range. This is an array of arrays, where each inner array represents a major dimension (a row or column) of data. (e.g. [["a", "b"], ["c", "d"]])' ), }), }, output: { schema: z.object({ spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'), updatedRange: z .string() .title('Updated Range') .describe('The range (in A1 notation) that updates were applied to.'), updatedRows: z .number() .title('Updated Rows') .describe('The number of rows where at least one cell in the row was updated.'), updatedColumns: z .number() .title('Updated Columns') .describe('The number of columns where at least one cell in the column was updated.'), updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'), }), }, } as const satisfies ActionDef const appendValues = { title: 'Append Values', description: 'Appends values to the spreadsheet. The input startColumn is used to search for existing data and find a "table" within that range. Values will be appended to the next row of the table, starting with the first column of the table.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), startColumn: z .string() .title('Start Column') .describe( 'The start column letter(s) (e.g. "A", "B", "AA"). Used to identify the table in the sheet — data will be appended after the last row of the detected table.' ), majorDimension: _commonFields.majorDimension .optional() .describe( 'If it equals "ROWS", then the values are inserted as rows. If it equals "COLUMNS", then the values are inserted as columns.' ), values: _commonFields.values.describe( 'The values to write to the range. This is an array of arrays, where each inner array represents a major dimension (a row or column) of data. (e.g. [["a", "b"], ["c", "d"]])' ), }), }, output: { schema: z.object({ spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'), tableRange: z .string() .title('Table Range') .describe( 'The range (in A1 notation) of the table that values are being appended to (before the values were appended). Empty if no table was found.' ), updates: z .object({ spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'), updatedRange: z .string() .title('Updated Range') .describe('The range (in A1 notation) that updates were applied to.'), updatedRows: z .number() .title('Updated Rows') .describe('The number of rows where at least one cell in the row was updated.'), updatedColumns: z .number() .title('Updated Columns') .describe('The number of columns where at least one cell in the column was updated.'), updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'), }) .title('Updates') .describe('The updates that were applied to the spreadsheet.'), }), }, } as const satisfies ActionDef const clearValues = { title: 'Clear Values', description: 'Clears values from a spreadsheet. Only values are cleared; all other properties of the cell (such as formatting, data validation, etc..) are kept.', input: { schema: z.object({ range: _commonFields.range.describe('The A1 notation of the range to clear. (e.g. "Sheet1!A1:B2")'), }), }, output: { schema: z.object({ spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'), clearedRange: z.string().title('Cleared Range').describe('The range (in A1 notation) that was cleared.'), }), }, } as const satisfies ActionDef const getInfoSpreadsheet = { title: 'Get Info of a SpreadSheet', description: 'Returns the properties and metadata of the specified spreadsheet.', input: { schema: z.object({ fields: z .array(z.string().title('Field name').describe('The field to include in the response')) .title('Fields') .describe( 'The fields to include in the response when retrieving spreadsheet properties and metadata. This is a list of field names. (eg. spreadsheetId, properties.title, sheets.properties.sheetId, sheets.properties.title)' ), }), }, output: { schema: z.object({ spreadsheetId: z .union([z.string(), z.null()]) .title('Spreadsheet ID') .describe('The unique identifier of the spreadsheet.') .optional(), spreadsheetUrl: z .union([z.string(), z.null()]) .title('Spreadsheet URL') .describe('The URL of the spreadsheet.') .optional(), dataSources: z .array(z.any()) .describe('The data sources connected to the spreadsheet.') .title('Data Sources') .optional(), dataSourceSchedules: z .array(z.any()) .describe('The schedules of the data sources.') .title('Data Source Schedules') .optional(), developerMetadata: z .array(z.any()) .describe('The developer metadata associated with the spreadsheet.') .title('Developer Metadata') .optional(), namedRanges: z .array(z.any()) .describe('The named ranges defined in the spreadsheet.') .title('Named Ranges') .optional(), properties: z.any().describe('The properties of the spreadsheet.').title('Properties').optional(), sheets: z.array(z.any()).describe('The sheets present in the spreadsheet.').title('Sheets').optional(), }), }, } as const satisfies ActionDef const addSheet = { title: 'Add Sheet', description: 'Adds a new sheet to the spreadsheet.', input: { schema: z.object({ title: z.string().title('Title').describe('The title of the new sheet to add to the spreadsheet.'), }), }, output: { schema: z.object({ spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet ID of the new sheet.'), newSheet: z .object({ sheetId: z.number().title('Sheet ID').describe('The ID of the new sheet.'), title: z.string().title('Title').describe('The title of the new sheet.'), index: z.number().title('Index').describe('The index of the new sheet within the spreadsheet.'), isHidden: z.boolean().title('Is Hidden').describe('Whether the new sheet is hidden.'), }) .title('New Sheet') .describe('The new sheet that was added to the spreadsheet.'), }), }, } as const satisfies ActionDef const deleteSheet = { title: 'Delete Sheet', description: 'Deletes a sheet from the spreadsheet.', input: { schema: z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to delete.'), }), }, output: { schema: z.object({}), }, } as const satisfies ActionDef const renameSheet = { title: 'Rename Sheet', description: 'Renames a sheet in the spreadsheet.', input: { schema: z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to rename.'), newTitle: z.string().title('New Title').describe('The new title of the sheet.'), }), }, output: { schema: z.object({}), }, } as const satisfies ActionDef const getAllSheetsInSpreadsheet = { title: 'Get All Sheets in Spreadsheet', description: 'Returns all sheets in the spreadsheet.', input: { schema: z.object({}), }, output: { schema: z.object({ sheets: z .array( z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet.'), title: z.string().title('Title').describe('The name of the sheet.'), index: z.number().title('Index').describe('The index of the sheet within the spreadsheet.'), isHidden: z.boolean().title('Is Hidden').describe('Whether the sheet is hidden.'), hasProtectedRanges: z .boolean() .title('Has Protected Ranges') .describe('Whether the sheet has protected ranges.'), isFullyProtected: z.boolean().title('Is Protected').describe('Whether the entire sheet is protected.'), }) ) .title('Sheets') .describe('The sheets present in the spreadsheet.'), }), }, } as const satisfies ActionDef const setSheetVisibility = { title: 'Set Sheet Visibility', description: 'Sets the visibility of a sheet in the spreadsheet.', input: { schema: z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to set visibility.'), isHidden: z.boolean().title('Is Hidden').describe('Whether the sheet is hidden.'), }), }, output: { schema: z.object({}), }, } as const satisfies ActionDef const moveSheetHorizontally = { title: 'Move Sheet Horizontally', description: 'Moves a sheet to a new index in the spreadsheet.', input: { schema: z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to move.'), newIndex: z.number().title('New Index').describe('The new index of the sheet within the spreadsheet.'), }), }, output: { schema: z.object({}), }, } as const satisfies ActionDef const protectNamedRange = { title: 'Protect Named Range', description: 'Creates a protected range from a named range, preventing modification.', input: { schema: z.object({ namedRangeId: z.string().title('Named Range ID').describe('The ID of the named range to protect.'), warningOnly: z .boolean() .title('Warning Only') .optional() .describe('Whether the protection displays a warning but still allows editing.'), requestingUserCanEdit: z .boolean() .title('Requesting User Can Edit') .optional() .describe('Whether the user adding the protection can edit the protected range.'), }), }, output: { schema: z.object({ protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the new protected range.'), }), }, } as const satisfies ActionDef const getNamedRanges = { title: 'Get Named Ranges', description: 'Returns all named ranges in the spreadsheet.', input: { schema: z.object({}), }, output: { schema: z.object({ namedRanges: z .array( z.object({ namedRangeId: z.string().title('Named Range ID').describe('The ID of the named range.'), name: z.string().title('Name').describe('The name of the named range.'), range: _commonFields.range.describe('The range of the named range in A1 notation. (e.g. "A1:B2")'), sheetId: z.number().title('Sheet ID').describe('The ID of the sheet the named range applies to.'), }) ) .title('Named Ranges') .describe('The named ranges defined in the spreadsheet.'), }), }, } as const satisfies ActionDef const getProtectedRanges = { title: 'Get Protected Ranges', description: 'Returns all protected ranges in the spreadsheet.', input: { schema: z.object({}), }, output: { schema: z.object({ protectedRanges: z .array( z.object({ protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the protected range.'), namedRangeId: z .string() .title('Named Range ID') .describe('The ID of the named range, if the protected range is backed by a named range.'), range: _commonFields.range.describe('The range of the protected range in A1 notation. (e.g. "A1:B2")'), sheetId: z.number().title('Sheet ID').describe('The ID of the sheet the protected range applies to.'), description: z.string().title('Description').describe('The description of the protected range.'), warningOnly: z .boolean() .title('Warning Only') .describe('Whether the protection displays a warning but still allows editing.'), requestingUserCanEdit: z .boolean() .title('Requesting User Can Edit') .describe('Whether the user adding the protection can edit the protected range.'), }) ) .title('Protected Ranges') .describe('The protected ranges defined in the spreadsheet.'), }), }, } as const satisfies ActionDef const unprotectRange = { title: 'Unprotect Range', description: 'Removes protection from a protected range in the spreadsheet.', input: { schema: z.object({ protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the protected range to unprotect.'), }), }, output: { schema: z.object({}), }, } as const satisfies ActionDef const createNamedRangeInSheet = { title: 'Create Named Range in Sheet', description: 'Creates a named range in a sheet.', input: { schema: z.object({ sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to create the named range in.'), rangeName: z .string() .title('Name') .describe( 'The name of the named range. Must follow naming rules: cannot contain spaces, cannot start with a number, and cannot conflict with standard cell references (e.g., "A1", "R1C1"). Names violating these rules will be considered invalid.' ), rangeA1: _commonFields.range.describe( 'The A1 notation of the range to associate with the named range. (e.g. "Sheet1!A1:B2")' ), }), }, output: { schema: z.object({ namedRangeId: z.string().title('Named Range ID').describe('The ID of the new named range.'), }), }, } as const satisfies ActionDef const _rowSchema = z .object({ rowIndex: z.number().title('Row Index').describe('The 1-based index of the row in the sheet.'), values: z.array(z.string().title('Cell Value')).title('Values').describe('The cell values in the row.'), }) .title('Row') .describe('A row with its index and values.') const findRows = { title: 'Find Rows', description: 'Search for rows where a specific column matches a value. Returns all matching rows with their indexes. Handles empty sheets and no matches gracefully by returning an empty array.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), searchColumn: z.string().title('Search Column').describe('The column letter to search in (e.g. "A", "B", "AA").'), searchValue: z.string().title('Search Value').describe('The value to search for in the specified column.'), dataRange: z .string() .title('Data Range') .optional() .describe( 'Optional A1 notation range to limit the search (e.g. "A1:F100"). If not provided, searches the entire sheet.' ), }), }, output: { schema: z.object({ rows: z.array(_rowSchema).title('Matching Rows').describe('The rows that match the search criteria.'), totalMatches: z.number().title('Total Matches').describe('The total number of matching rows found.'), }), }, } as const satisfies ActionDef const findRow = { title: 'Find Row (First Match)', description: 'Search for the first row where a specific column matches a value. Returns the row data and its index, or null if no match is found.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), searchColumn: z.string().title('Search Column').describe('The column letter to search in (e.g. "A", "B", "AA").'), searchValue: z.string().title('Search Value').describe('The value to search for in the specified column.'), dataRange: z .string() .title('Data Range') .optional() .describe( 'Optional A1 notation range to limit the search (e.g. "A1:F100"). If not provided, searches the entire sheet.' ), }), }, output: { schema: z.object({ found: z.boolean().title('Found').describe('Whether a matching row was found.'), row: _rowSchema.nullable().describe('The first matching row, or null if not found.'), }), }, } as const satisfies ActionDef const getRow = { title: 'Get Row', description: 'Fetch a specific row by its 1-based index. Provides direct row access without A1 notation math.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), rowIndex: z .number() .title('Row Index') .describe('The 1-based row index to retrieve (e.g. 1 for the first row, 2 for the second row).'), startColumn: z .string() .title('Start Column') .optional() .default('A') .describe('The starting column letter (e.g. "A"). Defaults to "A".'), endColumn: z .string() .title('End Column') .optional() .describe('The ending column letter (e.g. "Z"). If not provided, returns all columns with data.'), }), }, output: { schema: z.object({ found: z.boolean().title('Found').describe('Whether the row exists and has data.'), row: _rowSchema.nullable().describe('The row data, or null if the row is empty or does not exist.'), }), }, } as const satisfies ActionDef const updateRow = { title: 'Update Row', description: 'Update a specific row by its 1-based index with a partial or complete set of values. Only the provided values are updated.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), rowIndex: z.number().title('Row Index').describe('The 1-based row index to update.'), values: z .array(z.string().title('Cell Value')) .title('Values') .describe('The values to write to the row, starting from the start column.'), startColumn: z .string() .title('Start Column') .optional() .default('A') .describe('The starting column letter for the update (e.g. "A"). Defaults to "A".'), }), }, output: { schema: z.object({ updatedRange: z.string().title('Updated Range').describe('The range (in A1 notation) that was updated.'), updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'), }), }, } as const satisfies ActionDef const insertRowAtIndex = { title: 'Insert Row at Index', description: 'Insert a new row at a specific 1-based index. Existing rows at and below the index are shifted down.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), rowIndex: z.number().title('Row Index').describe('The 1-based index where the new row should be inserted.'), values: z .array(z.string().title('Cell Value')) .title('Values') .optional() .describe('Optional values to populate the new row.'), startColumn: z .string() .title('Start Column') .optional() .default('A') .describe('The starting column letter for the values (e.g. "A"). Defaults to "A".'), }), }, output: { schema: z.object({ insertedRowIndex: z.number().title('Inserted Row Index').describe('The 1-based index of the newly inserted row.'), }), }, } as const satisfies ActionDef const deleteRows = { title: 'Delete Rows', description: 'Delete one or more rows by their 1-based indexes. Rows are deleted in reverse order to preserve indexes during deletion.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), rowIndexes: z .array(z.number().title('Row Index')) .title('Row Indexes') .describe('The 1-based row indexes to delete.'), }), }, output: { schema: z.object({ deletedCount: z.number().title('Deleted Count').describe('The number of rows deleted.'), }), }, } as const satisfies ActionDef const upsertRow = { title: 'Upsert Row', description: 'Update a row if it exists (based on a key column match), or append a new row if no match is found. Useful for maintaining unique records.', input: { schema: z.object({ sheetName: z .string() .title('Sheet Name') .optional() .describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'), keyColumn: z .string() .title('Key Column') .describe('The column letter to use for matching (e.g. "A" for ID column).'), keyValue: z.string().title('Key Value').describe('The value to match in the key column.'), values: z.array(z.string().title('Cell Value')).title('Values').describe('The values to write to the row.'), startColumn: z .string() .title('Start Column') .optional() .default('A') .describe('The starting column letter for the values (e.g. "A"). Defaults to "A".'), }), }, output: { schema: z.object({ action: z.enum(['updated', 'inserted']).title('Action').describe('Whether the row was updated or inserted.'), rowIndex: z.number().title('Row Index').describe('The 1-based index of the affected row.'), }), }, } as const satisfies ActionDef export const actions = { addSheet, appendValues, clearValues, createNamedRangeInSheet, deleteRows, deleteSheet, findRow, findRows, getAllSheetsInSpreadsheet, getInfoSpreadsheet, getNamedRanges, getProtectedRanges, getRow, getValues, insertRowAtIndex, moveSheetHorizontally, protectNamedRange, renameSheet, setSheetVisibility, unprotectRange, updateRow, upsertRow, setValues, } as const satisfies ActionDefinitions