gogcli-mcp-drive 2.2.0 → 2.4.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +239 -1
- package/manifest.json +49 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/drive-extra.ts +252 -0
- package/tests/tools/drive-extra.test.ts +312 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.4.0"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "gogcli (Drive)",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
18
|
-
"version": "2.
|
|
18
|
+
"version": "2.4.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-drive",
|
|
3
3
|
"displayName": "gogcli (Drive)",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.4.0",
|
|
5
5
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/index.js
CHANGED
|
@@ -31351,7 +31351,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
31351
31351
|
);
|
|
31352
31352
|
|
|
31353
31353
|
// ../gogcli-mcp/src/server.ts
|
|
31354
|
-
var VERSION = true ? "2.
|
|
31354
|
+
var VERSION = true ? "2.4.0" : "0.0.0";
|
|
31355
31355
|
function createServer(options) {
|
|
31356
31356
|
return new McpServer({
|
|
31357
31357
|
name: options?.name ?? "gogcli",
|
|
@@ -31558,6 +31558,244 @@ function registerExtraDriveTools(server2) {
|
|
|
31558
31558
|
}, async ({ fileId, commentId, account }) => {
|
|
31559
31559
|
return runOrDiagnose(["drive", "comments", "reopen", fileId, commentId], { account });
|
|
31560
31560
|
});
|
|
31561
|
+
server2.registerTool("gog_drive_du", {
|
|
31562
|
+
description: "Summarize Drive folder sizes (disk-usage style) starting from a folder.",
|
|
31563
|
+
annotations: { readOnlyHint: true },
|
|
31564
|
+
inputSchema: {
|
|
31565
|
+
parent: external_exports.string().optional().describe("Folder ID to start from (default: root)"),
|
|
31566
|
+
depth: external_exports.number().optional().describe("Depth for folder totals (default: 1)"),
|
|
31567
|
+
max: external_exports.number().optional().describe("Max folders to return (0 = unlimited; default: 50)"),
|
|
31568
|
+
sort: external_exports.enum(["size", "path", "files"]).optional().describe("Sort key (default: size)"),
|
|
31569
|
+
order: external_exports.enum(["asc", "desc"]).optional().describe("Sort order (default: desc)"),
|
|
31570
|
+
noAllDrives: external_exports.boolean().optional().describe("My Drive only \u2014 exclude shared drives (shared drives are included by default)"),
|
|
31571
|
+
account: accountParam
|
|
31572
|
+
}
|
|
31573
|
+
}, async ({ parent, depth, max, sort, order, noAllDrives, account }) => {
|
|
31574
|
+
const args = ["drive", "du"];
|
|
31575
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
31576
|
+
if (depth !== void 0) args.push(`--depth=${depth}`);
|
|
31577
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31578
|
+
if (sort) args.push(`--sort=${sort}`);
|
|
31579
|
+
if (order) args.push(`--order=${order}`);
|
|
31580
|
+
if (noAllDrives) args.push("--no-all-drives");
|
|
31581
|
+
return runOrDiagnose(args, { account });
|
|
31582
|
+
});
|
|
31583
|
+
server2.registerTool("gog_drive_tree", {
|
|
31584
|
+
description: "Print a read-only folder tree starting from a folder.",
|
|
31585
|
+
annotations: { readOnlyHint: true },
|
|
31586
|
+
inputSchema: {
|
|
31587
|
+
parent: external_exports.string().optional().describe("Folder ID to start from (default: root)"),
|
|
31588
|
+
depth: external_exports.number().optional().describe("Max depth (0 = unlimited; default: 2)"),
|
|
31589
|
+
max: external_exports.number().optional().describe("Max items to return (0 = unlimited; default: 0)"),
|
|
31590
|
+
noAllDrives: external_exports.boolean().optional().describe("My Drive only \u2014 exclude shared drives (shared drives are included by default)"),
|
|
31591
|
+
account: accountParam
|
|
31592
|
+
}
|
|
31593
|
+
}, async ({ parent, depth, max, noAllDrives, account }) => {
|
|
31594
|
+
const args = ["drive", "tree"];
|
|
31595
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
31596
|
+
if (depth !== void 0) args.push(`--depth=${depth}`);
|
|
31597
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31598
|
+
if (noAllDrives) args.push("--no-all-drives");
|
|
31599
|
+
return runOrDiagnose(args, { account });
|
|
31600
|
+
});
|
|
31601
|
+
server2.registerTool("gog_drive_changes_start_token", {
|
|
31602
|
+
description: "Get a Drive changes start page token \u2014 the cursor you pass to gog_drive_changes_list to enumerate changes since this point.",
|
|
31603
|
+
annotations: { readOnlyHint: true },
|
|
31604
|
+
inputSchema: {
|
|
31605
|
+
drive: external_exports.string().optional().describe("Shared drive ID for a shared-drive change log"),
|
|
31606
|
+
account: accountParam
|
|
31607
|
+
}
|
|
31608
|
+
}, async ({ drive, account }) => {
|
|
31609
|
+
const args = ["drive", "changes", "start-token"];
|
|
31610
|
+
if (drive) args.push(`--drive=${drive}`);
|
|
31611
|
+
return runOrDiagnose(args, { account });
|
|
31612
|
+
});
|
|
31613
|
+
server2.registerTool("gog_drive_changes_list", {
|
|
31614
|
+
description: "List Drive changes since a page token (for sync/automation). Get the initial token from gog_drive_changes_start_token; the response includes a newStartPageToken to persist for the next poll.",
|
|
31615
|
+
annotations: { readOnlyHint: true },
|
|
31616
|
+
inputSchema: {
|
|
31617
|
+
token: external_exports.string().describe("Start page token or next page token"),
|
|
31618
|
+
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
31619
|
+
page: external_exports.string().optional().describe("Alias for token when continuing a page"),
|
|
31620
|
+
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
31621
|
+
includeRemoved: external_exports.boolean().optional().describe("Include removed changes"),
|
|
31622
|
+
drive: external_exports.string().optional().describe("Shared drive ID for a shared-drive change log"),
|
|
31623
|
+
account: accountParam
|
|
31624
|
+
}
|
|
31625
|
+
}, async ({ token, max, page, all, includeRemoved, drive, account }) => {
|
|
31626
|
+
const args = ["drive", "changes", "list", `--token=${token}`];
|
|
31627
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31628
|
+
if (page) args.push(`--page=${page}`);
|
|
31629
|
+
if (all) args.push("--all");
|
|
31630
|
+
if (includeRemoved) args.push("--include-removed");
|
|
31631
|
+
if (drive) args.push(`--drive=${drive}`);
|
|
31632
|
+
return runOrDiagnose(args, { account });
|
|
31633
|
+
});
|
|
31634
|
+
server2.registerTool("gog_drive_labels_list", {
|
|
31635
|
+
description: "List Drive label schemas available to the account.",
|
|
31636
|
+
annotations: { readOnlyHint: true },
|
|
31637
|
+
inputSchema: {
|
|
31638
|
+
language: external_exports.string().optional().describe("BCP-47 language code"),
|
|
31639
|
+
view: external_exports.enum(["LABEL_VIEW_BASIC", "LABEL_VIEW_FULL"]).optional().describe("Label view (default: LABEL_VIEW_BASIC)"),
|
|
31640
|
+
minimumRole: external_exports.string().optional().describe("Minimum role filter (e.g. READER, APPLIER, ORGANIZER)"),
|
|
31641
|
+
publishedOnly: external_exports.boolean().optional().describe("Only list published labels"),
|
|
31642
|
+
adminAccess: external_exports.boolean().optional().describe("Use admin access for Workspace admin accounts"),
|
|
31643
|
+
account: accountParam
|
|
31644
|
+
}
|
|
31645
|
+
}, async ({ language, view, minimumRole, publishedOnly, adminAccess, account }) => {
|
|
31646
|
+
const args = ["drive", "labels", "list"];
|
|
31647
|
+
if (language) args.push(`--language=${language}`);
|
|
31648
|
+
if (view) args.push(`--view=${view}`);
|
|
31649
|
+
if (minimumRole) args.push(`--minimum-role=${minimumRole}`);
|
|
31650
|
+
if (publishedOnly) args.push("--published-only");
|
|
31651
|
+
if (adminAccess) args.push("--admin-access");
|
|
31652
|
+
return runOrDiagnose(args, { account });
|
|
31653
|
+
});
|
|
31654
|
+
server2.registerTool("gog_drive_labels_get", {
|
|
31655
|
+
description: 'Get a single Drive label schema by name (e.g. "labels/abc123").',
|
|
31656
|
+
annotations: { readOnlyHint: true },
|
|
31657
|
+
inputSchema: {
|
|
31658
|
+
name: external_exports.string().describe("Label schema name (e.g. labels/abc123)"),
|
|
31659
|
+
language: external_exports.string().optional().describe("BCP-47 language code"),
|
|
31660
|
+
view: external_exports.enum(["LABEL_VIEW_BASIC", "LABEL_VIEW_FULL"]).optional().describe("Label view (default: LABEL_VIEW_FULL)"),
|
|
31661
|
+
adminAccess: external_exports.boolean().optional().describe("Use admin access for Workspace admin accounts"),
|
|
31662
|
+
account: accountParam
|
|
31663
|
+
}
|
|
31664
|
+
}, async ({ name, language, view, adminAccess, account }) => {
|
|
31665
|
+
const args = ["drive", "labels", "get", name];
|
|
31666
|
+
if (language) args.push(`--language=${language}`);
|
|
31667
|
+
if (view) args.push(`--view=${view}`);
|
|
31668
|
+
if (adminAccess) args.push("--admin-access");
|
|
31669
|
+
return runOrDiagnose(args, { account });
|
|
31670
|
+
});
|
|
31671
|
+
server2.registerTool("gog_drive_labels_file_list", {
|
|
31672
|
+
description: "List labels applied to a Drive file.",
|
|
31673
|
+
annotations: { readOnlyHint: true },
|
|
31674
|
+
inputSchema: {
|
|
31675
|
+
fileId: external_exports.string().describe("File ID"),
|
|
31676
|
+
max: external_exports.number().optional().describe("Max results (default: 100)"),
|
|
31677
|
+
page: external_exports.string().optional().describe("Page token"),
|
|
31678
|
+
account: accountParam
|
|
31679
|
+
}
|
|
31680
|
+
}, async ({ fileId, max, page, account }) => {
|
|
31681
|
+
const args = ["drive", "labels", "file", "list", fileId];
|
|
31682
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31683
|
+
if (page) args.push(`--page=${page}`);
|
|
31684
|
+
return runOrDiagnose(args, { account });
|
|
31685
|
+
});
|
|
31686
|
+
server2.registerTool("gog_drive_labels_file_apply", {
|
|
31687
|
+
description: 'Apply or update a label on a Drive file, optionally setting field values. Each field flag takes "fieldId=value" entries (repeatable). selection/integer/date/user values may be comma-separated within one entry for multi-valued fields.',
|
|
31688
|
+
inputSchema: {
|
|
31689
|
+
fileId: external_exports.string().describe("File ID"),
|
|
31690
|
+
labelId: external_exports.string().describe("Label ID to apply"),
|
|
31691
|
+
text: external_exports.array(external_exports.string()).optional().describe("Text fields as fieldId=value (repeatable)"),
|
|
31692
|
+
selection: external_exports.array(external_exports.string()).optional().describe("Selection fields as fieldId=choiceId[,choiceId] (repeatable)"),
|
|
31693
|
+
integer: external_exports.array(external_exports.string()).optional().describe("Integer fields as fieldId=123[,456] (repeatable)"),
|
|
31694
|
+
date: external_exports.array(external_exports.string()).optional().describe("Date fields as fieldId=YYYY-MM-DD[,YYYY-MM-DD] (repeatable)"),
|
|
31695
|
+
user: external_exports.array(external_exports.string()).optional().describe("User fields as fieldId=email[,email] (repeatable)"),
|
|
31696
|
+
unset: external_exports.array(external_exports.string()).optional().describe("Field IDs to unset (repeatable)"),
|
|
31697
|
+
fieldsJson: external_exports.string().optional().describe("Simple JSON object of fieldId to string/number/bool/string-array values"),
|
|
31698
|
+
account: accountParam
|
|
31699
|
+
}
|
|
31700
|
+
}, async ({ fileId, labelId, text, selection, integer: integer2, date: date5, user, unset, fieldsJson, account }) => {
|
|
31701
|
+
const args = ["drive", "labels", "file", "apply", fileId, labelId];
|
|
31702
|
+
if (text) for (const t of text) args.push(`--text=${t}`);
|
|
31703
|
+
if (selection) for (const s of selection) args.push(`--selection=${s}`);
|
|
31704
|
+
if (integer2) for (const i of integer2) args.push(`--integer=${i}`);
|
|
31705
|
+
if (date5) for (const d of date5) args.push(`--date=${d}`);
|
|
31706
|
+
if (user) for (const u of user) args.push(`--user=${u}`);
|
|
31707
|
+
if (unset) for (const u of unset) args.push(`--unset=${u}`);
|
|
31708
|
+
if (fieldsJson) args.push(`--fields-json=${fieldsJson}`);
|
|
31709
|
+
return runOrDiagnose(args, { account });
|
|
31710
|
+
});
|
|
31711
|
+
server2.registerTool("gog_drive_labels_file_remove", {
|
|
31712
|
+
description: "Remove a label from a Drive file.",
|
|
31713
|
+
annotations: { destructiveHint: true },
|
|
31714
|
+
inputSchema: {
|
|
31715
|
+
fileId: external_exports.string().describe("File ID"),
|
|
31716
|
+
labelId: external_exports.string().describe("Label ID to remove"),
|
|
31717
|
+
account: accountParam
|
|
31718
|
+
}
|
|
31719
|
+
}, async ({ fileId, labelId, account }) => {
|
|
31720
|
+
return runOrDiagnose(["drive", "labels", "file", "remove", fileId, labelId], { account });
|
|
31721
|
+
});
|
|
31722
|
+
server2.registerTool("gog_drive_activity", {
|
|
31723
|
+
description: "Query the Drive Activity API for audit events (edits, creates, deletes, moves, shares, etc.) scoped to a file or folder and/or time range.",
|
|
31724
|
+
annotations: { readOnlyHint: true },
|
|
31725
|
+
inputSchema: {
|
|
31726
|
+
file: external_exports.string().optional().describe("Drive file ID to query"),
|
|
31727
|
+
folder: external_exports.string().optional().describe("Drive folder ID; includes descendants"),
|
|
31728
|
+
actions: external_exports.string().optional().describe("Comma-separated action filters: edit,create,delete,move,rename,restore,comment,share,label,dlp,reference,settings"),
|
|
31729
|
+
from: external_exports.string().optional().describe("Lower activity time bound (RFC3339)"),
|
|
31730
|
+
to: external_exports.string().optional().describe("Upper activity time bound (RFC3339)"),
|
|
31731
|
+
filter: external_exports.string().optional().describe("Raw Drive Activity filter expression appended with AND"),
|
|
31732
|
+
max: external_exports.number().optional().describe("Page size (default: 10)"),
|
|
31733
|
+
page: external_exports.string().optional().describe("Page token"),
|
|
31734
|
+
all: external_exports.boolean().optional().describe("Fetch all pages"),
|
|
31735
|
+
consolidate: external_exports.boolean().optional().describe("Use Drive Activity legacy consolidation strategy"),
|
|
31736
|
+
account: accountParam
|
|
31737
|
+
}
|
|
31738
|
+
}, async ({ file: file2, folder, actions, from, to, filter, max, page, all, consolidate, account }) => {
|
|
31739
|
+
const args = ["drive", "activity", "query"];
|
|
31740
|
+
if (file2) args.push(`--file=${file2}`);
|
|
31741
|
+
if (folder) args.push(`--folder=${folder}`);
|
|
31742
|
+
if (actions) args.push(`--actions=${actions}`);
|
|
31743
|
+
if (from) args.push(`--from=${from}`);
|
|
31744
|
+
if (to) args.push(`--to=${to}`);
|
|
31745
|
+
if (filter) args.push(`--filter=${filter}`);
|
|
31746
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31747
|
+
if (page) args.push(`--page=${page}`);
|
|
31748
|
+
if (all) args.push("--all");
|
|
31749
|
+
if (consolidate) args.push("--consolidate");
|
|
31750
|
+
return runOrDiagnose(args, { account });
|
|
31751
|
+
});
|
|
31752
|
+
server2.registerTool("gog_drive_audit_sharing", {
|
|
31753
|
+
description: "Audit Drive sharing without mutation \u2014 find public (anyone-with-link) or external permissions across a folder tree or a single file.",
|
|
31754
|
+
annotations: { readOnlyHint: true },
|
|
31755
|
+
inputSchema: {
|
|
31756
|
+
file: external_exports.string().optional().describe("Audit one file ID instead of a folder tree"),
|
|
31757
|
+
parent: external_exports.string().optional().describe("Folder ID to scan (default: root)"),
|
|
31758
|
+
depth: external_exports.number().optional().describe("Max folder depth (0 = unlimited; default: 2)"),
|
|
31759
|
+
max: external_exports.number().optional().describe("Max files/folders to scan (0 = unlimited; default: 500)"),
|
|
31760
|
+
internalDomain: external_exports.array(external_exports.string()).optional().describe("Domains treated as internal (defaults to account email domain)"),
|
|
31761
|
+
publicOnly: external_exports.boolean().optional().describe("Only report anyone-with-link/public permissions"),
|
|
31762
|
+
externalOnly: external_exports.boolean().optional().describe("Only report external user/group/domain permissions"),
|
|
31763
|
+
noAllDrives: external_exports.boolean().optional().describe("My Drive only \u2014 exclude shared drives (shared drives are included by default)"),
|
|
31764
|
+
account: accountParam
|
|
31765
|
+
}
|
|
31766
|
+
}, async ({ file: file2, parent, depth, max, internalDomain, publicOnly, externalOnly, noAllDrives, account }) => {
|
|
31767
|
+
const args = ["drive", "audit", "sharing"];
|
|
31768
|
+
if (file2) args.push(`--file=${file2}`);
|
|
31769
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
31770
|
+
if (depth !== void 0) args.push(`--depth=${depth}`);
|
|
31771
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31772
|
+
if (internalDomain) for (const d of internalDomain) args.push(`--internal-domain=${d}`);
|
|
31773
|
+
if (publicOnly) args.push("--public-only");
|
|
31774
|
+
if (externalOnly) args.push("--external-only");
|
|
31775
|
+
if (noAllDrives) args.push("--no-all-drives");
|
|
31776
|
+
return runOrDiagnose(args, { account });
|
|
31777
|
+
});
|
|
31778
|
+
server2.registerTool("gog_drive_audit_user", {
|
|
31779
|
+
description: "Audit Drive sharing without mutation \u2014 find permissions granted to a specific user across a folder tree or a single file.",
|
|
31780
|
+
annotations: { readOnlyHint: true },
|
|
31781
|
+
inputSchema: {
|
|
31782
|
+
user: external_exports.string().describe("User email to audit permissions for"),
|
|
31783
|
+
file: external_exports.string().optional().describe("Audit one file ID instead of a folder tree"),
|
|
31784
|
+
parent: external_exports.string().optional().describe("Folder ID to scan (default: root)"),
|
|
31785
|
+
depth: external_exports.number().optional().describe("Max folder depth (0 = unlimited; default: 2)"),
|
|
31786
|
+
max: external_exports.number().optional().describe("Max files/folders to scan (0 = unlimited; default: 500)"),
|
|
31787
|
+
noAllDrives: external_exports.boolean().optional().describe("My Drive only \u2014 exclude shared drives (shared drives are included by default)"),
|
|
31788
|
+
account: accountParam
|
|
31789
|
+
}
|
|
31790
|
+
}, async ({ user, file: file2, parent, depth, max, noAllDrives, account }) => {
|
|
31791
|
+
const args = ["drive", "audit", "user", user];
|
|
31792
|
+
if (file2) args.push(`--file=${file2}`);
|
|
31793
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
31794
|
+
if (depth !== void 0) args.push(`--depth=${depth}`);
|
|
31795
|
+
if (max !== void 0) args.push(`--max=${max}`);
|
|
31796
|
+
if (noAllDrives) args.push("--no-all-drives");
|
|
31797
|
+
return runOrDiagnose(args, { account });
|
|
31798
|
+
});
|
|
31561
31799
|
}
|
|
31562
31800
|
|
|
31563
31801
|
// src/index.ts
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp-drive",
|
|
5
5
|
"display_name": "gogcli (Drive)",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.4.0",
|
|
7
7
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
|
@@ -172,6 +172,54 @@
|
|
|
172
172
|
{
|
|
173
173
|
"name": "gog_drive_comments_reopen",
|
|
174
174
|
"description": "Reopen a previously resolved comment"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "gog_drive_du",
|
|
178
|
+
"description": "Summarize Drive folder sizes (disk-usage style)"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"name": "gog_drive_tree",
|
|
182
|
+
"description": "Print a read-only folder tree"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "gog_drive_changes_start_token",
|
|
186
|
+
"description": "Get a Drive changes start page token for sync/automation"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "gog_drive_changes_list",
|
|
190
|
+
"description": "List Drive changes since a page token"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "gog_drive_labels_list",
|
|
194
|
+
"description": "List Drive label schemas"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "gog_drive_labels_get",
|
|
198
|
+
"description": "Get a single Drive label schema by name"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "gog_drive_labels_file_list",
|
|
202
|
+
"description": "List labels applied to a Drive file"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"name": "gog_drive_labels_file_apply",
|
|
206
|
+
"description": "Apply or update a label (with field values) on a Drive file"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "gog_drive_labels_file_remove",
|
|
210
|
+
"description": "Remove a label from a Drive file"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "gog_drive_activity",
|
|
214
|
+
"description": "Query the Drive Activity API for audit events"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "gog_drive_audit_sharing",
|
|
218
|
+
"description": "Audit Drive sharing for public/external permissions (no mutation)"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"name": "gog_drive_audit_user",
|
|
222
|
+
"description": "Audit Drive permissions granted to a specific user (no mutation)"
|
|
175
223
|
}
|
|
176
224
|
],
|
|
177
225
|
"compatibility": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-drive",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp-drive",
|
|
5
5
|
"description": "Extended Google Drive MCP server via gogcli — auth + full Drive support (upload/download/permissions/comments/shared drives)",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp-drive"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.4.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-drive",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.4.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/tools/drive-extra.ts
CHANGED
|
@@ -214,4 +214,256 @@ export function registerExtraDriveTools(server: McpServer): void {
|
|
|
214
214
|
}, async ({ fileId, commentId, account }) => {
|
|
215
215
|
return runOrDiagnose(['drive', 'comments', 'reopen', fileId, commentId], { account });
|
|
216
216
|
});
|
|
217
|
+
|
|
218
|
+
// --- gog 0.19.0 ---
|
|
219
|
+
|
|
220
|
+
server.registerTool('gog_drive_du', {
|
|
221
|
+
description: 'Summarize Drive folder sizes (disk-usage style) starting from a folder.',
|
|
222
|
+
annotations: { readOnlyHint: true },
|
|
223
|
+
inputSchema: {
|
|
224
|
+
parent: z.string().optional().describe('Folder ID to start from (default: root)'),
|
|
225
|
+
depth: z.number().optional().describe('Depth for folder totals (default: 1)'),
|
|
226
|
+
max: z.number().optional().describe('Max folders to return (0 = unlimited; default: 50)'),
|
|
227
|
+
sort: z.enum(['size', 'path', 'files']).optional().describe('Sort key (default: size)'),
|
|
228
|
+
order: z.enum(['asc', 'desc']).optional().describe('Sort order (default: desc)'),
|
|
229
|
+
noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
|
|
230
|
+
account: accountParam,
|
|
231
|
+
},
|
|
232
|
+
}, async ({ parent, depth, max, sort, order, noAllDrives, account }) => {
|
|
233
|
+
const args = ['drive', 'du'];
|
|
234
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
235
|
+
if (depth !== undefined) args.push(`--depth=${depth}`);
|
|
236
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
237
|
+
if (sort) args.push(`--sort=${sort}`);
|
|
238
|
+
if (order) args.push(`--order=${order}`);
|
|
239
|
+
if (noAllDrives) args.push('--no-all-drives');
|
|
240
|
+
return runOrDiagnose(args, { account });
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
server.registerTool('gog_drive_tree', {
|
|
244
|
+
description: 'Print a read-only folder tree starting from a folder.',
|
|
245
|
+
annotations: { readOnlyHint: true },
|
|
246
|
+
inputSchema: {
|
|
247
|
+
parent: z.string().optional().describe('Folder ID to start from (default: root)'),
|
|
248
|
+
depth: z.number().optional().describe('Max depth (0 = unlimited; default: 2)'),
|
|
249
|
+
max: z.number().optional().describe('Max items to return (0 = unlimited; default: 0)'),
|
|
250
|
+
noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
|
|
251
|
+
account: accountParam,
|
|
252
|
+
},
|
|
253
|
+
}, async ({ parent, depth, max, noAllDrives, account }) => {
|
|
254
|
+
const args = ['drive', 'tree'];
|
|
255
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
256
|
+
if (depth !== undefined) args.push(`--depth=${depth}`);
|
|
257
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
258
|
+
if (noAllDrives) args.push('--no-all-drives');
|
|
259
|
+
return runOrDiagnose(args, { account });
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
server.registerTool('gog_drive_changes_start_token', {
|
|
263
|
+
description: 'Get a Drive changes start page token — the cursor you pass to gog_drive_changes_list to enumerate changes since this point.',
|
|
264
|
+
annotations: { readOnlyHint: true },
|
|
265
|
+
inputSchema: {
|
|
266
|
+
drive: z.string().optional().describe('Shared drive ID for a shared-drive change log'),
|
|
267
|
+
account: accountParam,
|
|
268
|
+
},
|
|
269
|
+
}, async ({ drive, account }) => {
|
|
270
|
+
const args = ['drive', 'changes', 'start-token'];
|
|
271
|
+
if (drive) args.push(`--drive=${drive}`);
|
|
272
|
+
return runOrDiagnose(args, { account });
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
server.registerTool('gog_drive_changes_list', {
|
|
276
|
+
description: 'List Drive changes since a page token (for sync/automation). Get the initial token from gog_drive_changes_start_token; the response includes a newStartPageToken to persist for the next poll.',
|
|
277
|
+
annotations: { readOnlyHint: true },
|
|
278
|
+
inputSchema: {
|
|
279
|
+
token: z.string().describe('Start page token or next page token'),
|
|
280
|
+
max: z.number().optional().describe('Max results (default: 100)'),
|
|
281
|
+
page: z.string().optional().describe('Alias for token when continuing a page'),
|
|
282
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
283
|
+
includeRemoved: z.boolean().optional().describe('Include removed changes'),
|
|
284
|
+
drive: z.string().optional().describe('Shared drive ID for a shared-drive change log'),
|
|
285
|
+
account: accountParam,
|
|
286
|
+
},
|
|
287
|
+
}, async ({ token, max, page, all, includeRemoved, drive, account }) => {
|
|
288
|
+
const args = ['drive', 'changes', 'list', `--token=${token}`];
|
|
289
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
290
|
+
if (page) args.push(`--page=${page}`);
|
|
291
|
+
if (all) args.push('--all');
|
|
292
|
+
if (includeRemoved) args.push('--include-removed');
|
|
293
|
+
if (drive) args.push(`--drive=${drive}`);
|
|
294
|
+
return runOrDiagnose(args, { account });
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
server.registerTool('gog_drive_labels_list', {
|
|
298
|
+
description: 'List Drive label schemas available to the account.',
|
|
299
|
+
annotations: { readOnlyHint: true },
|
|
300
|
+
inputSchema: {
|
|
301
|
+
language: z.string().optional().describe('BCP-47 language code'),
|
|
302
|
+
view: z.enum(['LABEL_VIEW_BASIC', 'LABEL_VIEW_FULL']).optional().describe('Label view (default: LABEL_VIEW_BASIC)'),
|
|
303
|
+
minimumRole: z.string().optional().describe('Minimum role filter (e.g. READER, APPLIER, ORGANIZER)'),
|
|
304
|
+
publishedOnly: z.boolean().optional().describe('Only list published labels'),
|
|
305
|
+
adminAccess: z.boolean().optional().describe('Use admin access for Workspace admin accounts'),
|
|
306
|
+
account: accountParam,
|
|
307
|
+
},
|
|
308
|
+
}, async ({ language, view, minimumRole, publishedOnly, adminAccess, account }) => {
|
|
309
|
+
const args = ['drive', 'labels', 'list'];
|
|
310
|
+
if (language) args.push(`--language=${language}`);
|
|
311
|
+
if (view) args.push(`--view=${view}`);
|
|
312
|
+
if (minimumRole) args.push(`--minimum-role=${minimumRole}`);
|
|
313
|
+
if (publishedOnly) args.push('--published-only');
|
|
314
|
+
if (adminAccess) args.push('--admin-access');
|
|
315
|
+
return runOrDiagnose(args, { account });
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
server.registerTool('gog_drive_labels_get', {
|
|
319
|
+
description: 'Get a single Drive label schema by name (e.g. "labels/abc123").',
|
|
320
|
+
annotations: { readOnlyHint: true },
|
|
321
|
+
inputSchema: {
|
|
322
|
+
name: z.string().describe('Label schema name (e.g. labels/abc123)'),
|
|
323
|
+
language: z.string().optional().describe('BCP-47 language code'),
|
|
324
|
+
view: z.enum(['LABEL_VIEW_BASIC', 'LABEL_VIEW_FULL']).optional().describe('Label view (default: LABEL_VIEW_FULL)'),
|
|
325
|
+
adminAccess: z.boolean().optional().describe('Use admin access for Workspace admin accounts'),
|
|
326
|
+
account: accountParam,
|
|
327
|
+
},
|
|
328
|
+
}, async ({ name, language, view, adminAccess, account }) => {
|
|
329
|
+
const args = ['drive', 'labels', 'get', name];
|
|
330
|
+
if (language) args.push(`--language=${language}`);
|
|
331
|
+
if (view) args.push(`--view=${view}`);
|
|
332
|
+
if (adminAccess) args.push('--admin-access');
|
|
333
|
+
return runOrDiagnose(args, { account });
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
server.registerTool('gog_drive_labels_file_list', {
|
|
337
|
+
description: 'List labels applied to a Drive file.',
|
|
338
|
+
annotations: { readOnlyHint: true },
|
|
339
|
+
inputSchema: {
|
|
340
|
+
fileId: z.string().describe('File ID'),
|
|
341
|
+
max: z.number().optional().describe('Max results (default: 100)'),
|
|
342
|
+
page: z.string().optional().describe('Page token'),
|
|
343
|
+
account: accountParam,
|
|
344
|
+
},
|
|
345
|
+
}, async ({ fileId, max, page, account }) => {
|
|
346
|
+
const args = ['drive', 'labels', 'file', 'list', fileId];
|
|
347
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
348
|
+
if (page) args.push(`--page=${page}`);
|
|
349
|
+
return runOrDiagnose(args, { account });
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
server.registerTool('gog_drive_labels_file_apply', {
|
|
353
|
+
description: 'Apply or update a label on a Drive file, optionally setting field values. Each field flag takes "fieldId=value" entries (repeatable). selection/integer/date/user values may be comma-separated within one entry for multi-valued fields.',
|
|
354
|
+
inputSchema: {
|
|
355
|
+
fileId: z.string().describe('File ID'),
|
|
356
|
+
labelId: z.string().describe('Label ID to apply'),
|
|
357
|
+
text: z.array(z.string()).optional().describe('Text fields as fieldId=value (repeatable)'),
|
|
358
|
+
selection: z.array(z.string()).optional().describe('Selection fields as fieldId=choiceId[,choiceId] (repeatable)'),
|
|
359
|
+
integer: z.array(z.string()).optional().describe('Integer fields as fieldId=123[,456] (repeatable)'),
|
|
360
|
+
date: z.array(z.string()).optional().describe('Date fields as fieldId=YYYY-MM-DD[,YYYY-MM-DD] (repeatable)'),
|
|
361
|
+
user: z.array(z.string()).optional().describe('User fields as fieldId=email[,email] (repeatable)'),
|
|
362
|
+
unset: z.array(z.string()).optional().describe('Field IDs to unset (repeatable)'),
|
|
363
|
+
fieldsJson: z.string().optional().describe('Simple JSON object of fieldId to string/number/bool/string-array values'),
|
|
364
|
+
account: accountParam,
|
|
365
|
+
},
|
|
366
|
+
}, async ({ fileId, labelId, text, selection, integer, date, user, unset, fieldsJson, account }) => {
|
|
367
|
+
const args = ['drive', 'labels', 'file', 'apply', fileId, labelId];
|
|
368
|
+
if (text) for (const t of text) args.push(`--text=${t}`);
|
|
369
|
+
if (selection) for (const s of selection) args.push(`--selection=${s}`);
|
|
370
|
+
if (integer) for (const i of integer) args.push(`--integer=${i}`);
|
|
371
|
+
if (date) for (const d of date) args.push(`--date=${d}`);
|
|
372
|
+
if (user) for (const u of user) args.push(`--user=${u}`);
|
|
373
|
+
if (unset) for (const u of unset) args.push(`--unset=${u}`);
|
|
374
|
+
if (fieldsJson) args.push(`--fields-json=${fieldsJson}`);
|
|
375
|
+
return runOrDiagnose(args, { account });
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
server.registerTool('gog_drive_labels_file_remove', {
|
|
379
|
+
description: 'Remove a label from a Drive file.',
|
|
380
|
+
annotations: { destructiveHint: true },
|
|
381
|
+
inputSchema: {
|
|
382
|
+
fileId: z.string().describe('File ID'),
|
|
383
|
+
labelId: z.string().describe('Label ID to remove'),
|
|
384
|
+
account: accountParam,
|
|
385
|
+
},
|
|
386
|
+
}, async ({ fileId, labelId, account }) => {
|
|
387
|
+
return runOrDiagnose(['drive', 'labels', 'file', 'remove', fileId, labelId], { account });
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
server.registerTool('gog_drive_activity', {
|
|
391
|
+
description: 'Query the Drive Activity API for audit events (edits, creates, deletes, moves, shares, etc.) scoped to a file or folder and/or time range.',
|
|
392
|
+
annotations: { readOnlyHint: true },
|
|
393
|
+
inputSchema: {
|
|
394
|
+
file: z.string().optional().describe('Drive file ID to query'),
|
|
395
|
+
folder: z.string().optional().describe('Drive folder ID; includes descendants'),
|
|
396
|
+
actions: z.string().optional().describe('Comma-separated action filters: edit,create,delete,move,rename,restore,comment,share,label,dlp,reference,settings'),
|
|
397
|
+
from: z.string().optional().describe('Lower activity time bound (RFC3339)'),
|
|
398
|
+
to: z.string().optional().describe('Upper activity time bound (RFC3339)'),
|
|
399
|
+
filter: z.string().optional().describe('Raw Drive Activity filter expression appended with AND'),
|
|
400
|
+
max: z.number().optional().describe('Page size (default: 10)'),
|
|
401
|
+
page: z.string().optional().describe('Page token'),
|
|
402
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
403
|
+
consolidate: z.boolean().optional().describe('Use Drive Activity legacy consolidation strategy'),
|
|
404
|
+
account: accountParam,
|
|
405
|
+
},
|
|
406
|
+
}, async ({ file, folder, actions, from, to, filter, max, page, all, consolidate, account }) => {
|
|
407
|
+
const args = ['drive', 'activity', 'query'];
|
|
408
|
+
if (file) args.push(`--file=${file}`);
|
|
409
|
+
if (folder) args.push(`--folder=${folder}`);
|
|
410
|
+
if (actions) args.push(`--actions=${actions}`);
|
|
411
|
+
if (from) args.push(`--from=${from}`);
|
|
412
|
+
if (to) args.push(`--to=${to}`);
|
|
413
|
+
if (filter) args.push(`--filter=${filter}`);
|
|
414
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
415
|
+
if (page) args.push(`--page=${page}`);
|
|
416
|
+
if (all) args.push('--all');
|
|
417
|
+
if (consolidate) args.push('--consolidate');
|
|
418
|
+
return runOrDiagnose(args, { account });
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
server.registerTool('gog_drive_audit_sharing', {
|
|
422
|
+
description: 'Audit Drive sharing without mutation — find public (anyone-with-link) or external permissions across a folder tree or a single file.',
|
|
423
|
+
annotations: { readOnlyHint: true },
|
|
424
|
+
inputSchema: {
|
|
425
|
+
file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
|
|
426
|
+
parent: z.string().optional().describe('Folder ID to scan (default: root)'),
|
|
427
|
+
depth: z.number().optional().describe('Max folder depth (0 = unlimited; default: 2)'),
|
|
428
|
+
max: z.number().optional().describe('Max files/folders to scan (0 = unlimited; default: 500)'),
|
|
429
|
+
internalDomain: z.array(z.string()).optional().describe('Domains treated as internal (defaults to account email domain)'),
|
|
430
|
+
publicOnly: z.boolean().optional().describe('Only report anyone-with-link/public permissions'),
|
|
431
|
+
externalOnly: z.boolean().optional().describe('Only report external user/group/domain permissions'),
|
|
432
|
+
noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
|
|
433
|
+
account: accountParam,
|
|
434
|
+
},
|
|
435
|
+
}, async ({ file, parent, depth, max, internalDomain, publicOnly, externalOnly, noAllDrives, account }) => {
|
|
436
|
+
const args = ['drive', 'audit', 'sharing'];
|
|
437
|
+
if (file) args.push(`--file=${file}`);
|
|
438
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
439
|
+
if (depth !== undefined) args.push(`--depth=${depth}`);
|
|
440
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
441
|
+
if (internalDomain) for (const d of internalDomain) args.push(`--internal-domain=${d}`);
|
|
442
|
+
if (publicOnly) args.push('--public-only');
|
|
443
|
+
if (externalOnly) args.push('--external-only');
|
|
444
|
+
if (noAllDrives) args.push('--no-all-drives');
|
|
445
|
+
return runOrDiagnose(args, { account });
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
server.registerTool('gog_drive_audit_user', {
|
|
449
|
+
description: 'Audit Drive sharing without mutation — find permissions granted to a specific user across a folder tree or a single file.',
|
|
450
|
+
annotations: { readOnlyHint: true },
|
|
451
|
+
inputSchema: {
|
|
452
|
+
user: z.string().describe('User email to audit permissions for'),
|
|
453
|
+
file: z.string().optional().describe('Audit one file ID instead of a folder tree'),
|
|
454
|
+
parent: z.string().optional().describe('Folder ID to scan (default: root)'),
|
|
455
|
+
depth: z.number().optional().describe('Max folder depth (0 = unlimited; default: 2)'),
|
|
456
|
+
max: z.number().optional().describe('Max files/folders to scan (0 = unlimited; default: 500)'),
|
|
457
|
+
noAllDrives: z.boolean().optional().describe('My Drive only — exclude shared drives (shared drives are included by default)'),
|
|
458
|
+
account: accountParam,
|
|
459
|
+
},
|
|
460
|
+
}, async ({ user, file, parent, depth, max, noAllDrives, account }) => {
|
|
461
|
+
const args = ['drive', 'audit', 'user', user];
|
|
462
|
+
if (file) args.push(`--file=${file}`);
|
|
463
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
464
|
+
if (depth !== undefined) args.push(`--depth=${depth}`);
|
|
465
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
466
|
+
if (noAllDrives) args.push('--no-all-drives');
|
|
467
|
+
return runOrDiagnose(args, { account });
|
|
468
|
+
});
|
|
217
469
|
}
|
|
@@ -254,3 +254,315 @@ describe('gog_drive_comments_reopen', () => {
|
|
|
254
254
|
);
|
|
255
255
|
});
|
|
256
256
|
});
|
|
257
|
+
|
|
258
|
+
// --- gog 0.19.0 ---
|
|
259
|
+
|
|
260
|
+
describe('gog_drive_du', () => {
|
|
261
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
262
|
+
await handlers.get('gog_drive_du')!({});
|
|
263
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'du'], { account: undefined });
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('passes all flags', async () => {
|
|
267
|
+
await handlers.get('gog_drive_du')!({
|
|
268
|
+
parent: 'folder1', depth: 3, max: 20, sort: 'files', order: 'asc', noAllDrives: true, account: 'a@b.com',
|
|
269
|
+
});
|
|
270
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
271
|
+
['drive', 'du', '--parent=folder1', '--depth=3', '--max=20', '--sort=files', '--order=asc', '--no-all-drives'],
|
|
272
|
+
{ account: 'a@b.com' },
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('handles depth/max=0 and omits noAllDrives when false', async () => {
|
|
277
|
+
await handlers.get('gog_drive_du')!({ depth: 0, max: 0, noAllDrives: false });
|
|
278
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
279
|
+
['drive', 'du', '--depth=0', '--max=0'],
|
|
280
|
+
{ account: undefined },
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('gog_drive_tree', () => {
|
|
286
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
287
|
+
await handlers.get('gog_drive_tree')!({});
|
|
288
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'tree'], { account: undefined });
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('passes all flags', async () => {
|
|
292
|
+
await handlers.get('gog_drive_tree')!({ parent: 'folder1', depth: 4, max: 100, noAllDrives: true });
|
|
293
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
294
|
+
['drive', 'tree', '--parent=folder1', '--depth=4', '--max=100', '--no-all-drives'],
|
|
295
|
+
{ account: undefined },
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('handles depth=0 and omits noAllDrives when false', async () => {
|
|
300
|
+
await handlers.get('gog_drive_tree')!({ depth: 0, noAllDrives: false });
|
|
301
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'tree', '--depth=0'], { account: undefined });
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe('gog_drive_changes_start_token', () => {
|
|
306
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
307
|
+
await handlers.get('gog_drive_changes_start_token')!({});
|
|
308
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'changes', 'start-token'], { account: undefined });
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('passes --drive when provided', async () => {
|
|
312
|
+
await handlers.get('gog_drive_changes_start_token')!({ drive: 'd1', account: 'a@b.com' });
|
|
313
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
314
|
+
['drive', 'changes', 'start-token', '--drive=d1'],
|
|
315
|
+
{ account: 'a@b.com' },
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('gog_drive_changes_list', () => {
|
|
321
|
+
it('calls runOrDiagnose with token only', async () => {
|
|
322
|
+
await handlers.get('gog_drive_changes_list')!({ token: 'tok1' });
|
|
323
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
324
|
+
['drive', 'changes', 'list', '--token=tok1'],
|
|
325
|
+
{ account: undefined },
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('passes all flags', async () => {
|
|
330
|
+
await handlers.get('gog_drive_changes_list')!({
|
|
331
|
+
token: 'tok1', max: 50, page: 'p2', all: true, includeRemoved: true, drive: 'd1',
|
|
332
|
+
});
|
|
333
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
334
|
+
['drive', 'changes', 'list', '--token=tok1', '--max=50', '--page=p2', '--all', '--include-removed', '--drive=d1'],
|
|
335
|
+
{ account: undefined },
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('omits boolean flags when false', async () => {
|
|
340
|
+
await handlers.get('gog_drive_changes_list')!({ token: 'tok1', all: false, includeRemoved: false });
|
|
341
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
342
|
+
['drive', 'changes', 'list', '--token=tok1'],
|
|
343
|
+
{ account: undefined },
|
|
344
|
+
);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('gog_drive_labels_list', () => {
|
|
349
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
350
|
+
await handlers.get('gog_drive_labels_list')!({});
|
|
351
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'labels', 'list'], { account: undefined });
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('passes all flags', async () => {
|
|
355
|
+
await handlers.get('gog_drive_labels_list')!({
|
|
356
|
+
language: 'en', view: 'LABEL_VIEW_FULL', minimumRole: 'APPLIER', publishedOnly: true, adminAccess: true,
|
|
357
|
+
});
|
|
358
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
359
|
+
['drive', 'labels', 'list', '--language=en', '--view=LABEL_VIEW_FULL', '--minimum-role=APPLIER', '--published-only', '--admin-access'],
|
|
360
|
+
{ account: undefined },
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('omits boolean flags when false', async () => {
|
|
365
|
+
await handlers.get('gog_drive_labels_list')!({ publishedOnly: false, adminAccess: false });
|
|
366
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'labels', 'list'], { account: undefined });
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('gog_drive_labels_get', () => {
|
|
371
|
+
it('calls runOrDiagnose with name only', async () => {
|
|
372
|
+
await handlers.get('gog_drive_labels_get')!({ name: 'labels/abc' });
|
|
373
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'labels', 'get', 'labels/abc'], { account: undefined });
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('passes all flags', async () => {
|
|
377
|
+
await handlers.get('gog_drive_labels_get')!({
|
|
378
|
+
name: 'labels/abc', language: 'en', view: 'LABEL_VIEW_BASIC', adminAccess: true,
|
|
379
|
+
});
|
|
380
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
381
|
+
['drive', 'labels', 'get', 'labels/abc', '--language=en', '--view=LABEL_VIEW_BASIC', '--admin-access'],
|
|
382
|
+
{ account: undefined },
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('omits --admin-access when false', async () => {
|
|
387
|
+
await handlers.get('gog_drive_labels_get')!({ name: 'labels/abc', adminAccess: false });
|
|
388
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'labels', 'get', 'labels/abc'], { account: undefined });
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('gog_drive_labels_file_list', () => {
|
|
393
|
+
it('calls runOrDiagnose with fileId only', async () => {
|
|
394
|
+
await handlers.get('gog_drive_labels_file_list')!({ fileId: 'f1' });
|
|
395
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
396
|
+
['drive', 'labels', 'file', 'list', 'f1'],
|
|
397
|
+
{ account: undefined },
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('passes --max and --page when provided', async () => {
|
|
402
|
+
await handlers.get('gog_drive_labels_file_list')!({ fileId: 'f1', max: 25, page: 'tok' });
|
|
403
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
404
|
+
['drive', 'labels', 'file', 'list', 'f1', '--max=25', '--page=tok'],
|
|
405
|
+
{ account: undefined },
|
|
406
|
+
);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe('gog_drive_labels_file_apply', () => {
|
|
411
|
+
it('calls runOrDiagnose with fileId and labelId only', async () => {
|
|
412
|
+
await handlers.get('gog_drive_labels_file_apply')!({ fileId: 'f1', labelId: 'l1' });
|
|
413
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
414
|
+
['drive', 'labels', 'file', 'apply', 'f1', 'l1'],
|
|
415
|
+
{ account: undefined },
|
|
416
|
+
);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('passes all repeatable field flags and fields-json', async () => {
|
|
420
|
+
await handlers.get('gog_drive_labels_file_apply')!({
|
|
421
|
+
fileId: 'f1',
|
|
422
|
+
labelId: 'l1',
|
|
423
|
+
text: ['t1=hello', 't2=world'],
|
|
424
|
+
selection: ['s1=c1,c2'],
|
|
425
|
+
integer: ['i1=42'],
|
|
426
|
+
date: ['d1=2026-01-01'],
|
|
427
|
+
user: ['u1=a@b.com'],
|
|
428
|
+
unset: ['x1', 'x2'],
|
|
429
|
+
fieldsJson: '{"f":"v"}',
|
|
430
|
+
});
|
|
431
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
432
|
+
[
|
|
433
|
+
'drive', 'labels', 'file', 'apply', 'f1', 'l1',
|
|
434
|
+
'--text=t1=hello', '--text=t2=world',
|
|
435
|
+
'--selection=s1=c1,c2',
|
|
436
|
+
'--integer=i1=42',
|
|
437
|
+
'--date=d1=2026-01-01',
|
|
438
|
+
'--user=u1=a@b.com',
|
|
439
|
+
'--unset=x1', '--unset=x2',
|
|
440
|
+
'--fields-json={"f":"v"}',
|
|
441
|
+
],
|
|
442
|
+
{ account: undefined },
|
|
443
|
+
);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('omits empty arrays', async () => {
|
|
447
|
+
await handlers.get('gog_drive_labels_file_apply')!({
|
|
448
|
+
fileId: 'f1', labelId: 'l1', text: [], selection: [], integer: [], date: [], user: [], unset: [],
|
|
449
|
+
});
|
|
450
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
451
|
+
['drive', 'labels', 'file', 'apply', 'f1', 'l1'],
|
|
452
|
+
{ account: undefined },
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
describe('gog_drive_labels_file_remove', () => {
|
|
458
|
+
it('calls runOrDiagnose with fileId and labelId', async () => {
|
|
459
|
+
await handlers.get('gog_drive_labels_file_remove')!({ fileId: 'f1', labelId: 'l1' });
|
|
460
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
461
|
+
['drive', 'labels', 'file', 'remove', 'f1', 'l1'],
|
|
462
|
+
{ account: undefined },
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
describe('gog_drive_activity', () => {
|
|
468
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
469
|
+
await handlers.get('gog_drive_activity')!({});
|
|
470
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'activity', 'query'], { account: undefined });
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('passes all flags', async () => {
|
|
474
|
+
await handlers.get('gog_drive_activity')!({
|
|
475
|
+
file: 'f1',
|
|
476
|
+
folder: 'fo1',
|
|
477
|
+
actions: 'edit,share',
|
|
478
|
+
from: '2026-01-01T00:00:00Z',
|
|
479
|
+
to: '2026-02-01T00:00:00Z',
|
|
480
|
+
filter: 'detail.action_detail_case:RENAME',
|
|
481
|
+
max: 25,
|
|
482
|
+
page: 'p2',
|
|
483
|
+
all: true,
|
|
484
|
+
consolidate: true,
|
|
485
|
+
});
|
|
486
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
487
|
+
[
|
|
488
|
+
'drive', 'activity', 'query',
|
|
489
|
+
'--file=f1', '--folder=fo1', '--actions=edit,share',
|
|
490
|
+
'--from=2026-01-01T00:00:00Z', '--to=2026-02-01T00:00:00Z',
|
|
491
|
+
'--filter=detail.action_detail_case:RENAME', '--max=25', '--page=p2', '--all', '--consolidate',
|
|
492
|
+
],
|
|
493
|
+
{ account: undefined },
|
|
494
|
+
);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('omits boolean flags when false', async () => {
|
|
498
|
+
await handlers.get('gog_drive_activity')!({ all: false, consolidate: false });
|
|
499
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'activity', 'query'], { account: undefined });
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
describe('gog_drive_audit_sharing', () => {
|
|
504
|
+
it('calls runOrDiagnose with no flags', async () => {
|
|
505
|
+
await handlers.get('gog_drive_audit_sharing')!({});
|
|
506
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'audit', 'sharing'], { account: undefined });
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('passes all flags', async () => {
|
|
510
|
+
await handlers.get('gog_drive_audit_sharing')!({
|
|
511
|
+
file: 'f1',
|
|
512
|
+
parent: 'fo1',
|
|
513
|
+
depth: 3,
|
|
514
|
+
max: 100,
|
|
515
|
+
internalDomain: ['example.com', 'corp.example.com'],
|
|
516
|
+
publicOnly: true,
|
|
517
|
+
externalOnly: true,
|
|
518
|
+
noAllDrives: true,
|
|
519
|
+
});
|
|
520
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
521
|
+
[
|
|
522
|
+
'drive', 'audit', 'sharing',
|
|
523
|
+
'--file=f1', '--parent=fo1', '--depth=3', '--max=100',
|
|
524
|
+
'--internal-domain=example.com', '--internal-domain=corp.example.com',
|
|
525
|
+
'--public-only', '--external-only', '--no-all-drives',
|
|
526
|
+
],
|
|
527
|
+
{ account: undefined },
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('handles depth/max=0, empty domains, and omits booleans when false', async () => {
|
|
532
|
+
await handlers.get('gog_drive_audit_sharing')!({
|
|
533
|
+
depth: 0, max: 0, internalDomain: [], publicOnly: false, externalOnly: false, noAllDrives: false,
|
|
534
|
+
});
|
|
535
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
536
|
+
['drive', 'audit', 'sharing', '--depth=0', '--max=0'],
|
|
537
|
+
{ account: undefined },
|
|
538
|
+
);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
describe('gog_drive_audit_user', () => {
|
|
543
|
+
it('calls runOrDiagnose with user only', async () => {
|
|
544
|
+
await handlers.get('gog_drive_audit_user')!({ user: 'a@b.com' });
|
|
545
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
546
|
+
['drive', 'audit', 'user', 'a@b.com'],
|
|
547
|
+
{ account: undefined },
|
|
548
|
+
);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('passes all flags', async () => {
|
|
552
|
+
await handlers.get('gog_drive_audit_user')!({
|
|
553
|
+
user: 'a@b.com', file: 'f1', parent: 'fo1', depth: 3, max: 100, noAllDrives: true,
|
|
554
|
+
});
|
|
555
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
556
|
+
['drive', 'audit', 'user', 'a@b.com', '--file=f1', '--parent=fo1', '--depth=3', '--max=100', '--no-all-drives'],
|
|
557
|
+
{ account: undefined },
|
|
558
|
+
);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('handles depth/max=0 and omits noAllDrives when false', async () => {
|
|
562
|
+
await handlers.get('gog_drive_audit_user')!({ user: 'a@b.com', depth: 0, max: 0, noAllDrives: false });
|
|
563
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
564
|
+
['drive', 'audit', 'user', 'a@b.com', '--depth=0', '--max=0'],
|
|
565
|
+
{ account: undefined },
|
|
566
|
+
);
|
|
567
|
+
});
|
|
568
|
+
});
|