slates 0.0.2 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/example/google-sheets/auth.ts +317 -0
- package/example/google-sheets/config.ts +9 -0
- package/example/google-sheets/docs/abc.md +1 -0
- package/example/google-sheets/docs/readme.md +1 -0
- package/example/google-sheets/index.ts +42 -0
- package/example/google-sheets/lib/client.ts +717 -0
- package/example/google-sheets/lib/types.ts +1674 -0
- package/example/google-sheets/readme.md +1 -0
- package/example/google-sheets/slate.json +4 -0
- package/example/google-sheets/spec.ts +13 -0
- package/example/google-sheets/tools/append-values.ts +74 -0
- package/example/google-sheets/tools/clear-values.ts +45 -0
- package/example/google-sheets/tools/copy-paste.ts +99 -0
- package/example/google-sheets/tools/create-spreadsheet.ts +72 -0
- package/example/google-sheets/tools/find-replace.ts +68 -0
- package/example/google-sheets/tools/format-cells.ts +137 -0
- package/example/google-sheets/tools/get-spreadsheet.ts +108 -0
- package/example/google-sheets/tools/index.ts +15 -0
- package/example/google-sheets/tools/manage-rows-columns.ts +160 -0
- package/example/google-sheets/tools/manage-sheets.ts +183 -0
- package/example/google-sheets/tools/merge-cells.ts +76 -0
- package/example/google-sheets/tools/named-ranges.ts +82 -0
- package/example/google-sheets/tools/protect-range.ts +100 -0
- package/example/google-sheets/tools/read-values.ts +94 -0
- package/example/google-sheets/tools/sort-range.ts +78 -0
- package/example/google-sheets/tools/write-values.ts +91 -0
- package/example/google-sheets/triggers/index.ts +1 -0
- package/example/google-sheets/triggers/spreadsheet-changed.ts +108 -0
- package/example/out.zip.b64 +1 -0
- package/package.json +30 -20
- package/src/index.ts +1 -0
- package/tsconfig.json +8 -0
- package/bin/slates.js +0 -8
- package/readme.md +0 -9
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let manageRowsColumnsTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Manage Rows and Columns',
|
|
8
|
+
key: 'manage_rows_columns',
|
|
9
|
+
description: `Insert, delete, or resize rows and columns in a Google Sheets spreadsheet.
|
|
10
|
+
Supports bulk operations and auto-resizing to fit content.`,
|
|
11
|
+
tags: {
|
|
12
|
+
destructive: false,
|
|
13
|
+
readOnly: false
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
19
|
+
sheetId: z.number().describe('ID of the sheet'),
|
|
20
|
+
dimension: z.enum(['ROWS', 'COLUMNS']).describe('Whether to operate on rows or columns'),
|
|
21
|
+
operation: z
|
|
22
|
+
.enum(['insert', 'delete', 'append', 'autoResize'])
|
|
23
|
+
.describe('Operation to perform'),
|
|
24
|
+
startIndex: z
|
|
25
|
+
.number()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Start index (0-based) for insert/delete operations'),
|
|
28
|
+
endIndex: z
|
|
29
|
+
.number()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('End index (exclusive) for insert/delete operations'),
|
|
32
|
+
count: z.number().optional().describe('Number of rows/columns to append'),
|
|
33
|
+
inheritFromBefore: z
|
|
34
|
+
.boolean()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Inherit formatting from row/column before (insert only)')
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
.output(
|
|
40
|
+
z.object({
|
|
41
|
+
operationPerformed: z.string().describe('Description of the operation performed'),
|
|
42
|
+
affectedRange: z
|
|
43
|
+
.object({
|
|
44
|
+
dimension: z.enum(['ROWS', 'COLUMNS']).describe('Dimension affected'),
|
|
45
|
+
startIndex: z.number().optional().describe('Start index'),
|
|
46
|
+
endIndex: z.number().optional().describe('End index'),
|
|
47
|
+
count: z.number().optional().describe('Count affected')
|
|
48
|
+
})
|
|
49
|
+
.describe('Range that was affected')
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
.handleInvocation(async ctx => {
|
|
53
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
54
|
+
|
|
55
|
+
switch (ctx.input.operation) {
|
|
56
|
+
case 'insert': {
|
|
57
|
+
if (ctx.input.startIndex === undefined || ctx.input.endIndex === undefined) {
|
|
58
|
+
throw new Error('startIndex and endIndex are required for insert operation');
|
|
59
|
+
}
|
|
60
|
+
await client.insertDimension(
|
|
61
|
+
ctx.input.spreadsheetId,
|
|
62
|
+
ctx.input.sheetId,
|
|
63
|
+
ctx.input.dimension,
|
|
64
|
+
ctx.input.startIndex,
|
|
65
|
+
ctx.input.endIndex,
|
|
66
|
+
ctx.input.inheritFromBefore
|
|
67
|
+
);
|
|
68
|
+
let count = ctx.input.endIndex - ctx.input.startIndex;
|
|
69
|
+
return {
|
|
70
|
+
output: {
|
|
71
|
+
operationPerformed: `Inserted ${count} ${ctx.input.dimension.toLowerCase()} at index ${ctx.input.startIndex}`,
|
|
72
|
+
affectedRange: {
|
|
73
|
+
dimension: ctx.input.dimension,
|
|
74
|
+
startIndex: ctx.input.startIndex,
|
|
75
|
+
endIndex: ctx.input.endIndex,
|
|
76
|
+
count
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
message: `Inserted **${count}** ${ctx.input.dimension.toLowerCase()} starting at index ${ctx.input.startIndex}.`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'delete': {
|
|
84
|
+
if (ctx.input.startIndex === undefined || ctx.input.endIndex === undefined) {
|
|
85
|
+
throw new Error('startIndex and endIndex are required for delete operation');
|
|
86
|
+
}
|
|
87
|
+
await client.deleteDimension(
|
|
88
|
+
ctx.input.spreadsheetId,
|
|
89
|
+
ctx.input.sheetId,
|
|
90
|
+
ctx.input.dimension,
|
|
91
|
+
ctx.input.startIndex,
|
|
92
|
+
ctx.input.endIndex
|
|
93
|
+
);
|
|
94
|
+
let count = ctx.input.endIndex - ctx.input.startIndex;
|
|
95
|
+
return {
|
|
96
|
+
output: {
|
|
97
|
+
operationPerformed: `Deleted ${count} ${ctx.input.dimension.toLowerCase()} from index ${ctx.input.startIndex}`,
|
|
98
|
+
affectedRange: {
|
|
99
|
+
dimension: ctx.input.dimension,
|
|
100
|
+
startIndex: ctx.input.startIndex,
|
|
101
|
+
endIndex: ctx.input.endIndex,
|
|
102
|
+
count
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
message: `Deleted **${count}** ${ctx.input.dimension.toLowerCase()} starting at index ${ctx.input.startIndex}.`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case 'append': {
|
|
110
|
+
if (ctx.input.count === undefined) {
|
|
111
|
+
throw new Error('count is required for append operation');
|
|
112
|
+
}
|
|
113
|
+
await client.appendDimension(
|
|
114
|
+
ctx.input.spreadsheetId,
|
|
115
|
+
ctx.input.sheetId,
|
|
116
|
+
ctx.input.dimension,
|
|
117
|
+
ctx.input.count
|
|
118
|
+
);
|
|
119
|
+
return {
|
|
120
|
+
output: {
|
|
121
|
+
operationPerformed: `Appended ${ctx.input.count} ${ctx.input.dimension.toLowerCase()}`,
|
|
122
|
+
affectedRange: {
|
|
123
|
+
dimension: ctx.input.dimension,
|
|
124
|
+
count: ctx.input.count
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
message: `Appended **${ctx.input.count}** ${ctx.input.dimension.toLowerCase()} to the sheet.`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
case 'autoResize': {
|
|
132
|
+
await client.autoResizeDimensions(
|
|
133
|
+
ctx.input.spreadsheetId,
|
|
134
|
+
ctx.input.sheetId,
|
|
135
|
+
ctx.input.dimension,
|
|
136
|
+
ctx.input.startIndex,
|
|
137
|
+
ctx.input.endIndex
|
|
138
|
+
);
|
|
139
|
+
let rangeDesc =
|
|
140
|
+
ctx.input.startIndex !== undefined && ctx.input.endIndex !== undefined
|
|
141
|
+
? `from index ${ctx.input.startIndex} to ${ctx.input.endIndex}`
|
|
142
|
+
: 'all';
|
|
143
|
+
return {
|
|
144
|
+
output: {
|
|
145
|
+
operationPerformed: `Auto-resized ${ctx.input.dimension.toLowerCase()} ${rangeDesc}`,
|
|
146
|
+
affectedRange: {
|
|
147
|
+
dimension: ctx.input.dimension,
|
|
148
|
+
startIndex: ctx.input.startIndex,
|
|
149
|
+
endIndex: ctx.input.endIndex
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
message: `Auto-resized ${ctx.input.dimension.toLowerCase()} to fit content.`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Unknown operation: ${ctx.input.operation}`);
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
.build();
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let manageSheetsTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Manage Sheets',
|
|
8
|
+
key: 'manage_sheets',
|
|
9
|
+
description: `Manages sheets (tabs) within a Google Sheets spreadsheet.
|
|
10
|
+
Supports adding, deleting, duplicating, renaming, and reordering sheets.`,
|
|
11
|
+
tags: {
|
|
12
|
+
destructive: false,
|
|
13
|
+
readOnly: false
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
19
|
+
operation: z
|
|
20
|
+
.enum(['add', 'delete', 'duplicate', 'rename', 'move', 'hide', 'show'])
|
|
21
|
+
.describe('Operation to perform'),
|
|
22
|
+
sheetId: z
|
|
23
|
+
.number()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe(
|
|
26
|
+
'ID of the sheet to operate on (required for delete, duplicate, rename, move, hide, show)'
|
|
27
|
+
),
|
|
28
|
+
title: z.string().optional().describe('Title for new sheet (add) or new name (rename)'),
|
|
29
|
+
index: z
|
|
30
|
+
.number()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Index position for new sheet (add, duplicate) or new position (move)'),
|
|
33
|
+
rowCount: z.number().optional().describe('Initial row count for new sheet'),
|
|
34
|
+
columnCount: z.number().optional().describe('Initial column count for new sheet')
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
.output(
|
|
38
|
+
z.object({
|
|
39
|
+
sheetId: z.number().optional().describe('ID of the created/modified sheet'),
|
|
40
|
+
title: z.string().optional().describe('Title of the sheet'),
|
|
41
|
+
index: z.number().optional().describe('Index position of the sheet'),
|
|
42
|
+
operationPerformed: z.string().describe('Description of the operation performed')
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
.handleInvocation(async ctx => {
|
|
46
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
47
|
+
let output: {
|
|
48
|
+
sheetId?: number;
|
|
49
|
+
title?: string;
|
|
50
|
+
index?: number;
|
|
51
|
+
operationPerformed: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
switch (ctx.input.operation) {
|
|
55
|
+
case 'add': {
|
|
56
|
+
let result = await client.addSheet(ctx.input.spreadsheetId, {
|
|
57
|
+
title: ctx.input.title,
|
|
58
|
+
index: ctx.input.index,
|
|
59
|
+
gridProperties: {
|
|
60
|
+
rowCount: ctx.input.rowCount,
|
|
61
|
+
columnCount: ctx.input.columnCount
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
output = {
|
|
65
|
+
sheetId: result?.sheetId,
|
|
66
|
+
title: result?.title,
|
|
67
|
+
index: result?.index,
|
|
68
|
+
operationPerformed: `Added sheet "${result?.title}"`
|
|
69
|
+
};
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'delete': {
|
|
74
|
+
if (ctx.input.sheetId === undefined) {
|
|
75
|
+
throw new Error('sheetId is required for delete operation');
|
|
76
|
+
}
|
|
77
|
+
await client.deleteSheet(ctx.input.spreadsheetId, ctx.input.sheetId);
|
|
78
|
+
output = {
|
|
79
|
+
sheetId: ctx.input.sheetId,
|
|
80
|
+
operationPerformed: `Deleted sheet with ID ${ctx.input.sheetId}`
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case 'duplicate': {
|
|
86
|
+
if (ctx.input.sheetId === undefined) {
|
|
87
|
+
throw new Error('sheetId is required for duplicate operation');
|
|
88
|
+
}
|
|
89
|
+
let result = await client.duplicateSheet(ctx.input.spreadsheetId, ctx.input.sheetId, {
|
|
90
|
+
newSheetName: ctx.input.title,
|
|
91
|
+
insertSheetIndex: ctx.input.index
|
|
92
|
+
});
|
|
93
|
+
output = {
|
|
94
|
+
sheetId: result?.sheetId,
|
|
95
|
+
title: result?.title,
|
|
96
|
+
index: result?.index,
|
|
97
|
+
operationPerformed: `Duplicated sheet to "${result?.title}"`
|
|
98
|
+
};
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
case 'rename': {
|
|
103
|
+
if (ctx.input.sheetId === undefined) {
|
|
104
|
+
throw new Error('sheetId is required for rename operation');
|
|
105
|
+
}
|
|
106
|
+
if (!ctx.input.title) {
|
|
107
|
+
throw new Error('title is required for rename operation');
|
|
108
|
+
}
|
|
109
|
+
await client.updateSheetProperties(
|
|
110
|
+
ctx.input.spreadsheetId,
|
|
111
|
+
{ sheetId: ctx.input.sheetId, title: ctx.input.title, index: 0 },
|
|
112
|
+
'title'
|
|
113
|
+
);
|
|
114
|
+
output = {
|
|
115
|
+
sheetId: ctx.input.sheetId,
|
|
116
|
+
title: ctx.input.title,
|
|
117
|
+
operationPerformed: `Renamed sheet to "${ctx.input.title}"`
|
|
118
|
+
};
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case 'move': {
|
|
123
|
+
if (ctx.input.sheetId === undefined) {
|
|
124
|
+
throw new Error('sheetId is required for move operation');
|
|
125
|
+
}
|
|
126
|
+
if (ctx.input.index === undefined) {
|
|
127
|
+
throw new Error('index is required for move operation');
|
|
128
|
+
}
|
|
129
|
+
await client.updateSheetProperties(
|
|
130
|
+
ctx.input.spreadsheetId,
|
|
131
|
+
{ sheetId: ctx.input.sheetId, index: ctx.input.index, title: '' },
|
|
132
|
+
'index'
|
|
133
|
+
);
|
|
134
|
+
output = {
|
|
135
|
+
sheetId: ctx.input.sheetId,
|
|
136
|
+
index: ctx.input.index,
|
|
137
|
+
operationPerformed: `Moved sheet to index ${ctx.input.index}`
|
|
138
|
+
};
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case 'hide': {
|
|
143
|
+
if (ctx.input.sheetId === undefined) {
|
|
144
|
+
throw new Error('sheetId is required for hide operation');
|
|
145
|
+
}
|
|
146
|
+
await client.updateSheetProperties(
|
|
147
|
+
ctx.input.spreadsheetId,
|
|
148
|
+
{ sheetId: ctx.input.sheetId, hidden: true, index: 0, title: '' },
|
|
149
|
+
'hidden'
|
|
150
|
+
);
|
|
151
|
+
output = {
|
|
152
|
+
sheetId: ctx.input.sheetId,
|
|
153
|
+
operationPerformed: `Hidden sheet with ID ${ctx.input.sheetId}`
|
|
154
|
+
};
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case 'show': {
|
|
159
|
+
if (ctx.input.sheetId === undefined) {
|
|
160
|
+
throw new Error('sheetId is required for show operation');
|
|
161
|
+
}
|
|
162
|
+
await client.updateSheetProperties(
|
|
163
|
+
ctx.input.spreadsheetId,
|
|
164
|
+
{ sheetId: ctx.input.sheetId, hidden: false, index: 0, title: '' },
|
|
165
|
+
'hidden'
|
|
166
|
+
);
|
|
167
|
+
output = {
|
|
168
|
+
sheetId: ctx.input.sheetId,
|
|
169
|
+
operationPerformed: `Shown sheet with ID ${ctx.input.sheetId}`
|
|
170
|
+
};
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
default:
|
|
175
|
+
throw new Error(`Unknown operation: ${ctx.input.operation}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
output,
|
|
180
|
+
message: output.operationPerformed
|
|
181
|
+
};
|
|
182
|
+
})
|
|
183
|
+
.build();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let mergeCellsTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Merge Cells',
|
|
8
|
+
key: 'merge_cells',
|
|
9
|
+
description: `Merges or unmerges cells in a Google Sheets spreadsheet.
|
|
10
|
+
Supports merging all cells in a range, merging cells column by column, or row by row.`,
|
|
11
|
+
tags: {
|
|
12
|
+
destructive: false,
|
|
13
|
+
readOnly: false
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
19
|
+
sheetId: z.number().describe('ID of the sheet'),
|
|
20
|
+
startRowIndex: z.number().describe('Start row index (0-based)'),
|
|
21
|
+
endRowIndex: z.number().describe('End row index (exclusive)'),
|
|
22
|
+
startColumnIndex: z.number().describe('Start column index (0-based)'),
|
|
23
|
+
endColumnIndex: z.number().describe('End column index (exclusive)'),
|
|
24
|
+
operation: z.enum(['merge', 'unmerge']).describe('Whether to merge or unmerge cells'),
|
|
25
|
+
mergeType: z
|
|
26
|
+
.enum(['MERGE_ALL', 'MERGE_COLUMNS', 'MERGE_ROWS'])
|
|
27
|
+
.optional()
|
|
28
|
+
.describe(
|
|
29
|
+
'Type of merge: MERGE_ALL (single cell), MERGE_COLUMNS (merge within columns), MERGE_ROWS (merge within rows)'
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
.output(
|
|
34
|
+
z.object({
|
|
35
|
+
operationPerformed: z.string().describe('Description of the operation performed'),
|
|
36
|
+
range: z.string().describe('Description of the affected range')
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
.handleInvocation(async ctx => {
|
|
40
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
41
|
+
|
|
42
|
+
let range = {
|
|
43
|
+
sheetId: ctx.input.sheetId,
|
|
44
|
+
startRowIndex: ctx.input.startRowIndex,
|
|
45
|
+
endRowIndex: ctx.input.endRowIndex,
|
|
46
|
+
startColumnIndex: ctx.input.startColumnIndex,
|
|
47
|
+
endColumnIndex: ctx.input.endColumnIndex
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (ctx.input.operation === 'merge') {
|
|
51
|
+
await client.mergeCells(
|
|
52
|
+
ctx.input.spreadsheetId,
|
|
53
|
+
range,
|
|
54
|
+
ctx.input.mergeType || 'MERGE_ALL'
|
|
55
|
+
);
|
|
56
|
+
} else {
|
|
57
|
+
await client.unmergeCells(ctx.input.spreadsheetId, range);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let rowCount = ctx.input.endRowIndex - ctx.input.startRowIndex;
|
|
61
|
+
let colCount = ctx.input.endColumnIndex - ctx.input.startColumnIndex;
|
|
62
|
+
|
|
63
|
+
let output = {
|
|
64
|
+
operationPerformed:
|
|
65
|
+
ctx.input.operation === 'merge'
|
|
66
|
+
? `Merged cells (${ctx.input.mergeType || 'MERGE_ALL'})`
|
|
67
|
+
: 'Unmerged cells',
|
|
68
|
+
range: `${rowCount} row(s) x ${colCount} column(s)`
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
output,
|
|
73
|
+
message: `${ctx.input.operation === 'merge' ? 'Merged' : 'Unmerged'} cells in range (${rowCount} x ${colCount}).`
|
|
74
|
+
};
|
|
75
|
+
})
|
|
76
|
+
.build();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let namedRangesTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Manage Named Ranges',
|
|
8
|
+
key: 'manage_named_ranges',
|
|
9
|
+
description: `Creates or deletes named ranges in a Google Sheets spreadsheet.
|
|
10
|
+
Named ranges allow you to reference a cell range by name instead of A1 notation, making formulas more readable.`,
|
|
11
|
+
tags: {
|
|
12
|
+
destructive: false,
|
|
13
|
+
readOnly: false
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
19
|
+
operation: z.enum(['create', 'delete']).describe('Operation to perform'),
|
|
20
|
+
name: z.string().optional().describe('Name for the range (required for create)'),
|
|
21
|
+
namedRangeId: z.string().optional().describe('ID of the named range to delete'),
|
|
22
|
+
sheetId: z
|
|
23
|
+
.number()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Sheet ID containing the range (required for create)'),
|
|
26
|
+
startRowIndex: z.number().optional().describe('Start row index (0-based)'),
|
|
27
|
+
endRowIndex: z.number().optional().describe('End row index (exclusive)'),
|
|
28
|
+
startColumnIndex: z.number().optional().describe('Start column index (0-based)'),
|
|
29
|
+
endColumnIndex: z.number().optional().describe('End column index (exclusive)')
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
.output(
|
|
33
|
+
z.object({
|
|
34
|
+
namedRangeId: z.string().optional().describe('ID of the named range'),
|
|
35
|
+
name: z.string().optional().describe('Name of the range'),
|
|
36
|
+
operationPerformed: z.string().describe('Description of the operation')
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
.handleInvocation(async ctx => {
|
|
40
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
41
|
+
|
|
42
|
+
if (ctx.input.operation === 'create') {
|
|
43
|
+
if (!ctx.input.name) {
|
|
44
|
+
throw new Error('name is required for create operation');
|
|
45
|
+
}
|
|
46
|
+
if (ctx.input.sheetId === undefined) {
|
|
47
|
+
throw new Error('sheetId is required for create operation');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let namedRangeId = await client.addNamedRange(ctx.input.spreadsheetId, ctx.input.name, {
|
|
51
|
+
sheetId: ctx.input.sheetId,
|
|
52
|
+
startRowIndex: ctx.input.startRowIndex,
|
|
53
|
+
endRowIndex: ctx.input.endRowIndex,
|
|
54
|
+
startColumnIndex: ctx.input.startColumnIndex,
|
|
55
|
+
endColumnIndex: ctx.input.endColumnIndex
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
output: {
|
|
60
|
+
namedRangeId,
|
|
61
|
+
name: ctx.input.name,
|
|
62
|
+
operationPerformed: `Created named range "${ctx.input.name}"`
|
|
63
|
+
},
|
|
64
|
+
message: `Created named range **"${ctx.input.name}"** with ID \`${namedRangeId}\`.`
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
if (!ctx.input.namedRangeId) {
|
|
68
|
+
throw new Error('namedRangeId is required for delete operation');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await client.deleteNamedRange(ctx.input.spreadsheetId, ctx.input.namedRangeId);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
output: {
|
|
75
|
+
namedRangeId: ctx.input.namedRangeId,
|
|
76
|
+
operationPerformed: 'Deleted named range'
|
|
77
|
+
},
|
|
78
|
+
message: `Deleted named range with ID \`${ctx.input.namedRangeId}\`.`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.build();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let protectRangeTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Protect Range',
|
|
8
|
+
key: 'protect_range',
|
|
9
|
+
description: `Protects or unprotects a range of cells in a Google Sheets spreadsheet.
|
|
10
|
+
Protected ranges prevent unauthorized users from editing specified cells.`,
|
|
11
|
+
tags: {
|
|
12
|
+
destructive: false,
|
|
13
|
+
readOnly: false
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
19
|
+
operation: z.enum(['protect', 'unprotect']).describe('Whether to protect or unprotect'),
|
|
20
|
+
sheetId: z.number().optional().describe('ID of the sheet (required for protect)'),
|
|
21
|
+
startRowIndex: z.number().optional().describe('Start row index (0-based)'),
|
|
22
|
+
endRowIndex: z.number().optional().describe('End row index (exclusive)'),
|
|
23
|
+
startColumnIndex: z.number().optional().describe('Start column index (0-based)'),
|
|
24
|
+
endColumnIndex: z.number().optional().describe('End column index (exclusive)'),
|
|
25
|
+
protectedRangeId: z
|
|
26
|
+
.number()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('ID of the protected range to remove (for unprotect)'),
|
|
29
|
+
description: z.string().optional().describe('Description of why the range is protected'),
|
|
30
|
+
warningOnly: z.boolean().optional().describe('Show warning instead of blocking edits'),
|
|
31
|
+
editors: z
|
|
32
|
+
.object({
|
|
33
|
+
users: z
|
|
34
|
+
.array(z.string())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Email addresses of users who can edit'),
|
|
37
|
+
groups: z
|
|
38
|
+
.array(z.string())
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Email addresses of groups who can edit'),
|
|
41
|
+
domainUsersCanEdit: z.boolean().optional().describe('Allow all domain users to edit')
|
|
42
|
+
})
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Users/groups allowed to edit the protected range')
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
.output(
|
|
48
|
+
z.object({
|
|
49
|
+
protectedRangeId: z.number().optional().describe('ID of the protected range'),
|
|
50
|
+
operationPerformed: z.string().describe('Description of the operation')
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
.handleInvocation(async ctx => {
|
|
54
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
55
|
+
|
|
56
|
+
if (ctx.input.operation === 'protect') {
|
|
57
|
+
if (ctx.input.sheetId === undefined) {
|
|
58
|
+
throw new Error('sheetId is required for protect operation');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let protectedRangeId = await client.addProtectedRange(
|
|
62
|
+
ctx.input.spreadsheetId,
|
|
63
|
+
{
|
|
64
|
+
sheetId: ctx.input.sheetId,
|
|
65
|
+
startRowIndex: ctx.input.startRowIndex,
|
|
66
|
+
endRowIndex: ctx.input.endRowIndex,
|
|
67
|
+
startColumnIndex: ctx.input.startColumnIndex,
|
|
68
|
+
endColumnIndex: ctx.input.endColumnIndex
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
description: ctx.input.description,
|
|
72
|
+
warningOnly: ctx.input.warningOnly,
|
|
73
|
+
editors: ctx.input.editors
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
output: {
|
|
79
|
+
protectedRangeId,
|
|
80
|
+
operationPerformed: 'Created protected range'
|
|
81
|
+
},
|
|
82
|
+
message: `Protected range created with ID **${protectedRangeId}**.`
|
|
83
|
+
};
|
|
84
|
+
} else {
|
|
85
|
+
if (ctx.input.protectedRangeId === undefined) {
|
|
86
|
+
throw new Error('protectedRangeId is required for unprotect operation');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await client.deleteProtectedRange(ctx.input.spreadsheetId, ctx.input.protectedRangeId);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
output: {
|
|
93
|
+
protectedRangeId: ctx.input.protectedRangeId,
|
|
94
|
+
operationPerformed: 'Removed protected range'
|
|
95
|
+
},
|
|
96
|
+
message: `Removed protected range with ID **${ctx.input.protectedRangeId}**.`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
.build();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SheetsClient } from '../lib/client';
|
|
4
|
+
import { spec } from '../spec';
|
|
5
|
+
|
|
6
|
+
export let readValuesTool = SlateTool.create(spec, {
|
|
7
|
+
name: 'Read Values',
|
|
8
|
+
key: 'read_values',
|
|
9
|
+
description: `Reads cell values from a Google Sheets spreadsheet.
|
|
10
|
+
Supports reading a single range or multiple ranges in one request. Values can be returned as formatted strings, raw values, or formulas.`,
|
|
11
|
+
instructions: [
|
|
12
|
+
'Use A1 notation for ranges (e.g., "Sheet1!A1:B10", "A1:C5")',
|
|
13
|
+
'Omit sheet name to read from the first sheet'
|
|
14
|
+
],
|
|
15
|
+
tags: {
|
|
16
|
+
destructive: false,
|
|
17
|
+
readOnly: true
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
.input(
|
|
21
|
+
z.object({
|
|
22
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
23
|
+
ranges: z
|
|
24
|
+
.array(z.string())
|
|
25
|
+
.describe('Ranges to read in A1 notation (e.g., ["Sheet1!A1:B10", "Sheet2!C1:D5"])'),
|
|
26
|
+
valueRenderOption: z
|
|
27
|
+
.enum(['FORMATTED_VALUE', 'UNFORMATTED_VALUE', 'FORMULA'])
|
|
28
|
+
.optional()
|
|
29
|
+
.describe(
|
|
30
|
+
'How values should be rendered: FORMATTED_VALUE (display value), UNFORMATTED_VALUE (raw value), or FORMULA'
|
|
31
|
+
),
|
|
32
|
+
dateTimeRenderOption: z
|
|
33
|
+
.enum(['SERIAL_NUMBER', 'FORMATTED_STRING'])
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('How dates should be rendered'),
|
|
36
|
+
majorDimension: z
|
|
37
|
+
.enum(['ROWS', 'COLUMNS'])
|
|
38
|
+
.optional()
|
|
39
|
+
.describe('Whether to return data row-by-row or column-by-column')
|
|
40
|
+
})
|
|
41
|
+
)
|
|
42
|
+
.output(
|
|
43
|
+
z.object({
|
|
44
|
+
spreadsheetId: z.string().describe('ID of the spreadsheet'),
|
|
45
|
+
valueRanges: z
|
|
46
|
+
.array(
|
|
47
|
+
z.object({
|
|
48
|
+
range: z.string().optional().describe('The range that was read'),
|
|
49
|
+
majorDimension: z
|
|
50
|
+
.enum(['ROWS', 'COLUMNS'])
|
|
51
|
+
.optional()
|
|
52
|
+
.describe('How the data is organized'),
|
|
53
|
+
values: z
|
|
54
|
+
.array(z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])))
|
|
55
|
+
.optional()
|
|
56
|
+
.describe('2D array of cell values')
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
.describe('Values for each requested range')
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
.handleInvocation(async ctx => {
|
|
63
|
+
let client = new SheetsClient({ token: ctx.auth.token });
|
|
64
|
+
|
|
65
|
+
let result = await client.batchGetValues(ctx.input.spreadsheetId, ctx.input.ranges, {
|
|
66
|
+
valueRenderOption: ctx.input.valueRenderOption,
|
|
67
|
+
dateTimeRenderOption: ctx.input.dateTimeRenderOption,
|
|
68
|
+
majorDimension: ctx.input.majorDimension
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let totalCells = 0;
|
|
72
|
+
for (let vr of result.valueRanges || []) {
|
|
73
|
+
if (vr.values) {
|
|
74
|
+
for (let row of vr.values) {
|
|
75
|
+
totalCells += row.length;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let output = {
|
|
81
|
+
spreadsheetId: result.spreadsheetId || ctx.input.spreadsheetId,
|
|
82
|
+
valueRanges: (result.valueRanges || []).map(vr => ({
|
|
83
|
+
range: vr.range,
|
|
84
|
+
majorDimension: vr.majorDimension,
|
|
85
|
+
values: vr.values
|
|
86
|
+
}))
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
output,
|
|
91
|
+
message: `Read **${totalCells}** cells from **${ctx.input.ranges.length}** range(s).`
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
.build();
|