mcp-google-multi 6.0.1 → 6.1.0-alpha.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/dist/compose.js +13 -1
- package/dist/env-load.d.ts +6 -0
- package/dist/env-load.js +8 -0
- package/dist/executor.js +4 -6
- package/dist/identity.js +4 -2
- package/dist/index.js +2 -1
- package/dist/outbound-allowlist.d.ts +36 -3
- package/dist/outbound-allowlist.js +80 -44
- package/dist/registry.d.ts +46 -0
- package/dist/registry.js +67 -1
- package/dist/tools/_escape-hatch.d.ts +23 -0
- package/dist/tools/_escape-hatch.js +239 -0
- package/dist/tools/_local-files.d.ts +94 -1
- package/dist/tools/_local-files.js +269 -9
- package/dist/tools/drive.js +80 -13
- package/dist/tools/generated/_shared.js +22 -0
- package/dist/tools/generated/admin.js +2 -2
- package/dist/tools/generated/method-map.d.ts +3 -0
- package/dist/tools/generated/method-map.js +156 -1
- package/dist/tools/gmail.d.ts +6 -0
- package/dist/tools/gmail.js +71 -47
- package/dist/tools/google-api.d.ts +18 -1
- package/dist/tools/google-api.js +86 -12
- package/dist/usage-metrics.js +1 -1
- package/dist/write-control.d.ts +31 -7
- package/dist/write-control.js +30 -2
- package/package.json +1 -1
- package/skills/google-workspace/SKILL.md +21 -9
- package/skills/google-workspace/reference/drive.md +4 -4
- package/skills/google-workspace/reference/gmail.md +1 -1
package/dist/tools/drive.js
CHANGED
|
@@ -4,8 +4,8 @@ import { drive as driveClient } from '@googleapis/drive';
|
|
|
4
4
|
import { accountArgLive } from '../accounts.js';
|
|
5
5
|
import { getClient } from '../client.js';
|
|
6
6
|
import { handleGoogleApiError, invalidParams, safeMessage, stringifyEnvelope } from './_errors.js';
|
|
7
|
-
import { hostFilesRefused, openLocalReadStream, prepareLocalDest } from './_local-files.js';
|
|
8
|
-
import { checkOutbound, outboundDeniedEnvelope, resolveOutboundAllowlist } from '../outbound-allowlist.js';
|
|
7
|
+
import { hostFilesRefused, openLocalReadStream, openInternalReadStream, prepareLocalDest, cleanupPartialWrite, localFileErrorResult, LocalFileError } from './_local-files.js';
|
|
8
|
+
import { checkOutbound, checkOutboundGrantee, driveGranteeFromAccessProposal, driveGranteeFromPermission, outboundDeniedEnvelope, resolveOutboundAllowlist } from '../outbound-allowlist.js';
|
|
9
9
|
import { isAllowed, writeDisabledResult } from '../write-control.js';
|
|
10
10
|
import { capText, listResult } from '../trim.js';
|
|
11
11
|
import * as fs from 'fs';
|
|
@@ -351,7 +351,7 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
351
351
|
contentBase64: z.string().optional().describe(`Base64-encoded file content. Exactly one of contentBase64${localFiles ? ' or localPath' : ''} is required. Decoded size is capped at ${MAX_UPLOAD_BASE64_BYTES / (1024 * 1024)} MB.`),
|
|
352
352
|
...(localFiles
|
|
353
353
|
? {
|
|
354
|
-
localPath: z.string().min(1).optional().describe('
|
|
354
|
+
localPath: z.string().min(1).optional().describe('Path of the SOURCE file to upload, on the machine running the server (this is not savePath): confined to GOOGLE_LOCAL_FILES_DIR (default <home>/Downloads/mcp-google-multi); a relative path resolves inside it, an absolute path must already be inside it. Exactly one of contentBase64 or localPath is required.'),
|
|
355
355
|
}
|
|
356
356
|
: {}),
|
|
357
357
|
mimeType: z.string().optional().describe('Source MIME type of the content (inferred from filename if omitted). With `convertTo`, this is the format Drive imports from.'),
|
|
@@ -408,6 +408,8 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
408
408
|
};
|
|
409
409
|
}
|
|
410
410
|
catch (error) {
|
|
411
|
+
if (error instanceof LocalFileError)
|
|
412
|
+
return localFileErrorResult(error, account);
|
|
411
413
|
return handleDriveError(error, account);
|
|
412
414
|
}
|
|
413
415
|
});
|
|
@@ -416,7 +418,7 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
416
418
|
inputSchema: {
|
|
417
419
|
account: accountEnum.describe('Google account alias'),
|
|
418
420
|
fileId: z.string().min(1).describe('Google Drive file ID'),
|
|
419
|
-
savePath: z.string().min(1).describe('
|
|
421
|
+
savePath: z.string().min(1).describe('Destination folder to save into, on the machine running the server: confined to GOOGLE_LOCAL_FILES_DIR (default <home>/Downloads/mcp-google-multi), created if missing; a relative path resolves inside it, an absolute path must already be inside it. The file name comes from `filename`.'),
|
|
420
422
|
filename: z.string().optional().describe('Filename to save as (defaults to the file name in Drive)'),
|
|
421
423
|
},
|
|
422
424
|
}, async ({ account, fileId, savePath, filename }) => {
|
|
@@ -426,16 +428,31 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
426
428
|
const name = filename
|
|
427
429
|
?? (await drive.files.get({ fileId, fields: 'name', supportsAllDrives: true })).data.name
|
|
428
430
|
?? fileId;
|
|
429
|
-
const dest = prepareLocalDest(savePath, name);
|
|
431
|
+
const dest = await prepareLocalDest(savePath, name);
|
|
430
432
|
const res = await drive.files.get({ fileId, alt: 'media', supportsAllDrives: true }, { responseType: 'stream' });
|
|
431
|
-
// pipeline destroys both streams on source/sink error; raw .pipe leaks the
|
|
432
|
-
|
|
433
|
+
// pipeline destroys both streams on source/sink error; raw .pipe leaks the
|
|
434
|
+
// partial file. wx: prepareLocalDest already checked existence, this is
|
|
435
|
+
// the belt-and-suspenders race guard, never silently clobber.
|
|
436
|
+
try {
|
|
437
|
+
await pipeline(res.data, fs.createWriteStream(dest, { mode: 0o600, flags: 'wx' }));
|
|
438
|
+
}
|
|
439
|
+
catch (err) {
|
|
440
|
+
// A failed download must not leave a partial file behind blocking
|
|
441
|
+
// a retry with local_path_exists; best-effort, original error wins.
|
|
442
|
+
// EEXIST means wx itself refused to open: the TOCTOU race lost to
|
|
443
|
+
// another call that already owns dest, so this call never wrote
|
|
444
|
+
// anything there and must not delete that other call's file.
|
|
445
|
+
await cleanupPartialWrite(dest, err);
|
|
446
|
+
throw err;
|
|
447
|
+
}
|
|
433
448
|
const { size } = fs.statSync(dest);
|
|
434
449
|
return {
|
|
435
450
|
content: [{ type: 'text', text: JSON.stringify({ savedPath: dest, bytes: size }, null, 2) }],
|
|
436
451
|
};
|
|
437
452
|
}
|
|
438
453
|
catch (error) {
|
|
454
|
+
if (error instanceof LocalFileError)
|
|
455
|
+
return localFileErrorResult(error, account);
|
|
439
456
|
return handleDriveError(error, account);
|
|
440
457
|
}
|
|
441
458
|
});
|
|
@@ -445,7 +462,7 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
445
462
|
account: accountEnum.describe('Google account alias'),
|
|
446
463
|
fileId: z.string().min(1).describe('Google Drive file ID'),
|
|
447
464
|
mimeType: z.string().describe('Target export MIME type (e.g. "application/pdf", "text/markdown", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")'),
|
|
448
|
-
savePath: z.string().min(1).describe('
|
|
465
|
+
savePath: z.string().min(1).describe('Destination folder to save into, on the machine running the server: confined to GOOGLE_LOCAL_FILES_DIR (default <home>/Downloads/mcp-google-multi), created if missing; a relative path resolves inside it, an absolute path must already be inside it. The file name comes from `filename`.'),
|
|
449
466
|
filename: z.string().optional().describe('Filename to save as (defaults to the Drive name plus the extension implied by mimeType)'),
|
|
450
467
|
},
|
|
451
468
|
}, async ({ account, fileId, mimeType: exportMime, savePath, filename }) => {
|
|
@@ -458,16 +475,31 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
458
475
|
const ext = mime.extension(exportMime);
|
|
459
476
|
name = `${meta.data.name ?? fileId}${ext ? `.${ext}` : ''}`;
|
|
460
477
|
}
|
|
461
|
-
const dest = prepareLocalDest(savePath, name);
|
|
478
|
+
const dest = await prepareLocalDest(savePath, name);
|
|
462
479
|
const res = await drive.files.export({ fileId, mimeType: exportMime }, { responseType: 'stream' });
|
|
463
|
-
// pipeline destroys both streams on source/sink error; raw .pipe leaks the
|
|
464
|
-
|
|
480
|
+
// pipeline destroys both streams on source/sink error; raw .pipe leaks the
|
|
481
|
+
// partial file. wx: prepareLocalDest already checked existence, this is
|
|
482
|
+
// the belt-and-suspenders race guard, never silently clobber.
|
|
483
|
+
try {
|
|
484
|
+
await pipeline(res.data, fs.createWriteStream(dest, { mode: 0o600, flags: 'wx' }));
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
// A failed download must not leave a partial file behind blocking
|
|
488
|
+
// a retry with local_path_exists; best-effort, original error wins.
|
|
489
|
+
// EEXIST means wx itself refused to open: the TOCTOU race lost to
|
|
490
|
+
// another call that already owns dest, so this call never wrote
|
|
491
|
+
// anything there and must not delete that other call's file.
|
|
492
|
+
await cleanupPartialWrite(dest, err);
|
|
493
|
+
throw err;
|
|
494
|
+
}
|
|
465
495
|
const { size } = fs.statSync(dest);
|
|
466
496
|
return {
|
|
467
497
|
content: [{ type: 'text', text: JSON.stringify({ savedPath: dest, bytes: size }, null, 2) }],
|
|
468
498
|
};
|
|
469
499
|
}
|
|
470
500
|
catch (error) {
|
|
501
|
+
if (error instanceof LocalFileError)
|
|
502
|
+
return localFileErrorResult(error, account);
|
|
471
503
|
return handleDriveError(error, account);
|
|
472
504
|
}
|
|
473
505
|
});
|
|
@@ -508,7 +540,7 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
508
540
|
newParentFolderId: z.string().optional().describe('Move to this folder, named newParentFolderId here, not parentFolderId'),
|
|
509
541
|
...(localFiles
|
|
510
542
|
? {
|
|
511
|
-
localPath: z.string().optional().describe('Replace file content with this local file
|
|
543
|
+
localPath: z.string().optional().describe('Replace file content with this local file, on the machine running the server: confined to GOOGLE_LOCAL_FILES_DIR (default <home>/Downloads/mcp-google-multi); a relative path resolves inside it, an absolute path must already be inside it.'),
|
|
512
544
|
mimeType: z.string().optional().describe('MIME type of the replacement file (required if localPath is provided)'),
|
|
513
545
|
convertTo: z.enum(CONVERT_TO_VALUES).optional().describe('When replacing content via localPath, convert the new content into this native Google Workspace type on import: "document" | "spreadsheet" | "presentation" | "drawing" (full application/vnd.google-apps.* ids also accepted).'),
|
|
514
546
|
}
|
|
@@ -551,6 +583,8 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
551
583
|
};
|
|
552
584
|
}
|
|
553
585
|
catch (error) {
|
|
586
|
+
if (error instanceof LocalFileError)
|
|
587
|
+
return localFileErrorResult(error, account);
|
|
554
588
|
return handleDriveError(error, account);
|
|
555
589
|
}
|
|
556
590
|
});
|
|
@@ -802,6 +836,20 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
802
836
|
try {
|
|
803
837
|
const auth = await getClientFn(account);
|
|
804
838
|
const drive = driveClient({ version: 'v3', auth });
|
|
839
|
+
// This call's own body never carries the grantee (only role/
|
|
840
|
+
// expiration): look the real grantee up by permissionId rather than
|
|
841
|
+
// trust anything the caller claims it is.
|
|
842
|
+
if (resolveOutboundAllowlist()) {
|
|
843
|
+
const current = await drive.permissions.get({
|
|
844
|
+
fileId,
|
|
845
|
+
permissionId,
|
|
846
|
+
supportsAllDrives: true,
|
|
847
|
+
fields: 'type,domain,emailAddress',
|
|
848
|
+
});
|
|
849
|
+
const outbound = checkOutboundGrantee('drive_permission_update', driveGranteeFromPermission(current.data), String(account));
|
|
850
|
+
if (outbound)
|
|
851
|
+
return outbound;
|
|
852
|
+
}
|
|
805
853
|
const requestBody = {};
|
|
806
854
|
if (role)
|
|
807
855
|
requestBody.role = role;
|
|
@@ -1131,6 +1179,12 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
1131
1179
|
publishedOutsideDomain: coerceBoolean.optional().describe('Allow publish outside domain'),
|
|
1132
1180
|
},
|
|
1133
1181
|
}, async ({ account, fileId, revisionId, keepForever, published, publishAuto, publishedOutsideDomain }) => {
|
|
1182
|
+
// Publishing makes the revision reachable without an invite (the
|
|
1183
|
+
// allowlist cannot scope that to specific recipients the way a
|
|
1184
|
+
// share/grant can), so it is refused outright while the allowlist is active.
|
|
1185
|
+
if ((published || publishedOutsideDomain) && resolveOutboundAllowlist()) {
|
|
1186
|
+
return outboundDeniedEnvelope('drive_revision_update publish', ['(publishing exposes it beyond any addressable recipient)'], String(account));
|
|
1187
|
+
}
|
|
1134
1188
|
try {
|
|
1135
1189
|
const auth = await getClientFn(account);
|
|
1136
1190
|
const drive = driveClient({ version: 'v3', auth });
|
|
@@ -1220,6 +1274,15 @@ export function registerDriveTools(server, deps = {}) {
|
|
|
1220
1274
|
try {
|
|
1221
1275
|
const auth = await getClientFn(account);
|
|
1222
1276
|
const drive = driveClient({ version: 'v3', auth });
|
|
1277
|
+
// ACCEPT grants access to whoever the proposal names; DENY/other
|
|
1278
|
+
// actions grant nothing and need no check. The real recipient is a
|
|
1279
|
+
// property of the proposal itself, not of this call's own body.
|
|
1280
|
+
if (action === 'ACCEPT' && resolveOutboundAllowlist()) {
|
|
1281
|
+
const proposal = await drive.accessproposals.get({ fileId, proposalId });
|
|
1282
|
+
const outbound = checkOutboundGrantee('drive_access_proposal_resolve', driveGranteeFromAccessProposal(proposal.data), String(account));
|
|
1283
|
+
if (outbound)
|
|
1284
|
+
return outbound;
|
|
1285
|
+
}
|
|
1223
1286
|
const requestBody = { action };
|
|
1224
1287
|
if (role)
|
|
1225
1288
|
requestBody.role = role;
|
|
@@ -1592,7 +1655,11 @@ async function downloadAndUpload(sourceDrive, targetDrive, fileId, sourceMime, p
|
|
|
1592
1655
|
},
|
|
1593
1656
|
media: {
|
|
1594
1657
|
mimeType: plan.kind === 'native' ? plan.exportMime : (sourceMime ?? 'application/octet-stream'),
|
|
1595
|
-
|
|
1658
|
+
// tmp is the server's own staging file, not a caller argument: it
|
|
1659
|
+
// must not go through the GOOGLE_LOCAL_FILES_DIR guard (which would
|
|
1660
|
+
// refuse it outright once that root differs from os.tmpdir(), and
|
|
1661
|
+
// would create the root as an unwanted side effect even then).
|
|
1662
|
+
body: await openInternalReadStream(tmp),
|
|
1596
1663
|
},
|
|
1597
1664
|
supportsAllDrives: true,
|
|
1598
1665
|
fields: 'id,name,mimeType,webViewLink',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { accountAliasSchema, accountAliasSchemaFor, accountArgLive } from '../../accounts.js';
|
|
2
2
|
import { executeApiMethod } from '../../executor.js';
|
|
3
|
+
import { serviceOf } from '../../registry.js';
|
|
4
|
+
import { escapeHatchNames } from '../google-api.js';
|
|
5
|
+
import { escapeHatchGate } from '../_escape-hatch.js';
|
|
3
6
|
export function accountField(aliases) {
|
|
4
7
|
// Generated code passes a getter over registry.accountAliases(), so the field
|
|
5
8
|
// validates against the registry's CURRENT set, as curated tools do. An
|
|
@@ -11,12 +14,17 @@ export function registerGeneratedTool(registry, def, deps = {}) {
|
|
|
11
14
|
// Widened locally: `cud` is a registry extension the SDK config type doesn't
|
|
12
15
|
// carry; the registry reads it before handing the config to the SDK.
|
|
13
16
|
const register = registry.registerTool;
|
|
17
|
+
const service = serviceOf(def.name);
|
|
14
18
|
register(def.name, {
|
|
15
19
|
description: def.description,
|
|
16
20
|
inputSchema: def.shape,
|
|
17
21
|
cud: def.cud,
|
|
18
22
|
annotations: { openWorldHint: true },
|
|
19
23
|
requiredScopes: def.method.scopes,
|
|
24
|
+
// Every generated per-method tool IS the escape hatch (same as
|
|
25
|
+
// google_api_call): write-control for a write runs inside the
|
|
26
|
+
// handler below, not the registry's own name-based gate.
|
|
27
|
+
escapeHatch: true,
|
|
20
28
|
}, async (args) => {
|
|
21
29
|
const pathParams = {};
|
|
22
30
|
const queryParams = {};
|
|
@@ -39,6 +47,20 @@ export function registerGeneratedTool(registry, def, deps = {}) {
|
|
|
39
47
|
}
|
|
40
48
|
body = assembled;
|
|
41
49
|
}
|
|
50
|
+
if (def.cud !== 'read') {
|
|
51
|
+
const names = escapeHatchNames(service, def.method.id);
|
|
52
|
+
const gate = await escapeHatchGate(registry, names.deny, names.primary, { service, cud: def.cud, scopes: def.method.scopes }, registry.policy, {
|
|
53
|
+
account: args.account,
|
|
54
|
+
accountEmail: registry.accountSet().configs[args.account]?.email,
|
|
55
|
+
methodId: def.method.id,
|
|
56
|
+
httpMethod: def.method.httpMethod,
|
|
57
|
+
pathParams,
|
|
58
|
+
queryParams,
|
|
59
|
+
body,
|
|
60
|
+
});
|
|
61
|
+
if (gate)
|
|
62
|
+
return gate;
|
|
63
|
+
}
|
|
42
64
|
return executeApiMethod(def.method, {
|
|
43
65
|
account: args.account,
|
|
44
66
|
pathParams,
|
|
@@ -128,7 +128,7 @@ export function registerAdminGeneratedTools(registry, deps = {}) {
|
|
|
128
128
|
}, deps);
|
|
129
129
|
registerGeneratedTool(registry, {
|
|
130
130
|
name: "admin_customer_devices_chromeos_batch_change_status",
|
|
131
|
-
cud: "
|
|
131
|
+
cud: "delete",
|
|
132
132
|
description: "Changes the status of a batch of ChromeOS devices. For more information about changing a ChromeOS device state [Repair, repurpose, or retire ChromeOS devices](h",
|
|
133
133
|
method: { id: "admin.customer.devices.chromeos.batchChangeStatus", httpMethod: "POST", path: "admin/directory/v1/customer/{customerId}/devices/chromeos:batchChangeStatus", baseUrl: "https://admin.googleapis.com/", requiredParams: ["customerId"], scopes: S_admin_directory_v1[1] },
|
|
134
134
|
params: [{ "field": "customerId", "api": "customerId", "location": "path" }, { "field": "fields", "api": "fields", "location": "query" }],
|
|
@@ -176,7 +176,7 @@ export function registerAdminGeneratedTools(registry, deps = {}) {
|
|
|
176
176
|
}, deps);
|
|
177
177
|
registerGeneratedTool(registry, {
|
|
178
178
|
name: "admin_customer_devices_chromeos_issue_command",
|
|
179
|
-
cud: "
|
|
179
|
+
cud: "delete",
|
|
180
180
|
description: "Issues a command for the device to execute.",
|
|
181
181
|
method: { id: "admin.customer.devices.chromeos.issueCommand", httpMethod: "POST", path: "admin/directory/v1/customer/{customerId}/devices/chromeos/{deviceId}:issueCommand", baseUrl: "https://admin.googleapis.com/", requiredParams: ["customerId", "deviceId"], scopes: S_admin_directory_v1[1] },
|
|
182
182
|
params: [{ "field": "customerId", "api": "customerId", "location": "path" }, { "field": "deviceId", "api": "deviceId", "location": "path" }, { "field": "fields", "api": "fields", "location": "query" }],
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
import type { Cud } from '../../registry.js';
|
|
1
2
|
export declare const GENERATED_METHOD_TOOLS: Record<string, string>;
|
|
2
3
|
export declare const CURATED_METHOD_IDS: readonly string[];
|
|
4
|
+
export declare const CURATED_METHOD_TOOLS: Record<string, string[]>;
|
|
5
|
+
export declare const CUD_OVERRIDES: Record<string, Cud>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// GENERATED by scripts/gen-tools.ts — do not edit. Regenerate: npm run gen:tools
|
|
2
1
|
export const GENERATED_METHOD_TOOLS = {
|
|
3
2
|
"admin.channels.stop": "admin_channels_stop",
|
|
4
3
|
"admin.customer.devices.chromeos.batchChangeStatus": "admin_customer_devices_chromeos_batch_change_status",
|
|
@@ -892,3 +891,159 @@ export const CURATED_METHOD_IDS = [
|
|
|
892
891
|
"webmasters.sites.get",
|
|
893
892
|
"webmasters.sites.list",
|
|
894
893
|
];
|
|
894
|
+
// Every curated tool name registered on each curated method id (see
|
|
895
|
+
// gen-config.ts: derived from the curated registrations themselves).
|
|
896
|
+
export const CURATED_METHOD_TOOLS = {
|
|
897
|
+
"analyticsadmin.accountSummaries.list": ["analytics_account_summaries"],
|
|
898
|
+
"analyticsdata.properties.getMetadata": ["analytics_get_metadata"],
|
|
899
|
+
"analyticsdata.properties.runRealtimeReport": ["analytics_run_realtime_report"],
|
|
900
|
+
"analyticsdata.properties.runReport": ["analytics_run_report"],
|
|
901
|
+
"calendar.calendarList.list": ["calendar_list_calendars"],
|
|
902
|
+
"calendar.calendars.insert": ["calendar_create_calendar"],
|
|
903
|
+
"calendar.events.delete": ["calendar_delete_event"],
|
|
904
|
+
"calendar.events.get": ["calendar_get_event", "calendar_respond_event", "calendar_update_event"],
|
|
905
|
+
"calendar.events.insert": ["calendar_create_event"],
|
|
906
|
+
"calendar.events.instances": ["calendar_list_instances"],
|
|
907
|
+
"calendar.events.list": ["calendar_list_events"],
|
|
908
|
+
"calendar.events.move": ["calendar_move_event"],
|
|
909
|
+
"calendar.events.patch": ["calendar_respond_event", "calendar_update_event"],
|
|
910
|
+
"calendar.events.quickAdd": ["calendar_quick_add"],
|
|
911
|
+
"calendar.freebusy.query": ["calendar_get_freebusy"],
|
|
912
|
+
"chat.spaces.get": ["chat_spaces_get"],
|
|
913
|
+
"chat.spaces.list": ["chat_spaces_list"],
|
|
914
|
+
"chat.spaces.messages.create": ["chat_messages_create"],
|
|
915
|
+
"chat.spaces.messages.list": ["chat_messages_list"],
|
|
916
|
+
"directory.groups.list": ["admin_groups_list"],
|
|
917
|
+
"directory.members.list": ["admin_group_members_list"],
|
|
918
|
+
"directory.users.get": ["admin_users_get"],
|
|
919
|
+
"directory.users.list": ["admin_users_list"],
|
|
920
|
+
"directory.users.patch": ["admin_users_update"],
|
|
921
|
+
"docs.documents.batchUpdate": ["docs_add_tab", "docs_batch_update", "docs_create_footer", "docs_create_header", "docs_create_named_range", "docs_create_paragraph_bullets", "docs_delete_footer", "docs_delete_header", "docs_delete_named_range", "docs_delete_paragraph_bullets", "docs_delete_range", "docs_delete_tab", "docs_insert_inline_image", "docs_insert_page_break", "docs_insert_section_break", "docs_insert_table", "docs_insert_text", "docs_modify_table", "docs_replace_named_range_content", "docs_replace_text", "docs_update_document_style", "docs_update_paragraph_style", "docs_update_style", "docs_update_tab_properties"],
|
|
922
|
+
"docs.documents.create": ["docs_create"],
|
|
923
|
+
"docs.documents.get": ["docs_get", "docs_read"],
|
|
924
|
+
"drive.about.get": ["drive_get_about"],
|
|
925
|
+
"drive.accessproposals.list": ["drive_access_proposal_list"],
|
|
926
|
+
"drive.accessproposals.resolve": ["drive_access_proposal_resolve"],
|
|
927
|
+
"drive.comments.create": ["drive_comment_create"],
|
|
928
|
+
"drive.comments.delete": ["drive_comment_delete"],
|
|
929
|
+
"drive.comments.get": ["drive_comment_get"],
|
|
930
|
+
"drive.comments.list": ["drive_comment_list"],
|
|
931
|
+
"drive.comments.update": ["drive_comment_update"],
|
|
932
|
+
"drive.drives.get": ["drive_shared_drive_get"],
|
|
933
|
+
"drive.drives.list": ["drive_shared_drives_list"],
|
|
934
|
+
"drive.files.copy": ["drive_copy", "drive_transfer"],
|
|
935
|
+
"drive.files.create": ["drive_create_folder", "drive_transfer", "drive_upload"],
|
|
936
|
+
"drive.files.delete": ["drive_delete"],
|
|
937
|
+
"drive.files.emptyTrash": ["drive_empty_trash"],
|
|
938
|
+
"drive.files.export": ["drive_export", "drive_read", "drive_transfer"],
|
|
939
|
+
"drive.files.get": ["drive_download", "drive_export", "drive_move", "drive_read", "drive_transfer", "drive_update"],
|
|
940
|
+
"drive.files.list": ["drive_list", "drive_search"],
|
|
941
|
+
"drive.files.update": ["drive_move", "drive_transfer", "drive_trash", "drive_untrash", "drive_update"],
|
|
942
|
+
"drive.permissions.create": ["drive_share", "drive_transfer"],
|
|
943
|
+
"drive.permissions.delete": ["drive_remove_permission", "drive_transfer"],
|
|
944
|
+
"drive.permissions.list": ["drive_list_permissions"],
|
|
945
|
+
"drive.permissions.update": ["drive_permission_update"],
|
|
946
|
+
"drive.replies.create": ["drive_reply_create"],
|
|
947
|
+
"drive.replies.delete": ["drive_reply_delete"],
|
|
948
|
+
"drive.replies.list": ["drive_reply_list"],
|
|
949
|
+
"drive.replies.update": ["drive_reply_update"],
|
|
950
|
+
"drive.revisions.delete": ["drive_revision_delete"],
|
|
951
|
+
"drive.revisions.list": ["drive_revision_list"],
|
|
952
|
+
"drive.revisions.update": ["drive_revision_update"],
|
|
953
|
+
"forms.forms.batchUpdate": ["forms_batch_update"],
|
|
954
|
+
"forms.forms.create": ["forms_create"],
|
|
955
|
+
"forms.forms.get": ["forms_get"],
|
|
956
|
+
"forms.forms.responses.get": ["forms_response_get"],
|
|
957
|
+
"forms.forms.responses.list": ["forms_responses_list"],
|
|
958
|
+
"forms.forms.setPublishSettings": ["forms_set_publish_settings"],
|
|
959
|
+
"forms.forms.watches.list": ["forms_watches_list"],
|
|
960
|
+
"gmail.users.drafts.create": ["gmail_create_draft"],
|
|
961
|
+
"gmail.users.drafts.delete": ["gmail_delete_draft"],
|
|
962
|
+
"gmail.users.drafts.get": ["gmail_get_draft", "gmail_send_draft"],
|
|
963
|
+
"gmail.users.drafts.list": ["gmail_list_drafts"],
|
|
964
|
+
"gmail.users.drafts.send": ["gmail_send_draft"],
|
|
965
|
+
"gmail.users.getProfile": ["gmail_get_profile"],
|
|
966
|
+
"gmail.users.history.list": ["gmail_list_history"],
|
|
967
|
+
"gmail.users.labels.create": ["gmail_create_label"],
|
|
968
|
+
"gmail.users.labels.delete": ["gmail_delete_label"],
|
|
969
|
+
"gmail.users.labels.list": ["gmail_list_labels"],
|
|
970
|
+
"gmail.users.messages.attachments.get": ["gmail_download_attachment"],
|
|
971
|
+
"gmail.users.messages.batchDelete": ["gmail_batch_delete"],
|
|
972
|
+
"gmail.users.messages.batchModify": ["gmail_batch_modify"],
|
|
973
|
+
"gmail.users.messages.delete": ["gmail_delete"],
|
|
974
|
+
"gmail.users.messages.get": ["gmail_create_draft", "gmail_read", "gmail_read_batch", "gmail_search", "gmail_send"],
|
|
975
|
+
"gmail.users.messages.list": ["gmail_search"],
|
|
976
|
+
"gmail.users.messages.modify": ["gmail_modify_labels"],
|
|
977
|
+
"gmail.users.messages.send": ["gmail_send"],
|
|
978
|
+
"gmail.users.messages.trash": ["gmail_trash"],
|
|
979
|
+
"gmail.users.settings.getVacation": ["gmail_get_vacation"],
|
|
980
|
+
"gmail.users.settings.updateVacation": ["gmail_set_vacation"],
|
|
981
|
+
"gmail.users.threads.get": ["gmail_read_thread"],
|
|
982
|
+
"meet.conferenceRecords.get": ["meet_conference_record_get"],
|
|
983
|
+
"meet.conferenceRecords.list": ["meet_conference_records_list"],
|
|
984
|
+
"meet.conferenceRecords.recordings.list": ["meet_recordings_list"],
|
|
985
|
+
"meet.conferenceRecords.transcripts.entries.list": ["meet_transcript_entries_list"],
|
|
986
|
+
"meet.conferenceRecords.transcripts.list": ["meet_transcripts_list"],
|
|
987
|
+
"people.contactGroups.create": ["contacts_group_create"],
|
|
988
|
+
"people.contactGroups.get": ["contacts_group_members"],
|
|
989
|
+
"people.contactGroups.list": ["contacts_groups_list"],
|
|
990
|
+
"people.people.connections.list": ["contacts_list"],
|
|
991
|
+
"people.people.createContact": ["contacts_create"],
|
|
992
|
+
"people.people.deleteContact": ["contacts_delete"],
|
|
993
|
+
"people.people.get": ["contacts_get", "contacts_update"],
|
|
994
|
+
"people.people.getBatchGet": ["contacts_group_members"],
|
|
995
|
+
"people.people.searchContacts": ["contacts_resolve", "contacts_search"],
|
|
996
|
+
"people.people.updateContact": ["contacts_update"],
|
|
997
|
+
"reports.activities.list": ["reports_activities_list"],
|
|
998
|
+
"searchconsole.urlInspection.index.inspect": ["searchconsole_url_inspect"],
|
|
999
|
+
"sheets.spreadsheets.batchUpdate": ["sheets_add_conditional_format_rule", "sheets_add_named_range", "sheets_add_sheet", "sheets_auto_resize_dimensions", "sheets_batch_update", "sheets_clear_basic_filter", "sheets_delete_dimension", "sheets_delete_named_range", "sheets_delete_sheet", "sheets_duplicate_sheet", "sheets_find_replace", "sheets_format_cells", "sheets_insert_dimension", "sheets_merge_cells", "sheets_set_basic_filter", "sheets_set_data_validation", "sheets_sort_range", "sheets_unmerge_cells", "sheets_update_borders", "sheets_update_sheet_properties"],
|
|
1000
|
+
"sheets.spreadsheets.create": ["sheets_create"],
|
|
1001
|
+
"sheets.spreadsheets.get": ["sheets_format_cells", "sheets_get"],
|
|
1002
|
+
"sheets.spreadsheets.values.append": ["sheets_append_rows"],
|
|
1003
|
+
"sheets.spreadsheets.values.batchClear": ["sheets_batch_clear"],
|
|
1004
|
+
"sheets.spreadsheets.values.batchGet": ["sheets_batch_read"],
|
|
1005
|
+
"sheets.spreadsheets.values.batchUpdate": ["sheets_batch_write"],
|
|
1006
|
+
"sheets.spreadsheets.values.clear": ["sheets_clear_range"],
|
|
1007
|
+
"sheets.spreadsheets.values.get": ["sheets_read_range"],
|
|
1008
|
+
"sheets.spreadsheets.values.update": ["sheets_write_range"],
|
|
1009
|
+
"slides.presentations.batchUpdate": ["slides_batch_update"],
|
|
1010
|
+
"slides.presentations.create": ["slides_create"],
|
|
1011
|
+
"slides.presentations.get": ["slides_get"],
|
|
1012
|
+
"slides.presentations.pages.get": ["slides_page_get"],
|
|
1013
|
+
"slides.presentations.pages.getThumbnail": ["slides_page_thumbnail"],
|
|
1014
|
+
"tasks.tasklists.delete": ["tasks_list_delete"],
|
|
1015
|
+
"tasks.tasklists.get": ["tasks_list_get"],
|
|
1016
|
+
"tasks.tasklists.insert": ["tasks_list_insert"],
|
|
1017
|
+
"tasks.tasklists.list": ["tasks_lists_list"],
|
|
1018
|
+
"tasks.tasklists.patch": ["tasks_list_update"],
|
|
1019
|
+
"tasks.tasks.clear": ["tasks_clear"],
|
|
1020
|
+
"tasks.tasks.delete": ["tasks_delete"],
|
|
1021
|
+
"tasks.tasks.get": ["tasks_get"],
|
|
1022
|
+
"tasks.tasks.insert": ["tasks_insert"],
|
|
1023
|
+
"tasks.tasks.list": ["tasks_list"],
|
|
1024
|
+
"tasks.tasks.move": ["tasks_move"],
|
|
1025
|
+
"tasks.tasks.patch": ["tasks_update"],
|
|
1026
|
+
"webmasters.searchanalytics.query": ["searchconsole_searchanalytics_query"],
|
|
1027
|
+
"webmasters.sitemaps.delete": ["searchconsole_sitemaps_delete"],
|
|
1028
|
+
"webmasters.sitemaps.get": ["searchconsole_sitemaps_get"],
|
|
1029
|
+
"webmasters.sitemaps.list": ["searchconsole_sitemaps_list"],
|
|
1030
|
+
"webmasters.sitemaps.submit": ["searchconsole_sitemaps_submit"],
|
|
1031
|
+
"webmasters.sites.add": ["searchconsole_sites_add"],
|
|
1032
|
+
"webmasters.sites.delete": ["searchconsole_sites_delete"],
|
|
1033
|
+
"webmasters.sites.get": ["searchconsole_sites_get"],
|
|
1034
|
+
"webmasters.sites.list": ["searchconsole_sites_list"],
|
|
1035
|
+
};
|
|
1036
|
+
// A runtime copy of scripts/gen-config.ts's own CUD_OVERRIDES (scripts/ and
|
|
1037
|
+
// src/ are separate compilation roots, so src/ code cannot import the build
|
|
1038
|
+
// script directly). google-api.ts applies this the same way gen-tools.ts
|
|
1039
|
+
// does when generating a method's twin (CUD_OVERRIDES[id] ?? cudFromMethod),
|
|
1040
|
+
// so the escape hatch and a method's generated twin always agree on its
|
|
1041
|
+
// write class, including for the methods HTTP-verb inference misreads.
|
|
1042
|
+
export const CUD_OVERRIDES = {
|
|
1043
|
+
"admin.customer.devices.chromeos.batchChangeStatus": "delete",
|
|
1044
|
+
"admin.customer.devices.chromeos.issueCommand": "delete",
|
|
1045
|
+
"analyticsadmin.properties.acknowledgeUserDataCollection": "update",
|
|
1046
|
+
"directory.chromeosdevices.action": "delete",
|
|
1047
|
+
"directory.mobiledevices.action": "delete",
|
|
1048
|
+
"searchconsole.urlTestingTools.mobileFriendlyTest.run": "read",
|
|
1049
|
+
};
|
package/dist/tools/gmail.d.ts
CHANGED
|
@@ -18,6 +18,12 @@ interface ReplyDerivation {
|
|
|
18
18
|
* to/cc/subject. On any fetch failure, degrade to the API id for threading and
|
|
19
19
|
* report sourceFound:false so the caller can decide. */
|
|
20
20
|
export declare function resolveReply(gmail: any, account: string, primaryEmail: string, replyToMessageId: string, replyAll: boolean): Promise<ReplyDerivation>;
|
|
21
|
+
/** Reads attachment files into buffers (THIS server reads them, never
|
|
22
|
+
* MailComposer), enforces absolute-path + total-size guards, and derives the
|
|
23
|
+
* MIME filename via basename so a caller name can't inject path separators.
|
|
24
|
+
* The absolute-path + config/token-dir confinement rule itself lives in
|
|
25
|
+
* assertLocalPathAllowed (src/tools/_local-files.ts), shared with Drive's
|
|
26
|
+
* upload/update/download/export so the two surfaces cannot drift apart. */
|
|
21
27
|
export declare function readAttachments(raw: Array<{
|
|
22
28
|
path: string;
|
|
23
29
|
filename?: string;
|