@libai168/dsh-tool-google-drive 0.2.0

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/lib/index.js ADDED
@@ -0,0 +1,339 @@
1
+ import { defineTool } from '@deepseek-ai/dsh-tools';
2
+ import { GoogleDriveClient, GoogleDriveError } from './client.js';
3
+ export const name = 'dsh-tool-google-drive';
4
+ export const inject = ['tools'];
5
+ export function apply(ctx, config = {}) {
6
+ const client = new GoogleDriveClient(config);
7
+ for (const tool of createTools(client))
8
+ ctx.tools.register(tool);
9
+ }
10
+ function unavailable(reason) {
11
+ return { found: false, reason };
12
+ }
13
+ function text(value) {
14
+ return [{ type: 'text', text: value }];
15
+ }
16
+ function fileProperties() {
17
+ return {
18
+ id: { type: 'string' }, name: { type: 'string' }, mimeType: { type: 'string' }, kind: { type: 'string' },
19
+ description: { type: 'string' }, iconLink: { type: 'string' }, webViewLink: { type: 'string' }, webContentLink: { type: 'string' },
20
+ size: { type: 'string' }, starred: { type: 'boolean' }, trashed: { type: 'boolean' }, modifiedTime: { type: 'string' },
21
+ createdTime: { type: 'string' }, driveId: { type: 'string' }, parents: { type: 'array', items: { type: 'string' } },
22
+ owners: { type: 'array', items: { type: 'object', additionalProperties: false, properties: {
23
+ displayName: { type: 'string' }, emailAddress: { type: 'string' },
24
+ } } },
25
+ };
26
+ }
27
+ function driveProperties() {
28
+ return {
29
+ id: { type: 'string' }, name: { type: 'string' }, colorRgb: { type: 'string' }, backgroundImageLink: { type: 'string' },
30
+ createdTime: { type: 'string' }, hidden: { type: 'boolean' }, themeId: { type: 'string' },
31
+ capabilities: { type: 'string' }, restrictions: { type: 'string' },
32
+ };
33
+ }
34
+ function renderFiles(items) {
35
+ if (!items.length)
36
+ return text('No Google Drive files found.');
37
+ return text(items.map(file => `${file.name ?? ''} (${file.id ?? ''}) ${file.mimeType ?? ''} ${file.modifiedTime ?? ''}`).join('\n'));
38
+ }
39
+ function renderFile(file) {
40
+ return text([
41
+ `${file.name ?? ''} (${file.id ?? ''})`,
42
+ `mimeType=${file.mimeType ?? ''}`,
43
+ `modified=${file.modifiedTime ?? ''}`,
44
+ `link=${file.webViewLink ?? ''}`,
45
+ file.description ? `description=${file.description}` : '',
46
+ ].filter(Boolean).join('\n'));
47
+ }
48
+ function renderPermissions(items) {
49
+ if (!items.length)
50
+ return text('No Google Drive permissions found.');
51
+ return text(items.map(permission => {
52
+ const subject = permission.displayName || permission.emailAddress || permission.id || '';
53
+ return subject + ' (' + (permission.type ?? '') + ') role=' + (permission.role ?? '') + (permission.deleted ? ' deleted' : '');
54
+ }).join('\\n'));
55
+ }
56
+ function renderRevisions(items) {
57
+ if (!items.length)
58
+ return text('No Google Drive revisions found.');
59
+ return text(items.map(revision => [
60
+ revision.id ?? '', revision.modifiedTime ?? '', revision.mimeType ? 'mimeType=' + revision.mimeType : '',
61
+ revision.size ? 'size=' + revision.size : '', revision.keepForever ? 'keepForever' : '', revision.published ? 'published' : '',
62
+ ].filter(Boolean).join(' ')).join('\\n'));
63
+ }
64
+ function renderDrives(items) {
65
+ if (!items.length)
66
+ return text('No shared drives found.');
67
+ return text(items.map(drive => `${drive.name ?? ''} (${drive.id ?? ''}) ${drive.createdTime ?? ''}`).join('\n'));
68
+ }
69
+ function renderDocument(doc) {
70
+ return text([
71
+ `${doc.title ?? ''} (${doc.documentId ?? ''})`,
72
+ `revision=${doc.revisionId ?? ''} tabs=${doc.tabCount ?? 0}`,
73
+ (doc.text ?? '').slice(0, 4000),
74
+ ].filter(Boolean).join('\n'));
75
+ }
76
+ function renderSpreadsheet(sheet) {
77
+ const tabs = (sheet.sheets ?? []).map(s => `${s.title ?? ''} ${s.rowCount ?? 0}x${s.columnCount ?? 0}`).join('\n');
78
+ return text([
79
+ `${sheet.title ?? ''} (${sheet.spreadsheetId ?? ''})`,
80
+ `sheets=${sheet.sheetCount ?? 0}`,
81
+ `url=${sheet.spreadsheetUrl ?? ''}`,
82
+ tabs,
83
+ ].filter(Boolean).join('\n'));
84
+ }
85
+ function renderValues(value) {
86
+ const rows = (value.values ?? []).slice(0, 20).map(row => row.join('\t')).join('\n');
87
+ return text([`${value.range ?? ''} rows=${value.rowCount ?? 0} columns=${value.columnCount ?? 0}`, rows].filter(Boolean).join('\n'));
88
+ }
89
+ export function createTools(client) {
90
+ return [
91
+ defineTool({
92
+ name: 'gdrive_auth_test',
93
+ description: 'Verify Google Drive credentials and return token metadata.',
94
+ parameters: {},
95
+ output: {
96
+ schema: {
97
+ type: 'object', additionalProperties: false,
98
+ properties: { ok: { type: 'boolean' }, reason: { type: 'string' }, authMethod: { type: 'string' }, tokenPreview: { type: 'string' } },
99
+ },
100
+ render: (_args, value) => value.ok ? text(`authMethod: ${value.authMethod}\ntoken: ${value.tokenPreview}`) : text(`Google Drive auth failed: ${value.reason}`),
101
+ },
102
+ presentCall() { return { card: 'generic', title: 'Verify Google Drive credentials', kind: 'read' }; },
103
+ async execute(_args, exec) {
104
+ if (!client.hasCredentials())
105
+ return { ok: false, reason: 'Google Drive accessToken or refresh token credentials are not configured.' };
106
+ try {
107
+ return await client.authTest(exec.signal);
108
+ }
109
+ catch (error) {
110
+ if (error instanceof GoogleDriveError)
111
+ return { ok: false, reason: error.message };
112
+ throw error;
113
+ }
114
+ },
115
+ }),
116
+ defineTool({
117
+ name: 'gdrive_list_files',
118
+ description: 'List or search Google Drive files by query and pagination.',
119
+ parameters: {
120
+ query: { type: 'string', description: 'Optional Drive query string, such as name contains "report" and trashed = false' },
121
+ pageSize: { type: 'integer', description: 'Maximum results per page, 1-1000 (default 100)' }, pageToken: { type: 'string', description: 'Opaque cursor from a previous response' },
122
+ orderBy: { type: 'string', description: 'Sort order, such as modifiedTime desc or name' }, spaces: { type: 'string', description: 'Drive space, usually drive' },
123
+ corpora: { type: 'string', description: 'Corpora selector: user, domain, drive, or allDrives' }, driveId: { type: 'string', description: 'Shared drive ID when corpora=drive' },
124
+ includeItemsFromAllDrives: { type: 'boolean', description: 'Include My Drive and shared drive items' }, supportsAllDrives: { type: 'boolean', description: 'Enable shared drive support' },
125
+ },
126
+ output: { schema: { type: 'object', additionalProperties: false, properties: {
127
+ found: { type: 'boolean' }, reason: { type: 'string' }, items: { type: 'array', items: { type: 'object', additionalProperties: false, properties: fileProperties() } },
128
+ nextPageToken: { type: 'string' }, incompleteSearch: { type: 'boolean' },
129
+ } }, render: (_args, value) => value.found ? renderFiles(value.items ?? []) : text(value.reason ?? 'Google Drive is not configured.') },
130
+ presentCall(args) { return { card: 'generic', title: `Google Drive files ${args.query ?? ''}`.trim(), kind: 'search' }; },
131
+ async execute(args, exec) {
132
+ if (!client.hasCredentials())
133
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
134
+ try {
135
+ return { found: true, ...await client.listFiles({ query: args.query, pageSize: args.pageSize, pageToken: args.pageToken, orderBy: args.orderBy, spaces: args.spaces, corpora: args.corpora, driveId: args.driveId, includeItemsFromAllDrives: args.includeItemsFromAllDrives, supportsAllDrives: args.supportsAllDrives, signal: exec.signal }) };
136
+ }
137
+ catch (error) {
138
+ if (error instanceof GoogleDriveError)
139
+ return unavailable(error.message);
140
+ throw error;
141
+ }
142
+ },
143
+ }),
144
+ defineTool({
145
+ name: 'gdrive_get_file',
146
+ description: 'Get one Google Drive file metadata by file ID.',
147
+ parameters: { fileId: { type: 'string', required: true, description: 'Google Drive file ID' }, fields: { type: 'string', description: 'Optional Drive fields selector' }, supportsAllDrives: { type: 'boolean', description: 'Enable shared drive support' } },
148
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, ...fileProperties() } }, render: (_args, value) => value.found ? renderFile(value) : text(value.reason ?? 'Google Drive is not configured.') },
149
+ presentCall(args) { return { card: 'generic', title: `Google Drive file ${args.fileId ?? ''}`, kind: 'read' }; },
150
+ async execute(args, exec) {
151
+ if (!client.hasCredentials())
152
+ return { found: false, reason: 'Google Drive accessToken or refresh token credentials are not configured.' };
153
+ try {
154
+ return { found: true, ...await client.getFile(args.fileId, { fields: args.fields, supportsAllDrives: args.supportsAllDrives, signal: exec.signal }) };
155
+ }
156
+ catch (error) {
157
+ if (error instanceof GoogleDriveError)
158
+ return { found: false, reason: error.message };
159
+ throw error;
160
+ }
161
+ },
162
+ }),
163
+ defineTool({
164
+ name: 'gdrive_list_permissions',
165
+ description: 'List sharing permissions for one Google Drive file, folder, or shared drive with pagination.',
166
+ parameters: {
167
+ fileId: { type: 'string', required: true, description: 'Google Drive file, folder, or shared drive ID' },
168
+ pageSize: { type: 'integer', description: 'Maximum permissions per page' },
169
+ pageToken: { type: 'string', description: 'Opaque cursor from a previous response' },
170
+ supportsAllDrives: { type: 'boolean', description: 'Enable My Drive and shared drive support' },
171
+ useDomainAdminAccess: { type: 'boolean', description: 'Use domain administrator access for shared drives when allowed' },
172
+ includePermissionsForView: { type: 'string', description: 'Additional permission view, currently published' },
173
+ fields: { type: 'string', description: 'Optional Drive fields selector' },
174
+ },
175
+ output: { schema: { type: 'object', additionalProperties: false, properties: {
176
+ found: { type: 'boolean' }, reason: { type: 'string' }, fileId: { type: 'string' }, nextPageToken: { type: 'string' },
177
+ items: { type: 'array', items: { type: 'object', additionalProperties: false, properties: {
178
+ id: { type: 'string' }, type: { type: 'string' }, kind: { type: 'string' }, role: { type: 'string' }, emailAddress: { type: 'string' }, displayName: { type: 'string' }, allowFileDiscovery: { type: 'boolean' }, expirationTime: { type: 'string' }, deleted: { type: 'boolean' }, permissionDetails: { type: 'string' },
179
+ } } },
180
+ } }, render: (_args, value) => value.found ? renderPermissions(value.items ?? []) : text(value.reason ?? 'Google Drive is not configured.') },
181
+ presentCall(args) { return { card: 'generic', title: 'Google Drive permissions ' + (args.fileId ?? ''), kind: 'search' }; },
182
+ async execute(args, exec) {
183
+ if (!client.hasCredentials())
184
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
185
+ try {
186
+ return { found: true, ...await client.listPermissions(args.fileId, { pageSize: args.pageSize, pageToken: args.pageToken, supportsAllDrives: args.supportsAllDrives, useDomainAdminAccess: args.useDomainAdminAccess, includePermissionsForView: args.includePermissionsForView, fields: args.fields, signal: exec.signal }) };
187
+ }
188
+ catch (error) {
189
+ if (error instanceof GoogleDriveError)
190
+ return unavailable(error.message);
191
+ throw error;
192
+ }
193
+ },
194
+ }),
195
+ defineTool({
196
+ name: 'gdrive_list_revisions',
197
+ description: 'List revisions for one Google Drive file with pagination.',
198
+ parameters: {
199
+ fileId: { type: 'string', required: true, description: 'Google Drive file ID' },
200
+ pageSize: { type: 'integer', description: 'Maximum revisions per page' },
201
+ pageToken: { type: 'string', description: 'Opaque cursor from a previous response' },
202
+ fields: { type: 'string', description: 'Optional Drive fields selector' },
203
+ },
204
+ output: { schema: { type: 'object', additionalProperties: false, properties: {
205
+ found: { type: 'boolean' }, reason: { type: 'string' }, fileId: { type: 'string' }, nextPageToken: { type: 'string' },
206
+ items: { type: 'array', items: { type: 'object', additionalProperties: false, properties: {
207
+ id: { type: 'string' }, mimeType: { type: 'string' }, modifiedTime: { type: 'string' }, keepForever: { type: 'boolean' }, published: { type: 'boolean' }, publishAuto: { type: 'boolean' }, publishedOutsideDomain: { type: 'boolean' }, size: { type: 'string' }, originalFilename: { type: 'string' }, md5Checksum: { type: 'string' }, downloadUrl: { type: 'string' }, exportLinks: { type: 'string' },
208
+ } } },
209
+ } }, render: (_args, value) => value.found ? renderRevisions(value.items ?? []) : text(value.reason ?? 'Google Drive is not configured.') },
210
+ presentCall(args) { return { card: 'generic', title: 'Google Drive revisions ' + (args.fileId ?? ''), kind: 'search' }; },
211
+ async execute(args, exec) {
212
+ if (!client.hasCredentials())
213
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
214
+ try {
215
+ return { found: true, ...await client.listRevisions(args.fileId, { pageSize: args.pageSize, pageToken: args.pageToken, fields: args.fields, signal: exec.signal }) };
216
+ }
217
+ catch (error) {
218
+ if (error instanceof GoogleDriveError)
219
+ return unavailable(error.message);
220
+ throw error;
221
+ }
222
+ },
223
+ }),
224
+ defineTool({
225
+ name: 'gdrive_export_file',
226
+ description: 'Export a Google Workspace file to text or another MIME type. Read-only operation.',
227
+ parameters: { fileId: { type: 'string', required: true, description: 'Google Drive file ID' }, exportMimeType: { type: 'string', description: 'Export MIME type, default text/plain' }, responseEncoding: { type: 'string', description: 'Output encoding: text or base64 (default text)' }, supportsAllDrives: { type: 'boolean', description: 'Enable shared drive support' } },
228
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, fileId: { type: 'string' }, exportMimeType: { type: 'string' }, encoding: { type: 'string' }, content: { type: 'string' }, contentLength: { type: 'number' } } }, render: (_args, value) => value.found ? text((value.content ?? '').slice(0, 4000)) : text(value.reason ?? 'Google Drive is not configured.') },
229
+ presentCall(args) { return { card: 'generic', title: `Export Google Drive file ${args.fileId ?? ''}`, kind: 'read' }; },
230
+ async execute(args, exec) {
231
+ if (!client.hasCredentials())
232
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
233
+ try {
234
+ return { found: true, ...await client.exportFile(args.fileId, { exportMimeType: args.exportMimeType, responseEncoding: args.responseEncoding, supportsAllDrives: args.supportsAllDrives, signal: exec.signal }) };
235
+ }
236
+ catch (error) {
237
+ if (error instanceof GoogleDriveError)
238
+ return unavailable(error.message);
239
+ throw error;
240
+ }
241
+ },
242
+ }),
243
+ defineTool({
244
+ name: 'gdrive_list_shared_drives',
245
+ description: 'List shared drives accessible to the user with pagination.',
246
+ parameters: { pageSize: { type: 'integer', description: 'Maximum results per page' }, pageToken: { type: 'string', description: 'Opaque cursor from previous response' }, query: { type: 'string', description: 'Optional shared drive query' }, useDomainAdminAccess: { type: 'boolean', description: 'Issue request as domain admin when allowed' } },
247
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, items: { type: 'array', items: { type: 'object', additionalProperties: false, properties: driveProperties() } }, nextPageToken: { type: 'string' }, incompleteSearch: { type: 'boolean' } } }, render: (_args, value) => value.found ? renderDrives(value.items ?? []) : text(value.reason ?? 'Google Drive is not configured.') },
248
+ presentCall() { return { card: 'generic', title: 'Google shared drives', kind: 'search' }; },
249
+ async execute(args, exec) {
250
+ if (!client.hasCredentials())
251
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
252
+ try {
253
+ return { found: true, ...await client.listSharedDrives({ pageSize: args.pageSize, pageToken: args.pageToken, query: args.query, useDomainAdminAccess: args.useDomainAdminAccess, signal: exec.signal }) };
254
+ }
255
+ catch (error) {
256
+ if (error instanceof GoogleDriveError)
257
+ return unavailable(error.message);
258
+ throw error;
259
+ }
260
+ },
261
+ }),
262
+ defineTool({
263
+ name: 'gdrive_get_shared_drive',
264
+ description: 'Get one shared drive metadata record by shared drive ID.',
265
+ parameters: { driveId: { type: 'string', required: true, description: 'Google shared drive ID' }, useDomainAdminAccess: { type: 'boolean', description: 'Issue request as domain admin when allowed' } },
266
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, ...driveProperties() } }, render: (_args, value) => value.found ? renderDrives([value]) : text(value.reason ?? 'Google Drive is not configured.') },
267
+ presentCall(args) { return { card: 'generic', title: `Google shared drive ${args.driveId ?? ''}`, kind: 'read' }; },
268
+ async execute(args, exec) {
269
+ if (!client.hasCredentials())
270
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
271
+ try {
272
+ return { found: true, ...await client.getSharedDrive(args.driveId, { useDomainAdminAccess: args.useDomainAdminAccess, signal: exec.signal }) };
273
+ }
274
+ catch (error) {
275
+ if (error instanceof GoogleDriveError)
276
+ return unavailable(error.message);
277
+ throw error;
278
+ }
279
+ },
280
+ }),
281
+ defineTool({
282
+ name: 'gdocs_get_document',
283
+ description: 'Read a Google Docs document structure and extracted text by document ID.',
284
+ parameters: { documentId: { type: 'string', required: true, description: 'Google Docs document ID' }, includeTabsContent: { type: 'boolean', description: 'Include tab content, default true' }, suggestionsViewMode: { type: 'string', description: 'Optional suggestions view mode' }, commentsViewMode: { type: 'string', description: 'Optional comments view mode' }, fields: { type: 'string', description: 'Optional field mask' } },
285
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, documentId: { type: 'string' }, title: { type: 'string' }, revisionId: { type: 'string' }, text: { type: 'string' }, tabCount: { type: 'number' }, tabs: { type: 'array', items: { type: 'object', additionalProperties: false, properties: { tabId: { type: 'string' }, title: { type: 'string' }, index: { type: 'number' }, textLength: { type: 'number' } } } } } }, render: (_args, value) => value.found ? renderDocument(value) : text(value.reason ?? 'Google Docs is not configured.') },
286
+ presentCall(args) { return { card: 'generic', title: `Google Doc ${args.documentId ?? ''}`, kind: 'read' }; },
287
+ async execute(args, exec) {
288
+ if (!client.hasCredentials())
289
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
290
+ try {
291
+ return { found: true, ...await client.getDocument(args.documentId, { includeTabsContent: args.includeTabsContent, suggestionsViewMode: args.suggestionsViewMode, commentsViewMode: args.commentsViewMode, fields: args.fields, signal: exec.signal }) };
292
+ }
293
+ catch (error) {
294
+ if (error instanceof GoogleDriveError)
295
+ return unavailable(error.message);
296
+ throw error;
297
+ }
298
+ },
299
+ }),
300
+ defineTool({
301
+ name: 'gsheets_get_spreadsheet',
302
+ description: 'Read Google Sheets spreadsheet metadata and sheet properties.',
303
+ parameters: { spreadsheetId: { type: 'string', required: true, description: 'Google Sheets spreadsheet ID' }, ranges: { type: 'array', items: { type: 'string' }, description: 'Optional A1 ranges' }, includeGridData: { type: 'boolean', description: 'Include grid data' }, excludeTablesInBandedRanges: { type: 'boolean', description: 'Exclude tables in banded ranges' } },
304
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, spreadsheetId: { type: 'string' }, title: { type: 'string' }, spreadsheetUrl: { type: 'string' }, locale: { type: 'string' }, timeZone: { type: 'string' }, sheetCount: { type: 'number' }, sheets: { type: 'array', items: { type: 'object', additionalProperties: false, properties: { sheetId: { type: 'number' }, title: { type: 'string' }, index: { type: 'number' }, sheetType: { type: 'string' }, rowCount: { type: 'number' }, columnCount: { type: 'number' }, frozenRowCount: { type: 'number' } } } } } }, render: (_args, value) => value.found ? renderSpreadsheet(value) : text(value.reason ?? 'Google Sheets is not configured.') },
305
+ presentCall(args) { return { card: 'generic', title: `Google Sheet ${args.spreadsheetId ?? ''}`, kind: 'read' }; },
306
+ async execute(args, exec) {
307
+ if (!client.hasCredentials())
308
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
309
+ try {
310
+ return { found: true, ...await client.getSpreadsheet(args.spreadsheetId, { ranges: args.ranges, includeGridData: args.includeGridData, excludeTablesInBandedRanges: args.excludeTablesInBandedRanges, signal: exec.signal }) };
311
+ }
312
+ catch (error) {
313
+ if (error instanceof GoogleDriveError)
314
+ return unavailable(error.message);
315
+ throw error;
316
+ }
317
+ },
318
+ }),
319
+ defineTool({
320
+ name: 'gsheets_get_values',
321
+ description: 'Read values from one Google Sheets A1 range.',
322
+ parameters: { spreadsheetId: { type: 'string', required: true, description: 'Google Sheets spreadsheet ID' }, range: { type: 'string', required: true, description: 'A1 notation range, e.g. Sheet1!A1:D20' }, valueRenderOption: { type: 'string', description: 'FORMATTED_VALUE, UNFORMATTED_VALUE, or FORMULA' }, dateTimeRenderOption: { type: 'string', description: 'SERIAL_NUMBER or FORMATTED_STRING' }, majorDimension: { type: 'string', description: 'ROWS or COLUMNS' } },
323
+ output: { schema: { type: 'object', additionalProperties: false, properties: { found: { type: 'boolean' }, reason: { type: 'string' }, spreadsheetId: { type: 'string' }, range: { type: 'string' }, majorDimension: { type: 'string' }, rowCount: { type: 'number' }, columnCount: { type: 'number' }, values: { type: 'array', items: { type: 'array', items: { type: 'string' } } } } }, render: (_args, value) => value.found ? renderValues(value) : text(value.reason ?? 'Google Sheets is not configured.') },
324
+ presentCall(args) { return { card: 'generic', title: `Google Sheet values ${args.range ?? ''}`, kind: 'read' }; },
325
+ async execute(args, exec) {
326
+ if (!client.hasCredentials())
327
+ return unavailable('Google Drive accessToken or refresh token credentials are not configured.');
328
+ try {
329
+ return { found: true, ...await client.getSheetValues(args.spreadsheetId, args.range, { valueRenderOption: args.valueRenderOption, dateTimeRenderOption: args.dateTimeRenderOption, majorDimension: args.majorDimension, signal: exec.signal }) };
330
+ }
331
+ catch (error) {
332
+ if (error instanceof GoogleDriveError)
333
+ return unavailable(error.message);
334
+ throw error;
335
+ }
336
+ },
337
+ }),
338
+ ];
339
+ }
@@ -0,0 +1,225 @@
1
+ /** Google Drive API client with injected fetch for testability. */
2
+ export interface GoogleDriveClientOptions {
3
+ accessToken?: string;
4
+ clientId?: string;
5
+ clientSecret?: string;
6
+ refreshToken?: string;
7
+ baseUrl?: string;
8
+ tokenUrl?: string;
9
+ timeoutMs?: number;
10
+ fetchImpl?: typeof fetch;
11
+ }
12
+ export declare class GoogleDriveError extends Error {
13
+ readonly code: number;
14
+ constructor(message: string, code: number);
15
+ }
16
+ export interface DriveFileOwner {
17
+ displayName: string;
18
+ emailAddress: string;
19
+ }
20
+ export interface DriveFileInfo {
21
+ id: string;
22
+ name: string;
23
+ mimeType: string;
24
+ kind: string;
25
+ description: string;
26
+ iconLink: string;
27
+ webViewLink: string;
28
+ webContentLink: string;
29
+ size: string;
30
+ starred: boolean;
31
+ trashed: boolean;
32
+ modifiedTime: string;
33
+ createdTime: string;
34
+ driveId: string;
35
+ parents: string[];
36
+ owners: DriveFileOwner[];
37
+ }
38
+ export interface DrivePermissionInfo {
39
+ id: string;
40
+ type: string;
41
+ kind: string;
42
+ role: string;
43
+ emailAddress: string;
44
+ displayName: string;
45
+ allowFileDiscovery: boolean;
46
+ expirationTime: string;
47
+ deleted: boolean;
48
+ permissionDetails: string;
49
+ }
50
+ export interface DriveRevisionInfo {
51
+ id: string;
52
+ mimeType: string;
53
+ modifiedTime: string;
54
+ keepForever: boolean;
55
+ published: boolean;
56
+ publishAuto: boolean;
57
+ publishedOutsideDomain: boolean;
58
+ size: string;
59
+ originalFilename: string;
60
+ md5Checksum: string;
61
+ downloadUrl: string;
62
+ exportLinks: string;
63
+ }
64
+ export interface SharedDriveInfo {
65
+ id: string;
66
+ name: string;
67
+ colorRgb: string;
68
+ backgroundImageLink: string;
69
+ createdTime: string;
70
+ hidden: boolean;
71
+ themeId: string;
72
+ capabilities: string;
73
+ restrictions: string;
74
+ }
75
+ export interface GoogleDocTabInfo {
76
+ tabId: string;
77
+ title: string;
78
+ index: number;
79
+ textLength: number;
80
+ }
81
+ export interface GoogleDocumentInfo {
82
+ documentId: string;
83
+ title: string;
84
+ revisionId: string;
85
+ text: string;
86
+ tabCount: number;
87
+ tabs: GoogleDocTabInfo[];
88
+ }
89
+ export interface GoogleSheetPropertiesInfo {
90
+ sheetId: number;
91
+ title: string;
92
+ index: number;
93
+ sheetType: string;
94
+ rowCount: number;
95
+ columnCount: number;
96
+ frozenRowCount: number;
97
+ }
98
+ export interface GoogleSpreadsheetInfo {
99
+ spreadsheetId: string;
100
+ title: string;
101
+ spreadsheetUrl: string;
102
+ locale: string;
103
+ timeZone: string;
104
+ sheetCount: number;
105
+ sheets: GoogleSheetPropertiesInfo[];
106
+ }
107
+ export interface GoogleSheetValuesInfo {
108
+ spreadsheetId: string;
109
+ range: string;
110
+ majorDimension: string;
111
+ rowCount: number;
112
+ columnCount: number;
113
+ values: string[][];
114
+ }
115
+ export declare class GoogleDriveClient {
116
+ private readonly staticToken;
117
+ private readonly clientId;
118
+ private readonly clientSecret;
119
+ private readonly refreshToken;
120
+ private readonly baseUrl;
121
+ private readonly tokenUrl;
122
+ private readonly timeoutMs;
123
+ private readonly fetchImpl;
124
+ private tokenCache;
125
+ constructor(options?: GoogleDriveClientOptions);
126
+ hasCredentials(): boolean;
127
+ private buildUrl;
128
+ private getAccessToken;
129
+ private request;
130
+ authTest(signal?: AbortSignal): Promise<{
131
+ ok: boolean;
132
+ authMethod: string;
133
+ tokenPreview: string;
134
+ }>;
135
+ listFiles(options?: {
136
+ query?: string;
137
+ pageSize?: number;
138
+ pageToken?: string;
139
+ orderBy?: string;
140
+ spaces?: string;
141
+ corpora?: string;
142
+ driveId?: string;
143
+ includeItemsFromAllDrives?: boolean;
144
+ supportsAllDrives?: boolean;
145
+ signal?: AbortSignal;
146
+ }): Promise<{
147
+ items: DriveFileInfo[];
148
+ nextPageToken: string;
149
+ incompleteSearch: boolean;
150
+ }>;
151
+ getFile(fileId: string, options?: {
152
+ fields?: string;
153
+ supportsAllDrives?: boolean;
154
+ signal?: AbortSignal;
155
+ }): Promise<DriveFileInfo>;
156
+ listPermissions(fileId: string, options?: {
157
+ pageSize?: number;
158
+ pageToken?: string;
159
+ supportsAllDrives?: boolean;
160
+ useDomainAdminAccess?: boolean;
161
+ includePermissionsForView?: string;
162
+ fields?: string;
163
+ signal?: AbortSignal;
164
+ }): Promise<{
165
+ fileId: string;
166
+ items: DrivePermissionInfo[];
167
+ nextPageToken: string;
168
+ }>;
169
+ listRevisions(fileId: string, options?: {
170
+ pageSize?: number;
171
+ pageToken?: string;
172
+ fields?: string;
173
+ signal?: AbortSignal;
174
+ }): Promise<{
175
+ fileId: string;
176
+ items: DriveRevisionInfo[];
177
+ nextPageToken: string;
178
+ }>;
179
+ exportFile(fileId: string, options?: {
180
+ exportMimeType?: string;
181
+ responseEncoding?: 'text' | 'base64';
182
+ supportsAllDrives?: boolean;
183
+ signal?: AbortSignal;
184
+ }): Promise<{
185
+ fileId: string;
186
+ exportMimeType: string;
187
+ encoding: string;
188
+ content: string;
189
+ contentLength: number;
190
+ }>;
191
+ listSharedDrives(options?: {
192
+ pageSize?: number;
193
+ pageToken?: string;
194
+ query?: string;
195
+ useDomainAdminAccess?: boolean;
196
+ signal?: AbortSignal;
197
+ }): Promise<{
198
+ items: SharedDriveInfo[];
199
+ nextPageToken: string;
200
+ incompleteSearch: boolean;
201
+ }>;
202
+ getSharedDrive(driveId: string, options?: {
203
+ useDomainAdminAccess?: boolean;
204
+ signal?: AbortSignal;
205
+ }): Promise<SharedDriveInfo>;
206
+ getDocument(documentId: string, options?: {
207
+ includeTabsContent?: boolean;
208
+ suggestionsViewMode?: string;
209
+ commentsViewMode?: string;
210
+ fields?: string;
211
+ signal?: AbortSignal;
212
+ }): Promise<GoogleDocumentInfo>;
213
+ getSpreadsheet(spreadsheetId: string, options?: {
214
+ ranges?: string[];
215
+ includeGridData?: boolean;
216
+ excludeTablesInBandedRanges?: boolean;
217
+ signal?: AbortSignal;
218
+ }): Promise<GoogleSpreadsheetInfo>;
219
+ getSheetValues(spreadsheetId: string, range: string, options?: {
220
+ valueRenderOption?: string;
221
+ dateTimeRenderOption?: string;
222
+ majorDimension?: string;
223
+ signal?: AbortSignal;
224
+ }): Promise<GoogleSheetValuesInfo>;
225
+ }
@@ -0,0 +1,16 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { GoogleDriveClient } from './client.js';
3
+ export declare const name = "dsh-tool-google-drive";
4
+ export declare const inject: string[];
5
+ export interface GoogleDrivePluginConfig {
6
+ accessToken?: string;
7
+ clientId?: string;
8
+ clientSecret?: string;
9
+ refreshToken?: string;
10
+ baseUrl?: string;
11
+ tokenUrl?: string;
12
+ timeoutMs?: number;
13
+ }
14
+ export type { DrivePermissionInfo, DriveRevisionInfo } from './client.js';
15
+ export declare function apply(ctx: Context, config?: GoogleDrivePluginConfig): void;
16
+ export declare function createTools(client: GoogleDriveClient): import("@deepseek-ai/dsh-tools").ToolDefinition[];
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@libai168/dsh-tool-google-drive",
3
+ "description": "Google Workspace read-only tools for DeepSeek Harness: Drive files, permissions, revisions, Docs, Sheets, and Shared Drives",
4
+ "version": "0.2.0",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "scripts"
18
+ ],
19
+ "license": "MIT",
20
+ "peerDependencies": {
21
+ "@deepseek-ai/cordis": "^4.0.1",
22
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
23
+ },
24
+ "devDependencies": {
25
+ "@deepseek-ai/cordis": "^4.0.1",
26
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
27
+ "@types/node": "^26.2.0",
28
+ "typescript": "^5.5.0",
29
+ "vitest": "^2.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.json",
33
+ "typecheck": "tsc --noEmit -p tsconfig.test.json && tsc --noEmit -p tsconfig.json",
34
+ "test": "vitest run",
35
+ "auth:google": "node scripts/auth-google.mjs"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "engines": {
41
+ "node": ">=22.19"
42
+ },
43
+ "keywords": [
44
+ "deepseek-harness",
45
+ "dsh",
46
+ "dsh-plugin",
47
+ "awesome-dsh-plugin",
48
+ "cordis",
49
+ "google-drive",
50
+ "google",
51
+ "drive",
52
+ "files",
53
+ "search",
54
+ "metadata",
55
+ "agent",
56
+ "plugin",
57
+ "google-docs",
58
+ "google-sheets",
59
+ "shared-drive",
60
+ "export"
61
+ ],
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/LJH-snow/dsh-tool-google-drive.git"
65
+ },
66
+ "homepage": "https://github.com/LJH-snow/dsh-tool-google-drive#readme",
67
+ "bin": {
68
+ "dsh-google-drive-auth": "./scripts/auth-google.mjs"
69
+ }
70
+ }