runline 0.12.0 → 0.13.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.
@@ -54,7 +54,37 @@
54
54
  * (Buffer) vs a path (streamed).
55
55
  */
56
56
  import { createReadStream, readFileSync, statSync, writeFileSync } from "node:fs";
57
+ import * as t from "typebox";
57
58
  import { googleAccessToken } from "../../_shared/googleAuth.js";
59
+ import { Id, NonEmptyString, RawGoogleObject, StringMap, StringOrStringArray, stringEnum, } from "../../_shared/googleSchemas.js";
60
+ const PermissionRole = stringEnum([
61
+ "owner",
62
+ "organizer",
63
+ "fileOrganizer",
64
+ "writer",
65
+ "commenter",
66
+ "reader",
67
+ ]);
68
+ const PermissionType = stringEnum([
69
+ "user",
70
+ "group",
71
+ "domain",
72
+ "anyone",
73
+ ]);
74
+ const AccessProposalRole = stringEnum([
75
+ "writer",
76
+ "commenter",
77
+ "reader",
78
+ ]);
79
+ const PageSize100 = t.Integer({ minimum: 1, maximum: 100 });
80
+ const PageSize1000 = t.Integer({ minimum: 1, maximum: 1000 });
81
+ const Rfc3339Timestamp = t.String({
82
+ pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$",
83
+ });
84
+ const QuotedFileContent = t.Object({
85
+ value: NonEmptyString,
86
+ mimeType: t.Optional(NonEmptyString),
87
+ }, { additionalProperties: false });
58
88
  // ─── MIME constants ──────────────────────────────────────────────
59
89
  const DRIVE = {
60
90
  FOLDER: "application/vnd.google-apps.folder",
@@ -437,33 +467,28 @@ export default function googleDrive(rl) {
437
467
  rl.registerAction("file.upload", {
438
468
  access: "write",
439
469
  description: "Upload a file to Drive. Supply one of contentBase64 / contentPath / content. Uses multipart for small files, resumable (2 MiB chunks) for large streamed paths.",
440
- inputSchema: {
441
- name: { type: "string", required: false, description: "File name in Drive" },
442
- folderId: { type: "string", required: false, description: "Parent folder (default: root)" },
443
- driveId: { type: "string", required: false, description: "Target shared drive" },
444
- mimeType: { type: "string", required: false },
445
- contentBase64: { type: "string", required: false },
446
- contentPath: { type: "string", required: false, description: "Local filesystem path" },
447
- content: { type: "string", required: false, description: "Inline utf-8 content" },
448
- properties: {
449
- type: "object",
450
- required: false,
451
- description: "Public key-value properties",
452
- },
453
- appProperties: {
454
- type: "object",
455
- required: false,
456
- description: "App-private key-value properties",
457
- },
458
- keepRevisionForever: { type: "boolean", required: false },
459
- ocrLanguage: { type: "string", required: false },
460
- useContentAsIndexableText: { type: "boolean", required: false },
461
- fields: {
462
- type: "string",
463
- required: false,
464
- description: "Fields projection (default: '*' returns everything)",
465
- },
466
- },
470
+ inputSchema: t.Object({
471
+ name: t.Optional(NonEmptyString),
472
+ folderId: t.Optional(Id),
473
+ driveId: t.Optional(Id),
474
+ mimeType: t.Optional(NonEmptyString),
475
+ contentBase64: t.Optional(t.String()),
476
+ contentPath: t.Optional(NonEmptyString),
477
+ content: t.Optional(t.String()),
478
+ properties: t.Optional(StringMap),
479
+ appProperties: t.Optional(StringMap),
480
+ keepRevisionForever: t.Optional(t.Boolean()),
481
+ ocrLanguage: t.Optional(NonEmptyString),
482
+ useContentAsIndexableText: t.Optional(t.Boolean()),
483
+ fields: t.Optional(NonEmptyString),
484
+ }, {
485
+ additionalProperties: false,
486
+ oneOf: [
487
+ { required: ["contentBase64"] },
488
+ { required: ["contentPath"] },
489
+ { required: ["content"] },
490
+ ],
491
+ }),
467
492
  async execute(input, ctx) {
468
493
  const p = (input ?? {});
469
494
  const content = resolveContent(p);
@@ -499,15 +524,15 @@ export default function googleDrive(rl) {
499
524
  rl.registerAction("file.createFromText", {
500
525
  access: "write",
501
526
  description: "Create a text file from inline content. Set convertToGoogleDocument=true to convert to a Google Doc.",
502
- inputSchema: {
503
- name: { type: "string", required: false, description: 'Default: "Untitled"' },
504
- content: { type: "string", required: true },
505
- folderId: { type: "string", required: false },
506
- driveId: { type: "string", required: false },
507
- convertToGoogleDocument: { type: "boolean", required: false },
508
- properties: { type: "object", required: false },
509
- appProperties: { type: "object", required: false },
510
- },
527
+ inputSchema: t.Object({
528
+ name: t.Optional(NonEmptyString),
529
+ content: t.String(),
530
+ folderId: t.Optional(Id),
531
+ driveId: t.Optional(Id),
532
+ convertToGoogleDocument: t.Optional(t.Boolean()),
533
+ properties: t.Optional(StringMap),
534
+ appProperties: t.Optional(StringMap),
535
+ }, { additionalProperties: false }),
511
536
  async execute(input, ctx) {
512
537
  const p = (input ?? {});
513
538
  const name = p.name || "Untitled";
@@ -573,19 +598,11 @@ export default function googleDrive(rl) {
573
598
  rl.registerAction("file.download", {
574
599
  access: "write",
575
600
  description: "Download a file. Google-native docs are exported to the chosen format; regular files are downloaded as-is. Returns base64 by default, or writes to disk when savePath is set.",
576
- inputSchema: {
577
- fileId: { type: "string", required: true },
578
- savePath: {
579
- type: "string",
580
- required: false,
581
- description: "Write bytes to this filesystem path instead of returning base64",
582
- },
583
- googleDocFormat: {
584
- type: "string",
585
- required: false,
586
- description: "Export MIME type for Google Docs (default: DOCX / PPTX / XLSX / image/jpeg by type)",
587
- },
588
- },
601
+ inputSchema: t.Object({
602
+ fileId: Id,
603
+ savePath: t.Optional(NonEmptyString),
604
+ googleDocFormat: t.Optional(NonEmptyString),
605
+ }, { additionalProperties: false }),
589
606
  async execute(input, ctx) {
590
607
  const p = (input ?? {});
591
608
  const fileId = p.fileId;
@@ -638,18 +655,14 @@ export default function googleDrive(rl) {
638
655
  rl.registerAction("file.copy", {
639
656
  access: "write",
640
657
  description: "Copy a file",
641
- inputSchema: {
642
- fileId: { type: "string", required: true },
643
- name: { type: "string", required: false, description: 'Default: "Copy of {original}"' },
644
- folderId: {
645
- type: "string",
646
- required: false,
647
- description: "If omitted, copy stays in the same folder(s)",
648
- },
649
- driveId: { type: "string", required: false },
650
- description: { type: "string", required: false },
651
- copyRequiresWriterPermission: { type: "boolean", required: false },
652
- },
658
+ inputSchema: t.Object({
659
+ fileId: Id,
660
+ name: t.Optional(NonEmptyString),
661
+ folderId: t.Optional(Id),
662
+ driveId: t.Optional(Id),
663
+ description: t.Optional(t.String()),
664
+ copyRequiresWriterPermission: t.Optional(t.Boolean()),
665
+ }, { additionalProperties: false }),
653
666
  async execute(input, ctx) {
654
667
  const p = (input ?? {});
655
668
  const body = {};
@@ -669,11 +682,11 @@ export default function googleDrive(rl) {
669
682
  rl.registerAction("file.move", {
670
683
  access: "write",
671
684
  description: "Move a file to another folder. Resolves current parents and swaps them in a single PATCH.",
672
- inputSchema: {
673
- fileId: { type: "string", required: true },
674
- folderId: { type: "string", required: false, description: "Destination folder" },
675
- driveId: { type: "string", required: false, description: "Destination shared drive" },
676
- },
685
+ inputSchema: t.Object({
686
+ fileId: Id,
687
+ folderId: t.Optional(Id),
688
+ driveId: t.Optional(Id),
689
+ }, { additionalProperties: false }),
677
690
  async execute(input, ctx) {
678
691
  const p = (input ?? {});
679
692
  const current = (await driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}`, undefined, { fields: "parents", supportsAllDrives: true }));
@@ -689,20 +702,49 @@ export default function googleDrive(rl) {
689
702
  rl.registerAction("file.update", {
690
703
  access: "write",
691
704
  description: "Patch file metadata and/or replace its bytes. Supply content{Base64,Path} to update bytes.",
692
- inputSchema: {
693
- fileId: { type: "string", required: true },
694
- name: { type: "string", required: false },
695
- mimeType: { type: "string", required: false },
696
- trashed: { type: "boolean", required: false, description: "Move to trash" },
697
- properties: { type: "object", required: false },
698
- appProperties: { type: "object", required: false },
699
- contentBase64: { type: "string", required: false },
700
- contentPath: { type: "string", required: false },
701
- keepRevisionForever: { type: "boolean", required: false },
702
- ocrLanguage: { type: "string", required: false },
703
- useContentAsIndexableText: { type: "boolean", required: false },
704
- fields: { type: "string", required: false, description: "Fields projection" },
705
- },
705
+ inputSchema: t.Object({
706
+ fileId: Id,
707
+ name: t.Optional(NonEmptyString),
708
+ mimeType: t.Optional(NonEmptyString),
709
+ trashed: t.Optional(t.Boolean()),
710
+ properties: t.Optional(StringMap),
711
+ appProperties: t.Optional(StringMap),
712
+ contentBase64: t.Optional(t.String()),
713
+ contentPath: t.Optional(NonEmptyString),
714
+ keepRevisionForever: t.Optional(t.Boolean()),
715
+ ocrLanguage: t.Optional(NonEmptyString),
716
+ useContentAsIndexableText: t.Optional(t.Boolean()),
717
+ fields: t.Optional(NonEmptyString),
718
+ }, {
719
+ additionalProperties: false,
720
+ allOf: [
721
+ { not: { required: ["contentBase64", "contentPath"] } },
722
+ ...[
723
+ "keepRevisionForever",
724
+ "ocrLanguage",
725
+ "useContentAsIndexableText",
726
+ ].map((field) => ({
727
+ anyOf: [
728
+ { not: { required: [field] } },
729
+ {
730
+ anyOf: [
731
+ { required: ["contentBase64"] },
732
+ { required: ["contentPath"] },
733
+ ],
734
+ },
735
+ ],
736
+ })),
737
+ ],
738
+ anyOf: [
739
+ { required: ["name"] },
740
+ { required: ["mimeType"] },
741
+ { required: ["trashed"] },
742
+ { required: ["properties"] },
743
+ { required: ["appProperties"] },
744
+ { required: ["contentBase64"] },
745
+ { required: ["contentPath"] },
746
+ ],
747
+ }),
706
748
  async execute(input, ctx) {
707
749
  const p = (input ?? {});
708
750
  const fileId = p.fileId;
@@ -717,6 +759,15 @@ export default function googleDrive(rl) {
717
759
  const url = new URL(`${API_BASE}/upload/drive/v3/files/${fileId}`);
718
760
  url.searchParams.set("uploadType", "media");
719
761
  url.searchParams.set("supportsAllDrives", "true");
762
+ for (const key of [
763
+ "keepRevisionForever",
764
+ "ocrLanguage",
765
+ "useContentAsIndexableText",
766
+ ]) {
767
+ if (p[key] !== undefined) {
768
+ url.searchParams.set(key, String(p[key]));
769
+ }
770
+ }
720
771
  const res = await fetch(url.toString(), {
721
772
  method: "PATCH",
722
773
  headers: {
@@ -735,6 +786,15 @@ export default function googleDrive(rl) {
735
786
  const initUrl = new URL(`${API_BASE}/upload/drive/v3/files/${fileId}`);
736
787
  initUrl.searchParams.set("uploadType", "resumable");
737
788
  initUrl.searchParams.set("supportsAllDrives", "true");
789
+ for (const key of [
790
+ "keepRevisionForever",
791
+ "ocrLanguage",
792
+ "useContentAsIndexableText",
793
+ ]) {
794
+ if (p[key] !== undefined) {
795
+ initUrl.searchParams.set(key, String(p[key]));
796
+ }
797
+ }
738
798
  const initRes = await fetch(initUrl.toString(), {
739
799
  method: "PATCH",
740
800
  headers: {
@@ -785,34 +845,28 @@ export default function googleDrive(rl) {
785
845
  body.name = p.name;
786
846
  if (p.mimeType !== undefined)
787
847
  body.mimeType = p.mimeType;
848
+ if (p.trashed !== undefined)
849
+ body.trashed = p.trashed;
788
850
  if (p.properties !== undefined)
789
851
  body.properties = p.properties;
790
852
  if (p.appProperties !== undefined)
791
853
  body.appProperties = p.appProperties;
792
- const qs = { supportsAllDrives: true };
793
- if (p.trashed !== undefined)
794
- qs.trashed = p.trashed;
795
- if (p.keepRevisionForever)
796
- qs.keepRevisionForever = p.keepRevisionForever;
797
- if (p.ocrLanguage)
798
- qs.ocrLanguage = p.ocrLanguage;
799
- if (p.useContentAsIndexableText)
800
- qs.useContentAsIndexableText = p.useContentAsIndexableText;
801
- if (p.fields)
802
- qs.fields = p.fields;
803
- if (Object.keys(body).length === 0 && !p.trashed && !p.fields) {
854
+ if (Object.keys(body).length === 0) {
855
+ if (hasBytes && p.fields) {
856
+ return driveRequest(ctx, "GET", `/drive/v3/files/${fileId}`, undefined, { supportsAllDrives: true, fields: p.fields });
857
+ }
804
858
  return { id: fileId, success: true };
805
859
  }
806
- return driveRequest(ctx, "PATCH", `/drive/v3/files/${fileId}`, body, qs);
860
+ return driveRequest(ctx, "PATCH", `/drive/v3/files/${fileId}`, body, {
861
+ supportsAllDrives: true,
862
+ fields: p.fields,
863
+ });
807
864
  },
808
865
  });
809
866
  rl.registerAction("file.delete", {
810
867
  access: "write",
811
868
  description: "Delete a file. Moves to trash by default; pass deletePermanently=true to erase.",
812
- inputSchema: {
813
- fileId: { type: "string", required: true },
814
- deletePermanently: { type: "boolean", required: false },
815
- },
869
+ inputSchema: t.Object({ fileId: Id, deletePermanently: t.Optional(t.Boolean()) }, { additionalProperties: false }),
816
870
  async execute(input, ctx) {
817
871
  const p = (input ?? {});
818
872
  if (p.deletePermanently) {
@@ -829,10 +883,7 @@ export default function googleDrive(rl) {
829
883
  rl.registerAction("file.get", {
830
884
  access: "read",
831
885
  description: "Get file metadata",
832
- inputSchema: {
833
- fileId: { type: "string", required: true },
834
- fields: { type: "string", required: false, description: "Default: '*'" },
835
- },
886
+ inputSchema: t.Object({ fileId: Id, fields: t.Optional(NonEmptyString) }, { additionalProperties: false }),
836
887
  async execute(input, ctx) {
837
888
  const p = (input ?? {});
838
889
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}`, undefined, { supportsAllDrives: true, fields: p.fields ?? "*" });
@@ -841,27 +892,44 @@ export default function googleDrive(rl) {
841
892
  rl.registerAction("file.share", {
842
893
  access: "write",
843
894
  description: "Add a permission to a file. Create one permission per call; list the existing permissions via file.listPermissions.",
844
- inputSchema: {
845
- fileId: { type: "string", required: true },
846
- role: {
847
- type: "string",
848
- required: true,
849
- description: "owner | organizer | fileOrganizer | writer | commenter | reader",
850
- },
851
- type: {
852
- type: "string",
853
- required: true,
854
- description: "user | group | domain | anyone",
855
- },
856
- emailAddress: { type: "string", required: false },
857
- domain: { type: "string", required: false },
858
- allowFileDiscovery: { type: "boolean", required: false },
859
- emailMessage: { type: "string", required: false },
860
- sendNotificationEmail: { type: "boolean", required: false },
861
- transferOwnership: { type: "boolean", required: false },
862
- moveToNewOwnersRoot: { type: "boolean", required: false },
863
- useDomainAdminAccess: { type: "boolean", required: false },
864
- },
895
+ inputSchema: t.Object({
896
+ fileId: Id,
897
+ role: PermissionRole,
898
+ type: PermissionType,
899
+ emailAddress: t.Optional(NonEmptyString),
900
+ domain: t.Optional(NonEmptyString),
901
+ allowFileDiscovery: t.Optional(t.Boolean()),
902
+ emailMessage: t.Optional(t.String()),
903
+ sendNotificationEmail: t.Optional(t.Boolean()),
904
+ transferOwnership: t.Optional(t.Boolean()),
905
+ moveToNewOwnersRoot: t.Optional(t.Boolean()),
906
+ useDomainAdminAccess: t.Optional(t.Boolean()),
907
+ }, {
908
+ additionalProperties: false,
909
+ oneOf: [
910
+ {
911
+ properties: { type: { const: "user" } },
912
+ required: ["emailAddress"],
913
+ not: { required: ["domain"] },
914
+ },
915
+ {
916
+ properties: { type: { const: "group" } },
917
+ required: ["emailAddress"],
918
+ not: { required: ["domain"] },
919
+ },
920
+ {
921
+ properties: { type: { const: "domain" } },
922
+ required: ["domain"],
923
+ not: { required: ["emailAddress"] },
924
+ },
925
+ {
926
+ properties: { type: { const: "anyone" } },
927
+ not: {
928
+ anyOf: [{ required: ["emailAddress"] }, { required: ["domain"] }],
929
+ },
930
+ },
931
+ ],
932
+ }),
865
933
  async execute(input, ctx) {
866
934
  const p = (input ?? {});
867
935
  const body = {
@@ -891,10 +959,7 @@ export default function googleDrive(rl) {
891
959
  rl.registerAction("file.listPermissions", {
892
960
  access: "read",
893
961
  description: "List permissions on a file",
894
- inputSchema: {
895
- fileId: { type: "string", required: true },
896
- useDomainAdminAccess: { type: "boolean", required: false },
897
- },
962
+ inputSchema: t.Object({ fileId: Id, useDomainAdminAccess: t.Optional(t.Boolean()) }, { additionalProperties: false }),
898
963
  async execute(input, ctx) {
899
964
  const p = (input ?? {});
900
965
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/permissions`, undefined, {
@@ -907,10 +972,7 @@ export default function googleDrive(rl) {
907
972
  rl.registerAction("file.deletePermission", {
908
973
  access: "write",
909
974
  description: "Revoke a permission on a file",
910
- inputSchema: {
911
- fileId: { type: "string", required: true },
912
- permissionId: { type: "string", required: true },
913
- },
975
+ inputSchema: t.Object({ fileId: Id, permissionId: Id }, { additionalProperties: false }),
914
976
  async execute(input, ctx) {
915
977
  const p = (input ?? {});
916
978
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/permissions/${p.permissionId}`, undefined, { supportsAllDrives: true });
@@ -921,13 +983,13 @@ export default function googleDrive(rl) {
921
983
  rl.registerAction("folder.create", {
922
984
  access: "write",
923
985
  description: "Create a folder",
924
- inputSchema: {
925
- name: { type: "string", required: false, description: 'Default: "Untitled"' },
926
- folderId: { type: "string", required: false, description: "Parent folder" },
927
- driveId: { type: "string", required: false },
928
- folderColorRgb: { type: "string", required: false, description: "Hex RGB" },
929
- fields: { type: "string", required: false, description: "Fields projection" },
930
- },
986
+ inputSchema: t.Object({
987
+ name: t.Optional(NonEmptyString),
988
+ folderId: t.Optional(Id),
989
+ driveId: t.Optional(Id),
990
+ folderColorRgb: t.Optional(t.String({ pattern: "^#?[0-9A-Fa-f]{6}$" })),
991
+ fields: t.Optional(NonEmptyString),
992
+ }, { additionalProperties: false }),
931
993
  async execute(input, ctx) {
932
994
  const p = (input ?? {});
933
995
  const body = {
@@ -947,10 +1009,7 @@ export default function googleDrive(rl) {
947
1009
  rl.registerAction("folder.delete", {
948
1010
  access: "write",
949
1011
  description: "Delete a folder. Moves to trash by default; pass deletePermanently=true to erase.",
950
- inputSchema: {
951
- folderId: { type: "string", required: true },
952
- deletePermanently: { type: "boolean", required: false },
953
- },
1012
+ inputSchema: t.Object({ folderId: Id, deletePermanently: t.Optional(t.Boolean()) }, { additionalProperties: false }),
954
1013
  async execute(input, ctx) {
955
1014
  const p = (input ?? {});
956
1015
  if (p.deletePermanently) {
@@ -967,19 +1026,44 @@ export default function googleDrive(rl) {
967
1026
  rl.registerAction("folder.share", {
968
1027
  access: "write",
969
1028
  description: "Add a permission to a folder (same shape as file.share)",
970
- inputSchema: {
971
- folderId: { type: "string", required: true },
972
- role: { type: "string", required: true },
973
- type: { type: "string", required: true },
974
- emailAddress: { type: "string", required: false },
975
- domain: { type: "string", required: false },
976
- allowFileDiscovery: { type: "boolean", required: false },
977
- emailMessage: { type: "string", required: false },
978
- sendNotificationEmail: { type: "boolean", required: false },
979
- transferOwnership: { type: "boolean", required: false },
980
- moveToNewOwnersRoot: { type: "boolean", required: false },
981
- useDomainAdminAccess: { type: "boolean", required: false },
982
- },
1029
+ inputSchema: t.Object({
1030
+ folderId: Id,
1031
+ role: PermissionRole,
1032
+ type: PermissionType,
1033
+ emailAddress: t.Optional(NonEmptyString),
1034
+ domain: t.Optional(NonEmptyString),
1035
+ allowFileDiscovery: t.Optional(t.Boolean()),
1036
+ emailMessage: t.Optional(t.String()),
1037
+ sendNotificationEmail: t.Optional(t.Boolean()),
1038
+ transferOwnership: t.Optional(t.Boolean()),
1039
+ moveToNewOwnersRoot: t.Optional(t.Boolean()),
1040
+ useDomainAdminAccess: t.Optional(t.Boolean()),
1041
+ }, {
1042
+ additionalProperties: false,
1043
+ oneOf: [
1044
+ {
1045
+ properties: { type: { const: "user" } },
1046
+ required: ["emailAddress"],
1047
+ not: { required: ["domain"] },
1048
+ },
1049
+ {
1050
+ properties: { type: { const: "group" } },
1051
+ required: ["emailAddress"],
1052
+ not: { required: ["domain"] },
1053
+ },
1054
+ {
1055
+ properties: { type: { const: "domain" } },
1056
+ required: ["domain"],
1057
+ not: { required: ["emailAddress"] },
1058
+ },
1059
+ {
1060
+ properties: { type: { const: "anyone" } },
1061
+ not: {
1062
+ anyOf: [{ required: ["emailAddress"] }, { required: ["domain"] }],
1063
+ },
1064
+ },
1065
+ ],
1066
+ }),
983
1067
  async execute(input, ctx) {
984
1068
  const p = (input ?? {});
985
1069
  const body = { role: p.role, type: p.type };
@@ -1007,39 +1091,19 @@ export default function googleDrive(rl) {
1007
1091
  rl.registerAction("fileFolder.search", {
1008
1092
  access: "read",
1009
1093
  description: "Search files and folders. `query` is passed directly; `name` wraps it as `name contains '…'`. Combine with folderId/driveId/whatToSearch/fileTypes filters.",
1010
- inputSchema: {
1011
- name: {
1012
- type: "string",
1013
- required: false,
1014
- description: "Convenience: matches `name contains '<value>'`",
1015
- },
1016
- query: {
1017
- type: "string",
1018
- required: false,
1019
- description: "Raw Drive search query; takes precedence over `name`",
1020
- },
1021
- folderId: { type: "string", required: false },
1022
- driveId: { type: "string", required: false },
1023
- whatToSearch: {
1024
- type: "string",
1025
- required: false,
1026
- description: "all (default) | files | folders",
1027
- },
1028
- fileTypes: {
1029
- type: "array",
1030
- required: false,
1031
- description: "MIME type filter (ignored when whatToSearch=folders)",
1032
- },
1033
- includeTrashed: { type: "boolean", required: false },
1034
- fields: {
1035
- type: "array",
1036
- required: false,
1037
- description: "Per-file fields to return (default: id,name)",
1038
- },
1039
- returnAll: { type: "boolean", required: false },
1040
- maxResults: { type: "number", required: false },
1041
- pageToken: { type: "string", required: false },
1042
- },
1094
+ inputSchema: t.Object({
1095
+ name: t.Optional(t.String()),
1096
+ query: t.Optional(t.String()),
1097
+ folderId: t.Optional(Id),
1098
+ driveId: t.Optional(Id),
1099
+ whatToSearch: t.Optional(stringEnum(["all", "files", "folders"])),
1100
+ fileTypes: t.Optional(StringOrStringArray),
1101
+ includeTrashed: t.Optional(t.Boolean()),
1102
+ fields: t.Optional(StringOrStringArray),
1103
+ returnAll: t.Optional(t.Boolean()),
1104
+ maxResults: t.Optional(PageSize1000),
1105
+ pageToken: t.Optional(NonEmptyString),
1106
+ }, { additionalProperties: false }),
1043
1107
  async execute(input, ctx) {
1044
1108
  const p = (input ?? {});
1045
1109
  const clauses = [];
@@ -1096,13 +1160,13 @@ export default function googleDrive(rl) {
1096
1160
  rl.registerAction("drive.create", {
1097
1161
  access: "write",
1098
1162
  description: "Create a shared drive. requestId is generated automatically.",
1099
- inputSchema: {
1100
- name: { type: "string", required: true },
1101
- colorRgb: { type: "string", required: false },
1102
- hidden: { type: "boolean", required: false },
1103
- capabilities: { type: "object", required: false },
1104
- restrictions: { type: "object", required: false },
1105
- },
1163
+ inputSchema: t.Object({
1164
+ name: NonEmptyString,
1165
+ colorRgb: t.Optional(t.String({ pattern: "^#?[0-9A-Fa-f]{6}$" })),
1166
+ hidden: t.Optional(t.Boolean()),
1167
+ capabilities: t.Optional(RawGoogleObject),
1168
+ restrictions: t.Optional(RawGoogleObject),
1169
+ }, { additionalProperties: false }),
1106
1170
  async execute(input, ctx) {
1107
1171
  const p = (input ?? {});
1108
1172
  const body = { name: p.name };
@@ -1116,10 +1180,7 @@ export default function googleDrive(rl) {
1116
1180
  rl.registerAction("drive.get", {
1117
1181
  access: "read",
1118
1182
  description: "Get a shared drive",
1119
- inputSchema: {
1120
- driveId: { type: "string", required: true },
1121
- useDomainAdminAccess: { type: "boolean", required: false },
1122
- },
1183
+ inputSchema: t.Object({ driveId: Id, useDomainAdminAccess: t.Optional(t.Boolean()) }, { additionalProperties: false }),
1123
1184
  async execute(input, ctx) {
1124
1185
  const p = (input ?? {});
1125
1186
  const qs = {};
@@ -1131,13 +1192,13 @@ export default function googleDrive(rl) {
1131
1192
  rl.registerAction("drive.list", {
1132
1193
  access: "read",
1133
1194
  description: "List shared drives",
1134
- inputSchema: {
1135
- q: { type: "string", required: false, description: "Shared-drive search syntax" },
1136
- useDomainAdminAccess: { type: "boolean", required: false },
1137
- returnAll: { type: "boolean", required: false },
1138
- maxResults: { type: "number", required: false },
1139
- pageToken: { type: "string", required: false },
1140
- },
1195
+ inputSchema: t.Object({
1196
+ q: t.Optional(t.String()),
1197
+ useDomainAdminAccess: t.Optional(t.Boolean()),
1198
+ returnAll: t.Optional(t.Boolean()),
1199
+ maxResults: t.Optional(PageSize100),
1200
+ pageToken: t.Optional(NonEmptyString),
1201
+ }, { additionalProperties: false }),
1141
1202
  async execute(input, ctx) {
1142
1203
  const p = (input ?? {});
1143
1204
  const qs = {};
@@ -1158,12 +1219,19 @@ export default function googleDrive(rl) {
1158
1219
  rl.registerAction("drive.update", {
1159
1220
  access: "write",
1160
1221
  description: "Patch a shared drive",
1161
- inputSchema: {
1162
- driveId: { type: "string", required: true },
1163
- name: { type: "string", required: false },
1164
- colorRgb: { type: "string", required: false },
1165
- restrictions: { type: "object", required: false },
1166
- },
1222
+ inputSchema: t.Object({
1223
+ driveId: Id,
1224
+ name: t.Optional(NonEmptyString),
1225
+ colorRgb: t.Optional(t.String({ pattern: "^#?[0-9A-Fa-f]{6}$" })),
1226
+ restrictions: t.Optional(RawGoogleObject),
1227
+ }, {
1228
+ additionalProperties: false,
1229
+ anyOf: [
1230
+ { required: ["name"] },
1231
+ { required: ["colorRgb"] },
1232
+ { required: ["restrictions"] },
1233
+ ],
1234
+ }),
1167
1235
  async execute(input, ctx) {
1168
1236
  const p = (input ?? {});
1169
1237
  const body = {};
@@ -1177,7 +1245,7 @@ export default function googleDrive(rl) {
1177
1245
  rl.registerAction("drive.delete", {
1178
1246
  access: "write",
1179
1247
  description: "Delete a shared drive",
1180
- inputSchema: { driveId: { type: "string", required: true } },
1248
+ inputSchema: t.Object({ driveId: Id }, { additionalProperties: false }),
1181
1249
  async execute(input, ctx) {
1182
1250
  const p = (input ?? {});
1183
1251
  await driveRequest(ctx, "DELETE", `/drive/v3/drives/${p.driveId}`);
@@ -1202,24 +1270,12 @@ export default function googleDrive(rl) {
1202
1270
  rl.registerAction("comment.list", {
1203
1271
  access: "read",
1204
1272
  description: "List all comments on a Drive file, including each comment's replies. Returns an array sorted by Drive's default (most recent first).",
1205
- inputSchema: {
1206
- fileId: { type: "string", required: true },
1207
- includeDeleted: {
1208
- type: "boolean",
1209
- required: false,
1210
- description: "Include deleted comments. Default false.",
1211
- },
1212
- pageSize: {
1213
- type: "number",
1214
- required: false,
1215
- description: "Max comments per page (Drive caps at 100).",
1216
- },
1217
- startModifiedTime: {
1218
- type: "string",
1219
- required: false,
1220
- description: "RFC 3339 timestamp; only return comments modified at or after this time.",
1221
- },
1222
- },
1273
+ inputSchema: t.Object({
1274
+ fileId: Id,
1275
+ includeDeleted: t.Optional(t.Boolean()),
1276
+ pageSize: t.Optional(PageSize100),
1277
+ startModifiedTime: t.Optional(Rfc3339Timestamp),
1278
+ }, { additionalProperties: false }),
1223
1279
  async execute(input, ctx) {
1224
1280
  const p = (input ?? {});
1225
1281
  const fileId = p.fileId;
@@ -1245,11 +1301,7 @@ export default function googleDrive(rl) {
1245
1301
  rl.registerAction("comment.get", {
1246
1302
  access: "read",
1247
1303
  description: "Fetch a single comment (with its replies) by ID.",
1248
- inputSchema: {
1249
- fileId: { type: "string", required: true },
1250
- commentId: { type: "string", required: true },
1251
- includeDeleted: { type: "boolean", required: false },
1252
- },
1304
+ inputSchema: t.Object({ fileId: Id, commentId: Id, includeDeleted: t.Optional(t.Boolean()) }, { additionalProperties: false }),
1253
1305
  async execute(input, ctx) {
1254
1306
  const p = (input ?? {});
1255
1307
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`, undefined, { fields: COMMENT_FIELDS, includeDeleted: p.includeDeleted ?? false });
@@ -1258,15 +1310,11 @@ export default function googleDrive(rl) {
1258
1310
  rl.registerAction("comment.create", {
1259
1311
  access: "write",
1260
1312
  description: "Create a new top-level comment on a file. Pass quotedFileContent.value (and optionally mimeType) to anchor the comment to a specific snippet.",
1261
- inputSchema: {
1262
- fileId: { type: "string", required: true },
1263
- content: { type: "string", required: true, description: "Comment body (plain text)." },
1264
- quotedFileContent: {
1265
- type: "object",
1266
- required: false,
1267
- description: "{ value: string, mimeType?: string }",
1268
- },
1269
- },
1313
+ inputSchema: t.Object({
1314
+ fileId: Id,
1315
+ content: NonEmptyString,
1316
+ quotedFileContent: t.Optional(QuotedFileContent),
1317
+ }, { additionalProperties: false }),
1270
1318
  async execute(input, ctx) {
1271
1319
  const p = (input ?? {});
1272
1320
  const body = { content: p.content };
@@ -1278,11 +1326,7 @@ export default function googleDrive(rl) {
1278
1326
  rl.registerAction("comment.update", {
1279
1327
  access: "write",
1280
1328
  description: "Edit the content of an existing comment. Caller must be the author or have edit rights.",
1281
- inputSchema: {
1282
- fileId: { type: "string", required: true },
1283
- commentId: { type: "string", required: true },
1284
- content: { type: "string", required: true },
1285
- },
1329
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: NonEmptyString }, { additionalProperties: false }),
1286
1330
  async execute(input, ctx) {
1287
1331
  const p = (input ?? {});
1288
1332
  return driveRequest(ctx, "PATCH", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`, { content: p.content }, { fields: COMMENT_FIELDS });
@@ -1291,10 +1335,7 @@ export default function googleDrive(rl) {
1291
1335
  rl.registerAction("comment.delete", {
1292
1336
  access: "write",
1293
1337
  description: "Soft-delete a comment.",
1294
- inputSchema: {
1295
- fileId: { type: "string", required: true },
1296
- commentId: { type: "string", required: true },
1297
- },
1338
+ inputSchema: t.Object({ fileId: Id, commentId: Id }, { additionalProperties: false }),
1298
1339
  async execute(input, ctx) {
1299
1340
  const p = (input ?? {});
1300
1341
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`);
@@ -1304,15 +1345,7 @@ export default function googleDrive(rl) {
1304
1345
  rl.registerAction("comment.resolve", {
1305
1346
  access: "write",
1306
1347
  description: "Resolve a comment thread by posting a resolution reply. `resolved` on a Comment is computed from replies; this is the canonical way to mark a thread done.",
1307
- inputSchema: {
1308
- fileId: { type: "string", required: true },
1309
- commentId: { type: "string", required: true },
1310
- content: {
1311
- type: "string",
1312
- required: false,
1313
- description: "Optional reply body. Defaults to 'Resolved.'",
1314
- },
1315
- },
1348
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1316
1349
  async execute(input, ctx) {
1317
1350
  const p = (input ?? {});
1318
1351
  return driveRequest(ctx, "POST", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies`, { action: "resolve", content: p.content ?? "Resolved." }, { fields: REPLY_FIELDS });
@@ -1321,11 +1354,7 @@ export default function googleDrive(rl) {
1321
1354
  rl.registerAction("comment.reopen", {
1322
1355
  access: "write",
1323
1356
  description: "Re-open a previously resolved comment by posting a reopen reply.",
1324
- inputSchema: {
1325
- fileId: { type: "string", required: true },
1326
- commentId: { type: "string", required: true },
1327
- content: { type: "string", required: false },
1328
- },
1357
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1329
1358
  async execute(input, ctx) {
1330
1359
  const p = (input ?? {});
1331
1360
  return driveRequest(ctx, "POST", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies`, { action: "reopen", content: p.content ?? "Reopened." }, { fields: REPLY_FIELDS });
@@ -1334,12 +1363,12 @@ export default function googleDrive(rl) {
1334
1363
  rl.registerAction("reply.list", {
1335
1364
  access: "read",
1336
1365
  description: "List replies on a specific comment.",
1337
- inputSchema: {
1338
- fileId: { type: "string", required: true },
1339
- commentId: { type: "string", required: true },
1340
- includeDeleted: { type: "boolean", required: false },
1341
- pageSize: { type: "number", required: false },
1342
- },
1366
+ inputSchema: t.Object({
1367
+ fileId: Id,
1368
+ commentId: Id,
1369
+ includeDeleted: t.Optional(t.Boolean()),
1370
+ pageSize: t.Optional(PageSize100),
1371
+ }, { additionalProperties: false }),
1343
1372
  async execute(input, ctx) {
1344
1373
  const p = (input ?? {});
1345
1374
  const out = [];
@@ -1363,16 +1392,12 @@ export default function googleDrive(rl) {
1363
1392
  rl.registerAction("reply.create", {
1364
1393
  access: "write",
1365
1394
  description: "Post a reply to a comment. Pass action: 'resolve' | 'reopen' to also flip the comment state.",
1366
- inputSchema: {
1367
- fileId: { type: "string", required: true },
1368
- commentId: { type: "string", required: true },
1369
- content: { type: "string", required: true },
1370
- action: {
1371
- type: "string",
1372
- required: false,
1373
- description: "'resolve' | 'reopen'. Omit for a plain reply.",
1374
- },
1375
- },
1395
+ inputSchema: t.Object({
1396
+ fileId: Id,
1397
+ commentId: Id,
1398
+ content: NonEmptyString,
1399
+ action: t.Optional(stringEnum(["resolve", "reopen"])),
1400
+ }, { additionalProperties: false }),
1376
1401
  async execute(input, ctx) {
1377
1402
  const p = (input ?? {});
1378
1403
  const body = { content: p.content };
@@ -1384,12 +1409,7 @@ export default function googleDrive(rl) {
1384
1409
  rl.registerAction("reply.update", {
1385
1410
  access: "write",
1386
1411
  description: "Edit the content of a reply.",
1387
- inputSchema: {
1388
- fileId: { type: "string", required: true },
1389
- commentId: { type: "string", required: true },
1390
- replyId: { type: "string", required: true },
1391
- content: { type: "string", required: true },
1392
- },
1412
+ inputSchema: t.Object({ fileId: Id, commentId: Id, replyId: Id, content: NonEmptyString }, { additionalProperties: false }),
1393
1413
  async execute(input, ctx) {
1394
1414
  const p = (input ?? {});
1395
1415
  return driveRequest(ctx, "PATCH", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies/${p.replyId}`, { content: p.content }, { fields: REPLY_FIELDS });
@@ -1398,11 +1418,7 @@ export default function googleDrive(rl) {
1398
1418
  rl.registerAction("reply.delete", {
1399
1419
  access: "write",
1400
1420
  description: "Soft-delete a reply.",
1401
- inputSchema: {
1402
- fileId: { type: "string", required: true },
1403
- commentId: { type: "string", required: true },
1404
- replyId: { type: "string", required: true },
1405
- },
1421
+ inputSchema: t.Object({ fileId: Id, commentId: Id, replyId: Id }, { additionalProperties: false }),
1406
1422
  async execute(input, ctx) {
1407
1423
  const p = (input ?? {});
1408
1424
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies/${p.replyId}`);
@@ -1430,10 +1446,7 @@ export default function googleDrive(rl) {
1430
1446
  rl.registerAction("revision.list", {
1431
1447
  access: "read",
1432
1448
  description: "List every revision Drive retains for a file, oldest first. For Office files the per-revision bytes are what `revision.download` returns.",
1433
- inputSchema: {
1434
- fileId: { type: "string", required: true },
1435
- pageSize: { type: "number", required: false, description: "Default 200; Drive caps at 1000." },
1436
- },
1449
+ inputSchema: t.Object({ fileId: Id, pageSize: t.Optional(PageSize1000) }, { additionalProperties: false }),
1437
1450
  async execute(input, ctx) {
1438
1451
  const p = (input ?? {});
1439
1452
  const fileId = p.fileId;
@@ -1457,10 +1470,7 @@ export default function googleDrive(rl) {
1457
1470
  rl.registerAction("revision.get", {
1458
1471
  access: "read",
1459
1472
  description: "Fetch metadata for a single revision.",
1460
- inputSchema: {
1461
- fileId: { type: "string", required: true },
1462
- revisionId: { type: "string", required: true },
1463
- },
1473
+ inputSchema: t.Object({ fileId: Id, revisionId: Id }, { additionalProperties: false }),
1464
1474
  async execute(input, ctx) {
1465
1475
  const p = (input ?? {});
1466
1476
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/revisions/${p.revisionId}`, undefined, { fields: REVISION_FIELDS });
@@ -1469,15 +1479,7 @@ export default function googleDrive(rl) {
1469
1479
  rl.registerAction("revision.download", {
1470
1480
  access: "write",
1471
1481
  description: "Download the bytes of a specific revision. Pass savePath to write to disk and get back the path; otherwise returns { contentBase64, mimeType, size }. This is the recovery path when an in-place file.update has overwritten in-document comments on an Office file.",
1472
- inputSchema: {
1473
- fileId: { type: "string", required: true },
1474
- revisionId: { type: "string", required: true },
1475
- savePath: {
1476
- type: "string",
1477
- required: false,
1478
- description: "Filesystem path to write the bytes to. If omitted, returns base64.",
1479
- },
1480
- },
1482
+ inputSchema: t.Object({ fileId: Id, revisionId: Id, savePath: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1481
1483
  async execute(input, ctx) {
1482
1484
  const p = (input ?? {});
1483
1485
  const fileId = p.fileId;
@@ -1509,14 +1511,22 @@ export default function googleDrive(rl) {
1509
1511
  rl.registerAction("revision.update", {
1510
1512
  access: "write",
1511
1513
  description: "Patch revision metadata. The most useful flag is keepForever — without it Drive can garbage-collect revisions after 30 days / 100 versions on Office files. Set keepForever=true on the head revision before any risky file.update so prior bytes are guaranteed recoverable.",
1512
- inputSchema: {
1513
- fileId: { type: "string", required: true },
1514
- revisionId: { type: "string", required: true },
1515
- keepForever: { type: "boolean", required: false },
1516
- published: { type: "boolean", required: false },
1517
- publishAuto: { type: "boolean", required: false },
1518
- publishedOutsideDomain: { type: "boolean", required: false },
1519
- },
1514
+ inputSchema: t.Object({
1515
+ fileId: Id,
1516
+ revisionId: Id,
1517
+ keepForever: t.Optional(t.Boolean()),
1518
+ published: t.Optional(t.Boolean()),
1519
+ publishAuto: t.Optional(t.Boolean()),
1520
+ publishedOutsideDomain: t.Optional(t.Boolean()),
1521
+ }, {
1522
+ additionalProperties: false,
1523
+ anyOf: [
1524
+ { required: ["keepForever"] },
1525
+ { required: ["published"] },
1526
+ { required: ["publishAuto"] },
1527
+ { required: ["publishedOutsideDomain"] },
1528
+ ],
1529
+ }),
1520
1530
  async execute(input, ctx) {
1521
1531
  const p = (input ?? {});
1522
1532
  const body = {};
@@ -1538,10 +1548,7 @@ export default function googleDrive(rl) {
1538
1548
  rl.registerAction("revision.delete", {
1539
1549
  access: "write",
1540
1550
  description: "Permanently delete a revision (head revision cannot be deleted).",
1541
- inputSchema: {
1542
- fileId: { type: "string", required: true },
1543
- revisionId: { type: "string", required: true },
1544
- },
1551
+ inputSchema: t.Object({ fileId: Id, revisionId: Id }, { additionalProperties: false }),
1545
1552
  async execute(input, ctx) {
1546
1553
  const p = (input ?? {});
1547
1554
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/revisions/${p.revisionId}`);
@@ -1551,15 +1558,7 @@ export default function googleDrive(rl) {
1551
1558
  rl.registerAction("revision.restore", {
1552
1559
  access: "write",
1553
1560
  description: "Restore an older revision as the head of the file. Downloads the revision bytes and re-uploads them via multipart so the head moves to that content. Drive's REST API has no native restore endpoint for binary files; this performs the equivalent in two calls. Returns the new head file resource.",
1554
- inputSchema: {
1555
- fileId: { type: "string", required: true },
1556
- revisionId: { type: "string", required: true, description: "Revision to restore as head." },
1557
- mimeType: {
1558
- type: "string",
1559
- required: false,
1560
- description: "Override mime type for the re-upload. Defaults to the revision's mime type.",
1561
- },
1562
- },
1561
+ inputSchema: t.Object({ fileId: Id, revisionId: Id, mimeType: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1563
1562
  async execute(input, ctx) {
1564
1563
  const p = (input ?? {});
1565
1564
  const fileId = p.fileId;
@@ -1604,10 +1603,10 @@ export default function googleDrive(rl) {
1604
1603
  rl.registerAction("changes.getStartPageToken", {
1605
1604
  access: "read",
1606
1605
  description: "Get the current Drive change-feed start page token. Use as the seed `pageToken` for the first `changes.list` call.",
1607
- inputSchema: {
1608
- driveId: { type: "string", required: false, description: "Shared-drive id; omit for the user's My Drive corpus." },
1609
- supportsAllDrives: { type: "boolean", required: false, default: true },
1610
- },
1606
+ inputSchema: t.Object({
1607
+ driveId: t.Optional(Id),
1608
+ supportsAllDrives: t.Optional(t.Boolean()),
1609
+ }, { additionalProperties: false }),
1611
1610
  async execute(input, ctx) {
1612
1611
  const p = (input ?? {});
1613
1612
  return driveRequest(ctx, "GET", `/drive/v3/changes/startPageToken`, undefined, {
@@ -1619,17 +1618,17 @@ export default function googleDrive(rl) {
1619
1618
  rl.registerAction("changes.list", {
1620
1619
  access: "read",
1621
1620
  description: "List changes to files and Shared Drives since the given `pageToken`. Returns `{ changes, newStartPageToken, nextPageToken? }`. Drive does not surface comment-level changes here — use this for file metadata/content changes; pair with comments.list for review activity.",
1622
- inputSchema: {
1623
- pageToken: { type: "string", required: true, description: "Token from `changes.getStartPageToken` or a prior `nextPageToken`." },
1624
- driveId: { type: "string", required: false },
1625
- spaces: { type: "string", required: false, description: "drive | photos | appDataFolder. Default drive." },
1626
- includeRemoved: { type: "boolean", required: false },
1627
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1628
- supportsAllDrives: { type: "boolean", required: false, default: true },
1629
- restrictToMyDrive: { type: "boolean", required: false },
1630
- pageSize: { type: "number", required: false },
1631
- fields: { type: "string", required: false },
1632
- },
1621
+ inputSchema: t.Object({
1622
+ pageToken: NonEmptyString,
1623
+ driveId: t.Optional(Id),
1624
+ spaces: t.Optional(NonEmptyString),
1625
+ includeRemoved: t.Optional(t.Boolean()),
1626
+ includeItemsFromAllDrives: t.Optional(t.Boolean()),
1627
+ supportsAllDrives: t.Optional(t.Boolean()),
1628
+ restrictToMyDrive: t.Optional(t.Boolean()),
1629
+ pageSize: t.Optional(PageSize1000),
1630
+ fields: t.Optional(NonEmptyString),
1631
+ }, { additionalProperties: false }),
1633
1632
  async execute(input, ctx) {
1634
1633
  const p = (input ?? {});
1635
1634
  return driveRequest(ctx, "GET", `/drive/v3/changes`, undefined, {
@@ -1649,16 +1648,16 @@ export default function googleDrive(rl) {
1649
1648
  rl.registerAction("changes.watch", {
1650
1649
  access: "write",
1651
1650
  description: "Subscribe to push notifications on the change feed. Drive will POST to `address` whenever a change in this corpus is recorded. Returns a channel resource; pair with `channels.stop` (`changes.stop`) when done.",
1652
- inputSchema: {
1653
- pageToken: { type: "string", required: true },
1654
- address: { type: "string", required: true, description: "HTTPS URL Drive will POST notifications to." },
1655
- channelId: { type: "string", required: false, description: "Caller-chosen UUID. Auto-generated when omitted." },
1656
- token: { type: "string", required: false, description: "Optional opaque token Drive echoes on each delivery." },
1657
- expiration: { type: "number", required: false, description: "Unix ms at which Drive should expire the channel. Defaults ~1 hour." },
1658
- driveId: { type: "string", required: false },
1659
- supportsAllDrives: { type: "boolean", required: false, default: true },
1660
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1661
- },
1651
+ inputSchema: t.Object({
1652
+ pageToken: NonEmptyString,
1653
+ address: t.String({ pattern: "^https://" }),
1654
+ channelId: t.Optional(Id),
1655
+ token: t.Optional(t.String()),
1656
+ expiration: t.Optional(t.Integer({ minimum: 0 })),
1657
+ driveId: t.Optional(Id),
1658
+ supportsAllDrives: t.Optional(t.Boolean()),
1659
+ includeItemsFromAllDrives: t.Optional(t.Boolean()),
1660
+ }, { additionalProperties: false }),
1662
1661
  async execute(input, ctx) {
1663
1662
  const p = (input ?? {});
1664
1663
  const body = {
@@ -1681,10 +1680,7 @@ export default function googleDrive(rl) {
1681
1680
  rl.registerAction("changes.stop", {
1682
1681
  access: "write",
1683
1682
  description: "Stop a previously-subscribed change channel. Pass the same `channelId` and `resourceId` returned by `changes.watch`.",
1684
- inputSchema: {
1685
- channelId: { type: "string", required: true },
1686
- resourceId: { type: "string", required: true },
1687
- },
1683
+ inputSchema: t.Object({ channelId: Id, resourceId: Id }, { additionalProperties: false }),
1688
1684
  async execute(input, ctx) {
1689
1685
  const p = (input ?? {});
1690
1686
  await driveRequest(ctx, "POST", `/drive/v3/channels/stop`, { id: p.channelId, resourceId: p.resourceId });
@@ -1700,16 +1696,26 @@ export default function googleDrive(rl) {
1700
1696
  rl.registerAction("permission.update", {
1701
1697
  access: "write",
1702
1698
  description: "Patch an existing file/folder permission. Use to change the role on an existing share, set an expiration time, or transfer ownership.",
1703
- inputSchema: {
1704
- fileId: { type: "string", required: true },
1705
- permissionId: { type: "string", required: true },
1706
- role: { type: "string", required: false, description: "owner | organizer | fileOrganizer | writer | commenter | reader" },
1707
- expirationTime: { type: "string", required: false, description: "RFC 3339; only valid for writer/commenter/reader on My Drive files." },
1708
- transferOwnership: { type: "boolean", required: false },
1709
- removeExpiration: { type: "boolean", required: false },
1710
- useDomainAdminAccess: { type: "boolean", required: false },
1711
- supportsAllDrives: { type: "boolean", required: false, default: true },
1712
- },
1699
+ inputSchema: t.Object({
1700
+ fileId: Id,
1701
+ permissionId: Id,
1702
+ role: t.Optional(PermissionRole),
1703
+ expirationTime: t.Optional(Rfc3339Timestamp),
1704
+ transferOwnership: t.Optional(t.Boolean()),
1705
+ removeExpiration: t.Optional(t.Boolean()),
1706
+ useDomainAdminAccess: t.Optional(t.Boolean()),
1707
+ supportsAllDrives: t.Optional(t.Boolean()),
1708
+ }, {
1709
+ additionalProperties: false,
1710
+ anyOf: [
1711
+ { required: ["role"] },
1712
+ { required: ["expirationTime"] },
1713
+ {
1714
+ properties: { removeExpiration: { const: true } },
1715
+ required: ["removeExpiration"],
1716
+ },
1717
+ ],
1718
+ }),
1713
1719
  async execute(input, ctx) {
1714
1720
  const p = (input ?? {});
1715
1721
  const body = {};
@@ -1739,10 +1745,7 @@ export default function googleDrive(rl) {
1739
1745
  rl.registerAction("accessProposal.list", {
1740
1746
  access: "read",
1741
1747
  description: "List access proposals (Request-access entries) on a file.",
1742
- inputSchema: {
1743
- fileId: { type: "string", required: true },
1744
- pageSize: { type: "number", required: false },
1745
- },
1748
+ inputSchema: t.Object({ fileId: Id, pageSize: t.Optional(PageSize100) }, { additionalProperties: false }),
1746
1749
  async execute(input, ctx) {
1747
1750
  const p = (input ?? {});
1748
1751
  const out = [];
@@ -1759,14 +1762,23 @@ export default function googleDrive(rl) {
1759
1762
  rl.registerAction("accessProposal.resolve", {
1760
1763
  access: "write",
1761
1764
  description: "Resolve an access proposal. `action` is one of 'ACCEPT' or 'DENY'. When accepting, pass `role` (default 'reader') to grant.",
1762
- inputSchema: {
1763
- fileId: { type: "string", required: true },
1764
- proposalId: { type: "string", required: true },
1765
- action: { type: "string", required: true, description: "'ACCEPT' | 'DENY'" },
1766
- role: { type: "string", required: false, description: "Role to grant on ACCEPT. Default 'reader'." },
1767
- view: { type: "string", required: false, description: "Optional Drive scope view (e.g. 'published')." },
1768
- sendNotification: { type: "boolean", required: false },
1769
- },
1765
+ inputSchema: t.Object({
1766
+ fileId: Id,
1767
+ proposalId: Id,
1768
+ action: stringEnum(["ACCEPT", "DENY"]),
1769
+ role: t.Optional(AccessProposalRole),
1770
+ view: t.Optional(NonEmptyString),
1771
+ sendNotification: t.Optional(t.Boolean()),
1772
+ }, {
1773
+ additionalProperties: false,
1774
+ oneOf: [
1775
+ { properties: { action: { const: "ACCEPT" } } },
1776
+ {
1777
+ properties: { action: { const: "DENY" } },
1778
+ not: { anyOf: [{ required: ["role"] }, { required: ["view"] }] },
1779
+ },
1780
+ ],
1781
+ }),
1770
1782
  async execute(input, ctx) {
1771
1783
  const p = (input ?? {});
1772
1784
  const body = { action: p.action };
@@ -1784,9 +1796,7 @@ export default function googleDrive(rl) {
1784
1796
  rl.registerAction("about.get", {
1785
1797
  access: "read",
1786
1798
  description: "Current user info: storage quota, export formats, max upload size, importable mime types. Useful for healthchecks and conversion planning.",
1787
- inputSchema: {
1788
- fields: { type: "string", required: false },
1789
- },
1799
+ inputSchema: t.Object({ fields: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1790
1800
  async execute(input, ctx) {
1791
1801
  const p = (input ?? {});
1792
1802
  return driveRequest(ctx, "GET", `/drive/v3/about`, undefined, {
@@ -1799,15 +1809,11 @@ export default function googleDrive(rl) {
1799
1809
  rl.registerAction("file.export", {
1800
1810
  access: "write",
1801
1811
  description: "Export a Google-native file (Doc/Sheet/Slide/Drawing/Form) to a non-native mimeType. Wrapper around the export endpoint; pass `savePath` to write to disk and get the path back, otherwise returns base64.",
1802
- inputSchema: {
1803
- fileId: { type: "string", required: true },
1804
- mimeType: {
1805
- type: "string",
1806
- required: true,
1807
- description: "Target export mime, e.g. application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf, text/plain, text/csv.",
1808
- },
1809
- savePath: { type: "string", required: false },
1810
- },
1812
+ inputSchema: t.Object({
1813
+ fileId: Id,
1814
+ mimeType: NonEmptyString,
1815
+ savePath: t.Optional(NonEmptyString),
1816
+ }, { additionalProperties: false }),
1811
1817
  async execute(input, ctx) {
1812
1818
  const p = (input ?? {});
1813
1819
  const token = await accessToken(ctx);
@@ -1830,19 +1836,19 @@ export default function googleDrive(rl) {
1830
1836
  rl.registerAction("file.list", {
1831
1837
  access: "read",
1832
1838
  description: "Raw Drive `files.list`. Pass Drive search-syntax `q` and any combination of corpora / driveId / spaces / orderBy. `fileFolder.search` is the friendlier wrapper; reach for `file.list` only when you need the unwrapped surface.",
1833
- inputSchema: {
1834
- q: { type: "string", required: false },
1835
- corpora: { type: "string", required: false, description: "user | drive | allDrives" },
1836
- driveId: { type: "string", required: false },
1837
- spaces: { type: "string", required: false },
1838
- orderBy: { type: "string", required: false },
1839
- pageSize: { type: "number", required: false },
1840
- pageToken: { type: "string", required: false },
1841
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1842
- supportsAllDrives: { type: "boolean", required: false, default: true },
1843
- fields: { type: "string", required: false },
1844
- returnAll: { type: "boolean", required: false, description: "If true, follows pageToken until exhausted and returns the concatenated file list." },
1845
- },
1839
+ inputSchema: t.Object({
1840
+ q: t.Optional(t.String()),
1841
+ corpora: t.Optional(stringEnum(["user", "domain", "drive", "allDrives"])),
1842
+ driveId: t.Optional(Id),
1843
+ spaces: t.Optional(NonEmptyString),
1844
+ orderBy: t.Optional(NonEmptyString),
1845
+ pageSize: t.Optional(PageSize1000),
1846
+ pageToken: t.Optional(NonEmptyString),
1847
+ includeItemsFromAllDrives: t.Optional(t.Boolean()),
1848
+ supportsAllDrives: t.Optional(t.Boolean()),
1849
+ fields: t.Optional(NonEmptyString),
1850
+ returnAll: t.Optional(t.Boolean()),
1851
+ }, { additionalProperties: false }),
1846
1852
  async execute(input, ctx) {
1847
1853
  const p = (input ?? {});
1848
1854
  const baseQs = {