runline 0.11.6 → 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.
Files changed (256) hide show
  1. package/dist/commands/actions.js +3 -1
  2. package/dist/core/engine.js +3 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/plugin/api.d.ts +2 -1
  5. package/dist/plugin/types.d.ts +2 -0
  6. package/dist/plugins/_shared/googleSchemas.js +23 -0
  7. package/dist/plugins/actionNetwork/src/index.js +23 -0
  8. package/dist/plugins/activeCampaign/src/index.js +48 -0
  9. package/dist/plugins/adalo/src/index.js +5 -0
  10. package/dist/plugins/affinity/src/index.js +16 -0
  11. package/dist/plugins/agileCrm/src/index.js +15 -0
  12. package/dist/plugins/airtable/src/index.js +10 -0
  13. package/dist/plugins/airtop/src/index.js +24 -0
  14. package/dist/plugins/apiTemplateIo/src/index.js +4 -0
  15. package/dist/plugins/asana/src/index.js +22 -0
  16. package/dist/plugins/autopilot/src/index.js +11 -0
  17. package/dist/plugins/bambooHr/src/index.js +11 -0
  18. package/dist/plugins/bannerbear/src/index.js +4 -0
  19. package/dist/plugins/baserow/src/index.js +8 -0
  20. package/dist/plugins/beeminder/src/index.js +18 -0
  21. package/dist/plugins/bitly/src/index.js +3 -0
  22. package/dist/plugins/bitwarden/src/index.js +19 -0
  23. package/dist/plugins/box/src/index.js +11 -0
  24. package/dist/plugins/brandfetch/src/index.js +5 -0
  25. package/dist/plugins/brevo/src/index.js +15 -0
  26. package/dist/plugins/bubble/src/index.js +5 -0
  27. package/dist/plugins/chargebee/src/index.js +5 -0
  28. package/dist/plugins/circleci/src/index.js +3 -0
  29. package/dist/plugins/ciscoWebex/src/index.js +10 -0
  30. package/dist/plugins/clearbit/src/index.js +3 -0
  31. package/dist/plugins/clickup/src/index.js +61 -0
  32. package/dist/plugins/clockify/src/index.js +25 -0
  33. package/dist/plugins/cloudflare/src/index.js +4 -0
  34. package/dist/plugins/cockpit/src/index.js +5 -0
  35. package/dist/plugins/coda/src/index.js +18 -0
  36. package/dist/plugins/coingecko/src/index.js +10 -0
  37. package/dist/plugins/contentful/src/index.js +7 -0
  38. package/dist/plugins/convertkit/src/index.js +16 -0
  39. package/dist/plugins/copper/src/index.js +7 -0
  40. package/dist/plugins/cortex/src/index.js +4 -0
  41. package/dist/plugins/currents/src/index.js +22 -0
  42. package/dist/plugins/customerIo/src/index.js +9 -0
  43. package/dist/plugins/databricks/src/index.js +34 -0
  44. package/dist/plugins/deepl/src/index.js +2 -0
  45. package/dist/plugins/demio/src/index.js +4 -0
  46. package/dist/plugins/dhl/src/index.js +1 -0
  47. package/dist/plugins/discord/src/index.js +13 -0
  48. package/dist/plugins/discourse/src/index.js +16 -0
  49. package/dist/plugins/disqus/src/index.js +4 -0
  50. package/dist/plugins/docker/src/index.js +5 -0
  51. package/dist/plugins/drift/src/index.js +5 -0
  52. package/dist/plugins/dropbox/src/index.js +9 -0
  53. package/dist/plugins/dropcontact/src/index.js +2 -0
  54. package/dist/plugins/egoi/src/index.js +4 -0
  55. package/dist/plugins/elasticsearch/src/index.js +9 -0
  56. package/dist/plugins/emelia/src/index.js +9 -0
  57. package/dist/plugins/erpnext/src/index.js +5 -0
  58. package/dist/plugins/facebookGraph/src/index.js +1 -0
  59. package/dist/plugins/freshdesk/src/index.js +10 -0
  60. package/dist/plugins/freshservice/src/index.js +7 -0
  61. package/dist/plugins/freshworksCrm/src/index.js +7 -0
  62. package/dist/plugins/getresponse/src/index.js +5 -0
  63. package/dist/plugins/ghost/src/index.js +5 -0
  64. package/dist/plugins/github/src/index.js +37 -0
  65. package/dist/plugins/gitlab/src/index.js +17 -0
  66. package/dist/plugins/gmail/src/index.js +200 -184
  67. package/dist/plugins/gong/src/index.js +4 -0
  68. package/dist/plugins/googleAppsScript/src/index.js +98 -43
  69. package/dist/plugins/googleCalendar/src/index.js +329 -250
  70. package/dist/plugins/googleContacts/src/index.js +144 -145
  71. package/dist/plugins/googleDocs/src/documents.js +31 -38
  72. package/dist/plugins/googleDocs/src/formatting.js +51 -47
  73. package/dist/plugins/googleDocs/src/images.js +23 -26
  74. package/dist/plugins/googleDocs/src/shared.js +55 -0
  75. package/dist/plugins/googleDocs/src/structure.js +111 -123
  76. package/dist/plugins/googleDocs/src/tables.js +103 -154
  77. package/dist/plugins/googleDocs/src/tabs.js +27 -23
  78. package/dist/plugins/googleDocs/src/text.js +106 -136
  79. package/dist/plugins/googleDrive/src/index.js +461 -408
  80. package/dist/plugins/googleImage/src/index.js +14 -16
  81. package/dist/plugins/googleSheets/src/index.js +527 -339
  82. package/dist/plugins/googleSlides/src/index.js +92 -72
  83. package/dist/plugins/googleTasks/src/index.js +94 -92
  84. package/dist/plugins/gotify/src/index.js +3 -0
  85. package/dist/plugins/gotowebinar/src/index.js +20 -0
  86. package/dist/plugins/grafana/src/index.js +17 -0
  87. package/dist/plugins/graphql/src/index.js +2 -0
  88. package/dist/plugins/grist/src/index.js +4 -0
  89. package/dist/plugins/hackernews/src/index.js +6 -0
  90. package/dist/plugins/halopsa/src/index.js +5 -0
  91. package/dist/plugins/harvest/src/index.js +14 -0
  92. package/dist/plugins/helpscout/src/index.js +13 -0
  93. package/dist/plugins/highlevel/src/index.js +17 -0
  94. package/dist/plugins/homeAssistant/src/index.js +13 -0
  95. package/dist/plugins/hubspot/src/index.js +14 -0
  96. package/dist/plugins/humanticAi/src/index.js +3 -0
  97. package/dist/plugins/hunter/src/index.js +3 -0
  98. package/dist/plugins/intercom/src/index.js +10 -0
  99. package/dist/plugins/iterable/src/index.js +6 -0
  100. package/dist/plugins/jenkins/src/index.js +11 -0
  101. package/dist/plugins/jira/src/index.js +16 -0
  102. package/dist/plugins/keap/src/index.js +28 -0
  103. package/dist/plugins/kobotoolbox/src/index.js +17 -0
  104. package/dist/plugins/lemlist/src/index.js +15 -0
  105. package/dist/plugins/linear/src/attachments.js +6 -0
  106. package/dist/plugins/linear/src/comments.js +5 -0
  107. package/dist/plugins/linear/src/cycles.js +2 -0
  108. package/dist/plugins/linear/src/initiatives.js +5 -0
  109. package/dist/plugins/linear/src/issues.js +14 -0
  110. package/dist/plugins/linear/src/labels.js +5 -0
  111. package/dist/plugins/linear/src/organization.js +1 -0
  112. package/dist/plugins/linear/src/projects.js +11 -0
  113. package/dist/plugins/linear/src/shared.js +2 -0
  114. package/dist/plugins/linear/src/states.js +2 -0
  115. package/dist/plugins/linear/src/teams.js +3 -0
  116. package/dist/plugins/linear/src/users.js +2 -0
  117. package/dist/plugins/linear/src/views.js +4 -0
  118. package/dist/plugins/linear/src/webhooks.js +4 -0
  119. package/dist/plugins/lingvanex/src/index.js +1 -0
  120. package/dist/plugins/linkedin/src/index.js +1 -0
  121. package/dist/plugins/lonescale/src/index.js +4 -0
  122. package/dist/plugins/magento/src/index.js +15 -0
  123. package/dist/plugins/mailcheck/src/index.js +1 -0
  124. package/dist/plugins/mailchimp/src/index.js +14 -0
  125. package/dist/plugins/mailerlite/src/index.js +4 -0
  126. package/dist/plugins/mailgun/src/index.js +1 -0
  127. package/dist/plugins/mailjet/src/index.js +3 -0
  128. package/dist/plugins/mandrill/src/index.js +2 -0
  129. package/dist/plugins/marketstack/src/index.js +3 -0
  130. package/dist/plugins/matrix/src/index.js +10 -0
  131. package/dist/plugins/mattermost/src/index.js +19 -0
  132. package/dist/plugins/mautic/src/index.js +20 -0
  133. package/dist/plugins/medium/src/index.js +3 -0
  134. package/dist/plugins/messagebird/src/index.js +2 -0
  135. package/dist/plugins/metabase/src/index.js +10 -0
  136. package/dist/plugins/microsoftCalendar/src/index.js +2 -0
  137. package/dist/plugins/microsoftFiles/src/index.js +5 -0
  138. package/dist/plugins/microsoftMail/src/index.js +4 -0
  139. package/dist/plugins/misp/src/index.js +44 -0
  140. package/dist/plugins/mocean/src/index.js +2 -0
  141. package/dist/plugins/monday/src/index.js +18 -0
  142. package/dist/plugins/monicaCrm/src/index.js +5 -0
  143. package/dist/plugins/msg91/src/index.js +1 -0
  144. package/dist/plugins/nasa/src/index.js +7 -0
  145. package/dist/plugins/netlify/src/index.js +7 -0
  146. package/dist/plugins/netscalerAdc/src/index.js +3 -0
  147. package/dist/plugins/nextcloud/src/index.js +13 -0
  148. package/dist/plugins/nocodb/src/index.js +5 -0
  149. package/dist/plugins/node/src/index.js +38 -0
  150. package/dist/plugins/notion/src/index.js +14 -0
  151. package/dist/plugins/npm/src/index.js +5 -0
  152. package/dist/plugins/odoo/src/index.js +6 -0
  153. package/dist/plugins/okta/src/index.js +5 -0
  154. package/dist/plugins/oneSimpleApi/src/index.js +10 -0
  155. package/dist/plugins/onfleet/src/index.js +24 -0
  156. package/dist/plugins/openThesaurus/src/index.js +1 -0
  157. package/dist/plugins/openai/src/index.js +1 -0
  158. package/dist/plugins/openweathermap/src/index.js +2 -0
  159. package/dist/plugins/oura/src/index.js +2 -0
  160. package/dist/plugins/paddle/src/index.js +9 -0
  161. package/dist/plugins/pagerduty/src/index.js +9 -0
  162. package/dist/plugins/parallel/src/index.js +1 -0
  163. package/dist/plugins/paypal/src/index.js +4 -0
  164. package/dist/plugins/peekalink/src/index.js +2 -0
  165. package/dist/plugins/phantombuster/src/index.js +5 -0
  166. package/dist/plugins/philipsHue/src/index.js +4 -0
  167. package/dist/plugins/pipedrive/src/index.js +14 -0
  168. package/dist/plugins/plivo/src/index.js +3 -0
  169. package/dist/plugins/postbin/src/index.js +6 -0
  170. package/dist/plugins/posthog/src/index.js +5 -0
  171. package/dist/plugins/profitwell/src/index.js +2 -0
  172. package/dist/plugins/pushbullet/src/index.js +4 -0
  173. package/dist/plugins/pushcut/src/index.js +1 -0
  174. package/dist/plugins/pushover/src/index.js +1 -0
  175. package/dist/plugins/quickbase/src/index.js +8 -0
  176. package/dist/plugins/quickbooks/src/index.js +5 -0
  177. package/dist/plugins/quickchart/src/index.js +1 -0
  178. package/dist/plugins/raindrop/src/index.js +13 -0
  179. package/dist/plugins/recraft/src/index.js +1 -0
  180. package/dist/plugins/reddit/src/index.js +10 -0
  181. package/dist/plugins/replicate/src/index.js +1 -0
  182. package/dist/plugins/rocketchat/src/index.js +1 -0
  183. package/dist/plugins/rundeck/src/index.js +2 -0
  184. package/dist/plugins/salesforce/src/metadata.js +4 -0
  185. package/dist/plugins/salesforce/src/query.js +5 -0
  186. package/dist/plugins/salesforce/src/sobjects.js +12 -0
  187. package/dist/plugins/salesmate/src/index.js +5 -0
  188. package/dist/plugins/securityScorecard/src/index.js +14 -0
  189. package/dist/plugins/segment/src/index.js +4 -0
  190. package/dist/plugins/sendgrid/src/index.js +10 -0
  191. package/dist/plugins/sendy/src/index.js +6 -0
  192. package/dist/plugins/sentry/src/index.js +24 -0
  193. package/dist/plugins/servicenow/src/index.js +10 -0
  194. package/dist/plugins/shiftLabs/src/index.js +6 -0
  195. package/dist/plugins/shiftLabs/src/issues.js +4 -0
  196. package/dist/plugins/shiftLabs/src/pages.js +10 -0
  197. package/dist/plugins/shiftLabs/src/shared.js +2 -0
  198. package/dist/plugins/shiftLabs/src/transcription.js +203 -0
  199. package/dist/plugins/shopify/src/index.js +10 -0
  200. package/dist/plugins/signl4/src/index.js +2 -0
  201. package/dist/plugins/slack/src/index.js +38 -0
  202. package/dist/plugins/sms77/src/index.js +2 -0
  203. package/dist/plugins/splunk/src/index.js +16 -0
  204. package/dist/plugins/spotify/src/index.js +30 -0
  205. package/dist/plugins/stackby/src/index.js +4 -0
  206. package/dist/plugins/steel/src/browser.js +7 -0
  207. package/dist/plugins/steel/src/captchas.js +3 -0
  208. package/dist/plugins/steel/src/credentials.js +4 -0
  209. package/dist/plugins/steel/src/extensions.js +5 -0
  210. package/dist/plugins/steel/src/files.js +10 -0
  211. package/dist/plugins/steel/src/profiles.js +4 -0
  212. package/dist/plugins/steel/src/sessions.js +11 -0
  213. package/dist/plugins/storyblok/src/index.js +7 -0
  214. package/dist/plugins/strapi/src/index.js +5 -0
  215. package/dist/plugins/strava/src/index.js +6 -0
  216. package/dist/plugins/stripe/src/index.js +20 -0
  217. package/dist/plugins/supabase/src/index.js +5 -0
  218. package/dist/plugins/syncromsp/src/index.js +20 -0
  219. package/dist/plugins/tapfiliate/src/index.js +11 -0
  220. package/dist/plugins/telegram/src/index.js +21 -0
  221. package/dist/plugins/thehive/src/index.js +23 -0
  222. package/dist/plugins/thehiveProject/src/index.js +31 -0
  223. package/dist/plugins/todoist/src/index.js +31 -0
  224. package/dist/plugins/together/src/index.js +1 -0
  225. package/dist/plugins/travisci/src/index.js +5 -0
  226. package/dist/plugins/trello/src/index.js +37 -0
  227. package/dist/plugins/twake/src/index.js +1 -0
  228. package/dist/plugins/twilio/src/index.js +2 -0
  229. package/dist/plugins/twist/src/index.js +22 -0
  230. package/dist/plugins/twitter/src/index.js +8 -0
  231. package/dist/plugins/unleashedSoftware/src/index.js +3 -0
  232. package/dist/plugins/uplead/src/index.js +2 -0
  233. package/dist/plugins/uproc/src/index.js +1 -0
  234. package/dist/plugins/uptimerobot/src/index.js +22 -0
  235. package/dist/plugins/urlscanio/src/index.js +3 -0
  236. package/dist/plugins/vercel/src/account.js +1 -0
  237. package/dist/plugins/vercel/src/deployments.js +5 -0
  238. package/dist/plugins/vercel/src/env.js +4 -0
  239. package/dist/plugins/vercel/src/projects.js +2 -0
  240. package/dist/plugins/vercel/src/shared.js +1 -0
  241. package/dist/plugins/vero/src/index.js +6 -0
  242. package/dist/plugins/vonage/src/index.js +1 -0
  243. package/dist/plugins/wekan/src/index.js +24 -0
  244. package/dist/plugins/woocommerce/src/index.js +5 -0
  245. package/dist/plugins/wordpress/src/index.js +10 -0
  246. package/dist/plugins/xai/src/index.js +1 -0
  247. package/dist/plugins/xero/src/index.js +8 -0
  248. package/dist/plugins/yourls/src/index.js +3 -0
  249. package/dist/plugins/zammad/src/index.js +12 -0
  250. package/dist/plugins/zendesk/src/index.js +18 -0
  251. package/dist/plugins/zoho/src/index.js +6 -0
  252. package/dist/plugins/zoom/src/index.js +5 -0
  253. package/dist/plugins/zulip/src/index.js +15 -0
  254. package/dist/sdk.d.ts +2 -1
  255. package/dist/sdk.js +1 -0
  256. package/package.json +1 -1
@@ -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",
@@ -435,34 +465,30 @@ export default function googleDrive(rl) {
435
465
  });
436
466
  // ── File ──────────────────────────────────────────────
437
467
  rl.registerAction("file.upload", {
468
+ access: "write",
438
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.",
439
- inputSchema: {
440
- name: { type: "string", required: false, description: "File name in Drive" },
441
- folderId: { type: "string", required: false, description: "Parent folder (default: root)" },
442
- driveId: { type: "string", required: false, description: "Target shared drive" },
443
- mimeType: { type: "string", required: false },
444
- contentBase64: { type: "string", required: false },
445
- contentPath: { type: "string", required: false, description: "Local filesystem path" },
446
- content: { type: "string", required: false, description: "Inline utf-8 content" },
447
- properties: {
448
- type: "object",
449
- required: false,
450
- description: "Public key-value properties",
451
- },
452
- appProperties: {
453
- type: "object",
454
- required: false,
455
- description: "App-private key-value properties",
456
- },
457
- keepRevisionForever: { type: "boolean", required: false },
458
- ocrLanguage: { type: "string", required: false },
459
- useContentAsIndexableText: { type: "boolean", required: false },
460
- fields: {
461
- type: "string",
462
- required: false,
463
- description: "Fields projection (default: '*' returns everything)",
464
- },
465
- },
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
+ }),
466
492
  async execute(input, ctx) {
467
493
  const p = (input ?? {});
468
494
  const content = resolveContent(p);
@@ -496,16 +522,17 @@ export default function googleDrive(rl) {
496
522
  },
497
523
  });
498
524
  rl.registerAction("file.createFromText", {
525
+ access: "write",
499
526
  description: "Create a text file from inline content. Set convertToGoogleDocument=true to convert to a Google Doc.",
500
- inputSchema: {
501
- name: { type: "string", required: false, description: 'Default: "Untitled"' },
502
- content: { type: "string", required: true },
503
- folderId: { type: "string", required: false },
504
- driveId: { type: "string", required: false },
505
- convertToGoogleDocument: { type: "boolean", required: false },
506
- properties: { type: "object", required: false },
507
- appProperties: { type: "object", required: false },
508
- },
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 }),
509
536
  async execute(input, ctx) {
510
537
  const p = (input ?? {});
511
538
  const name = p.name || "Untitled";
@@ -569,20 +596,13 @@ export default function googleDrive(rl) {
569
596
  },
570
597
  });
571
598
  rl.registerAction("file.download", {
599
+ access: "write",
572
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.",
573
- inputSchema: {
574
- fileId: { type: "string", required: true },
575
- savePath: {
576
- type: "string",
577
- required: false,
578
- description: "Write bytes to this filesystem path instead of returning base64",
579
- },
580
- googleDocFormat: {
581
- type: "string",
582
- required: false,
583
- description: "Export MIME type for Google Docs (default: DOCX / PPTX / XLSX / image/jpeg by type)",
584
- },
585
- },
601
+ inputSchema: t.Object({
602
+ fileId: Id,
603
+ savePath: t.Optional(NonEmptyString),
604
+ googleDocFormat: t.Optional(NonEmptyString),
605
+ }, { additionalProperties: false }),
586
606
  async execute(input, ctx) {
587
607
  const p = (input ?? {});
588
608
  const fileId = p.fileId;
@@ -633,19 +653,16 @@ export default function googleDrive(rl) {
633
653
  },
634
654
  });
635
655
  rl.registerAction("file.copy", {
656
+ access: "write",
636
657
  description: "Copy a file",
637
- inputSchema: {
638
- fileId: { type: "string", required: true },
639
- name: { type: "string", required: false, description: 'Default: "Copy of {original}"' },
640
- folderId: {
641
- type: "string",
642
- required: false,
643
- description: "If omitted, copy stays in the same folder(s)",
644
- },
645
- driveId: { type: "string", required: false },
646
- description: { type: "string", required: false },
647
- copyRequiresWriterPermission: { type: "boolean", required: false },
648
- },
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 }),
649
666
  async execute(input, ctx) {
650
667
  const p = (input ?? {});
651
668
  const body = {};
@@ -663,12 +680,13 @@ export default function googleDrive(rl) {
663
680
  },
664
681
  });
665
682
  rl.registerAction("file.move", {
683
+ access: "write",
666
684
  description: "Move a file to another folder. Resolves current parents and swaps them in a single PATCH.",
667
- inputSchema: {
668
- fileId: { type: "string", required: true },
669
- folderId: { type: "string", required: false, description: "Destination folder" },
670
- driveId: { type: "string", required: false, description: "Destination shared drive" },
671
- },
685
+ inputSchema: t.Object({
686
+ fileId: Id,
687
+ folderId: t.Optional(Id),
688
+ driveId: t.Optional(Id),
689
+ }, { additionalProperties: false }),
672
690
  async execute(input, ctx) {
673
691
  const p = (input ?? {});
674
692
  const current = (await driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}`, undefined, { fields: "parents", supportsAllDrives: true }));
@@ -682,21 +700,51 @@ export default function googleDrive(rl) {
682
700
  },
683
701
  });
684
702
  rl.registerAction("file.update", {
703
+ access: "write",
685
704
  description: "Patch file metadata and/or replace its bytes. Supply content{Base64,Path} to update bytes.",
686
- inputSchema: {
687
- fileId: { type: "string", required: true },
688
- name: { type: "string", required: false },
689
- mimeType: { type: "string", required: false },
690
- trashed: { type: "boolean", required: false, description: "Move to trash" },
691
- properties: { type: "object", required: false },
692
- appProperties: { type: "object", required: false },
693
- contentBase64: { type: "string", required: false },
694
- contentPath: { type: "string", required: false },
695
- keepRevisionForever: { type: "boolean", required: false },
696
- ocrLanguage: { type: "string", required: false },
697
- useContentAsIndexableText: { type: "boolean", required: false },
698
- fields: { type: "string", required: false, description: "Fields projection" },
699
- },
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
+ }),
700
748
  async execute(input, ctx) {
701
749
  const p = (input ?? {});
702
750
  const fileId = p.fileId;
@@ -711,6 +759,15 @@ export default function googleDrive(rl) {
711
759
  const url = new URL(`${API_BASE}/upload/drive/v3/files/${fileId}`);
712
760
  url.searchParams.set("uploadType", "media");
713
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
+ }
714
771
  const res = await fetch(url.toString(), {
715
772
  method: "PATCH",
716
773
  headers: {
@@ -729,6 +786,15 @@ export default function googleDrive(rl) {
729
786
  const initUrl = new URL(`${API_BASE}/upload/drive/v3/files/${fileId}`);
730
787
  initUrl.searchParams.set("uploadType", "resumable");
731
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
+ }
732
798
  const initRes = await fetch(initUrl.toString(), {
733
799
  method: "PATCH",
734
800
  headers: {
@@ -779,33 +845,28 @@ export default function googleDrive(rl) {
779
845
  body.name = p.name;
780
846
  if (p.mimeType !== undefined)
781
847
  body.mimeType = p.mimeType;
848
+ if (p.trashed !== undefined)
849
+ body.trashed = p.trashed;
782
850
  if (p.properties !== undefined)
783
851
  body.properties = p.properties;
784
852
  if (p.appProperties !== undefined)
785
853
  body.appProperties = p.appProperties;
786
- const qs = { supportsAllDrives: true };
787
- if (p.trashed !== undefined)
788
- qs.trashed = p.trashed;
789
- if (p.keepRevisionForever)
790
- qs.keepRevisionForever = p.keepRevisionForever;
791
- if (p.ocrLanguage)
792
- qs.ocrLanguage = p.ocrLanguage;
793
- if (p.useContentAsIndexableText)
794
- qs.useContentAsIndexableText = p.useContentAsIndexableText;
795
- if (p.fields)
796
- qs.fields = p.fields;
797
- 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
+ }
798
858
  return { id: fileId, success: true };
799
859
  }
800
- 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
+ });
801
864
  },
802
865
  });
803
866
  rl.registerAction("file.delete", {
867
+ access: "write",
804
868
  description: "Delete a file. Moves to trash by default; pass deletePermanently=true to erase.",
805
- inputSchema: {
806
- fileId: { type: "string", required: true },
807
- deletePermanently: { type: "boolean", required: false },
808
- },
869
+ inputSchema: t.Object({ fileId: Id, deletePermanently: t.Optional(t.Boolean()) }, { additionalProperties: false }),
809
870
  async execute(input, ctx) {
810
871
  const p = (input ?? {});
811
872
  if (p.deletePermanently) {
@@ -820,39 +881,55 @@ export default function googleDrive(rl) {
820
881
  },
821
882
  });
822
883
  rl.registerAction("file.get", {
884
+ access: "read",
823
885
  description: "Get file metadata",
824
- inputSchema: {
825
- fileId: { type: "string", required: true },
826
- fields: { type: "string", required: false, description: "Default: '*'" },
827
- },
886
+ inputSchema: t.Object({ fileId: Id, fields: t.Optional(NonEmptyString) }, { additionalProperties: false }),
828
887
  async execute(input, ctx) {
829
888
  const p = (input ?? {});
830
889
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}`, undefined, { supportsAllDrives: true, fields: p.fields ?? "*" });
831
890
  },
832
891
  });
833
892
  rl.registerAction("file.share", {
893
+ access: "write",
834
894
  description: "Add a permission to a file. Create one permission per call; list the existing permissions via file.listPermissions.",
835
- inputSchema: {
836
- fileId: { type: "string", required: true },
837
- role: {
838
- type: "string",
839
- required: true,
840
- description: "owner | organizer | fileOrganizer | writer | commenter | reader",
841
- },
842
- type: {
843
- type: "string",
844
- required: true,
845
- description: "user | group | domain | anyone",
846
- },
847
- emailAddress: { type: "string", required: false },
848
- domain: { type: "string", required: false },
849
- allowFileDiscovery: { type: "boolean", required: false },
850
- emailMessage: { type: "string", required: false },
851
- sendNotificationEmail: { type: "boolean", required: false },
852
- transferOwnership: { type: "boolean", required: false },
853
- moveToNewOwnersRoot: { type: "boolean", required: false },
854
- useDomainAdminAccess: { type: "boolean", required: false },
855
- },
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
+ }),
856
933
  async execute(input, ctx) {
857
934
  const p = (input ?? {});
858
935
  const body = {
@@ -880,11 +957,9 @@ export default function googleDrive(rl) {
880
957
  },
881
958
  });
882
959
  rl.registerAction("file.listPermissions", {
960
+ access: "read",
883
961
  description: "List permissions on a file",
884
- inputSchema: {
885
- fileId: { type: "string", required: true },
886
- useDomainAdminAccess: { type: "boolean", required: false },
887
- },
962
+ inputSchema: t.Object({ fileId: Id, useDomainAdminAccess: t.Optional(t.Boolean()) }, { additionalProperties: false }),
888
963
  async execute(input, ctx) {
889
964
  const p = (input ?? {});
890
965
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/permissions`, undefined, {
@@ -895,11 +970,9 @@ export default function googleDrive(rl) {
895
970
  },
896
971
  });
897
972
  rl.registerAction("file.deletePermission", {
973
+ access: "write",
898
974
  description: "Revoke a permission on a file",
899
- inputSchema: {
900
- fileId: { type: "string", required: true },
901
- permissionId: { type: "string", required: true },
902
- },
975
+ inputSchema: t.Object({ fileId: Id, permissionId: Id }, { additionalProperties: false }),
903
976
  async execute(input, ctx) {
904
977
  const p = (input ?? {});
905
978
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/permissions/${p.permissionId}`, undefined, { supportsAllDrives: true });
@@ -908,14 +981,15 @@ export default function googleDrive(rl) {
908
981
  });
909
982
  // ── Folder ────────────────────────────────────────────
910
983
  rl.registerAction("folder.create", {
984
+ access: "write",
911
985
  description: "Create a folder",
912
- inputSchema: {
913
- name: { type: "string", required: false, description: 'Default: "Untitled"' },
914
- folderId: { type: "string", required: false, description: "Parent folder" },
915
- driveId: { type: "string", required: false },
916
- folderColorRgb: { type: "string", required: false, description: "Hex RGB" },
917
- fields: { type: "string", required: false, description: "Fields projection" },
918
- },
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 }),
919
993
  async execute(input, ctx) {
920
994
  const p = (input ?? {});
921
995
  const body = {
@@ -933,11 +1007,9 @@ export default function googleDrive(rl) {
933
1007
  },
934
1008
  });
935
1009
  rl.registerAction("folder.delete", {
1010
+ access: "write",
936
1011
  description: "Delete a folder. Moves to trash by default; pass deletePermanently=true to erase.",
937
- inputSchema: {
938
- folderId: { type: "string", required: true },
939
- deletePermanently: { type: "boolean", required: false },
940
- },
1012
+ inputSchema: t.Object({ folderId: Id, deletePermanently: t.Optional(t.Boolean()) }, { additionalProperties: false }),
941
1013
  async execute(input, ctx) {
942
1014
  const p = (input ?? {});
943
1015
  if (p.deletePermanently) {
@@ -952,20 +1024,46 @@ export default function googleDrive(rl) {
952
1024
  },
953
1025
  });
954
1026
  rl.registerAction("folder.share", {
1027
+ access: "write",
955
1028
  description: "Add a permission to a folder (same shape as file.share)",
956
- inputSchema: {
957
- folderId: { type: "string", required: true },
958
- role: { type: "string", required: true },
959
- type: { type: "string", required: true },
960
- emailAddress: { type: "string", required: false },
961
- domain: { type: "string", required: false },
962
- allowFileDiscovery: { type: "boolean", required: false },
963
- emailMessage: { type: "string", required: false },
964
- sendNotificationEmail: { type: "boolean", required: false },
965
- transferOwnership: { type: "boolean", required: false },
966
- moveToNewOwnersRoot: { type: "boolean", required: false },
967
- useDomainAdminAccess: { type: "boolean", required: false },
968
- },
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
+ }),
969
1067
  async execute(input, ctx) {
970
1068
  const p = (input ?? {});
971
1069
  const body = { role: p.role, type: p.type };
@@ -991,40 +1089,21 @@ export default function googleDrive(rl) {
991
1089
  });
992
1090
  // ── File / folder search ──────────────────────────────
993
1091
  rl.registerAction("fileFolder.search", {
1092
+ access: "read",
994
1093
  description: "Search files and folders. `query` is passed directly; `name` wraps it as `name contains '…'`. Combine with folderId/driveId/whatToSearch/fileTypes filters.",
995
- inputSchema: {
996
- name: {
997
- type: "string",
998
- required: false,
999
- description: "Convenience: matches `name contains '<value>'`",
1000
- },
1001
- query: {
1002
- type: "string",
1003
- required: false,
1004
- description: "Raw Drive search query; takes precedence over `name`",
1005
- },
1006
- folderId: { type: "string", required: false },
1007
- driveId: { type: "string", required: false },
1008
- whatToSearch: {
1009
- type: "string",
1010
- required: false,
1011
- description: "all (default) | files | folders",
1012
- },
1013
- fileTypes: {
1014
- type: "array",
1015
- required: false,
1016
- description: "MIME type filter (ignored when whatToSearch=folders)",
1017
- },
1018
- includeTrashed: { type: "boolean", required: false },
1019
- fields: {
1020
- type: "array",
1021
- required: false,
1022
- description: "Per-file fields to return (default: id,name)",
1023
- },
1024
- returnAll: { type: "boolean", required: false },
1025
- maxResults: { type: "number", required: false },
1026
- pageToken: { type: "string", required: false },
1027
- },
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 }),
1028
1107
  async execute(input, ctx) {
1029
1108
  const p = (input ?? {});
1030
1109
  const clauses = [];
@@ -1079,14 +1158,15 @@ export default function googleDrive(rl) {
1079
1158
  });
1080
1159
  // ── Shared drive ──────────────────────────────────────
1081
1160
  rl.registerAction("drive.create", {
1161
+ access: "write",
1082
1162
  description: "Create a shared drive. requestId is generated automatically.",
1083
- inputSchema: {
1084
- name: { type: "string", required: true },
1085
- colorRgb: { type: "string", required: false },
1086
- hidden: { type: "boolean", required: false },
1087
- capabilities: { type: "object", required: false },
1088
- restrictions: { type: "object", required: false },
1089
- },
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 }),
1090
1170
  async execute(input, ctx) {
1091
1171
  const p = (input ?? {});
1092
1172
  const body = { name: p.name };
@@ -1098,11 +1178,9 @@ export default function googleDrive(rl) {
1098
1178
  },
1099
1179
  });
1100
1180
  rl.registerAction("drive.get", {
1181
+ access: "read",
1101
1182
  description: "Get a shared drive",
1102
- inputSchema: {
1103
- driveId: { type: "string", required: true },
1104
- useDomainAdminAccess: { type: "boolean", required: false },
1105
- },
1183
+ inputSchema: t.Object({ driveId: Id, useDomainAdminAccess: t.Optional(t.Boolean()) }, { additionalProperties: false }),
1106
1184
  async execute(input, ctx) {
1107
1185
  const p = (input ?? {});
1108
1186
  const qs = {};
@@ -1112,14 +1190,15 @@ export default function googleDrive(rl) {
1112
1190
  },
1113
1191
  });
1114
1192
  rl.registerAction("drive.list", {
1193
+ access: "read",
1115
1194
  description: "List shared drives",
1116
- inputSchema: {
1117
- q: { type: "string", required: false, description: "Shared-drive search syntax" },
1118
- useDomainAdminAccess: { type: "boolean", required: false },
1119
- returnAll: { type: "boolean", required: false },
1120
- maxResults: { type: "number", required: false },
1121
- pageToken: { type: "string", required: false },
1122
- },
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 }),
1123
1202
  async execute(input, ctx) {
1124
1203
  const p = (input ?? {});
1125
1204
  const qs = {};
@@ -1138,13 +1217,21 @@ export default function googleDrive(rl) {
1138
1217
  },
1139
1218
  });
1140
1219
  rl.registerAction("drive.update", {
1220
+ access: "write",
1141
1221
  description: "Patch a shared drive",
1142
- inputSchema: {
1143
- driveId: { type: "string", required: true },
1144
- name: { type: "string", required: false },
1145
- colorRgb: { type: "string", required: false },
1146
- restrictions: { type: "object", required: false },
1147
- },
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
+ }),
1148
1235
  async execute(input, ctx) {
1149
1236
  const p = (input ?? {});
1150
1237
  const body = {};
@@ -1156,8 +1243,9 @@ export default function googleDrive(rl) {
1156
1243
  },
1157
1244
  });
1158
1245
  rl.registerAction("drive.delete", {
1246
+ access: "write",
1159
1247
  description: "Delete a shared drive",
1160
- inputSchema: { driveId: { type: "string", required: true } },
1248
+ inputSchema: t.Object({ driveId: Id }, { additionalProperties: false }),
1161
1249
  async execute(input, ctx) {
1162
1250
  const p = (input ?? {});
1163
1251
  await driveRequest(ctx, "DELETE", `/drive/v3/drives/${p.driveId}`);
@@ -1180,25 +1268,14 @@ export default function googleDrive(rl) {
1180
1268
  "author(displayName,emailAddress),action,content,htmlContent";
1181
1269
  const REPLY_LIST_FIELDS = `kind,nextPageToken,replies(${REPLY_FIELDS})`;
1182
1270
  rl.registerAction("comment.list", {
1271
+ access: "read",
1183
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).",
1184
- inputSchema: {
1185
- fileId: { type: "string", required: true },
1186
- includeDeleted: {
1187
- type: "boolean",
1188
- required: false,
1189
- description: "Include deleted comments. Default false.",
1190
- },
1191
- pageSize: {
1192
- type: "number",
1193
- required: false,
1194
- description: "Max comments per page (Drive caps at 100).",
1195
- },
1196
- startModifiedTime: {
1197
- type: "string",
1198
- required: false,
1199
- description: "RFC 3339 timestamp; only return comments modified at or after this time.",
1200
- },
1201
- },
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 }),
1202
1279
  async execute(input, ctx) {
1203
1280
  const p = (input ?? {});
1204
1281
  const fileId = p.fileId;
@@ -1222,28 +1299,22 @@ export default function googleDrive(rl) {
1222
1299
  },
1223
1300
  });
1224
1301
  rl.registerAction("comment.get", {
1302
+ access: "read",
1225
1303
  description: "Fetch a single comment (with its replies) by ID.",
1226
- inputSchema: {
1227
- fileId: { type: "string", required: true },
1228
- commentId: { type: "string", required: true },
1229
- includeDeleted: { type: "boolean", required: false },
1230
- },
1304
+ inputSchema: t.Object({ fileId: Id, commentId: Id, includeDeleted: t.Optional(t.Boolean()) }, { additionalProperties: false }),
1231
1305
  async execute(input, ctx) {
1232
1306
  const p = (input ?? {});
1233
1307
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`, undefined, { fields: COMMENT_FIELDS, includeDeleted: p.includeDeleted ?? false });
1234
1308
  },
1235
1309
  });
1236
1310
  rl.registerAction("comment.create", {
1311
+ access: "write",
1237
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.",
1238
- inputSchema: {
1239
- fileId: { type: "string", required: true },
1240
- content: { type: "string", required: true, description: "Comment body (plain text)." },
1241
- quotedFileContent: {
1242
- type: "object",
1243
- required: false,
1244
- description: "{ value: string, mimeType?: string }",
1245
- },
1246
- },
1313
+ inputSchema: t.Object({
1314
+ fileId: Id,
1315
+ content: NonEmptyString,
1316
+ quotedFileContent: t.Optional(QuotedFileContent),
1317
+ }, { additionalProperties: false }),
1247
1318
  async execute(input, ctx) {
1248
1319
  const p = (input ?? {});
1249
1320
  const body = { content: p.content };
@@ -1253,23 +1324,18 @@ export default function googleDrive(rl) {
1253
1324
  },
1254
1325
  });
1255
1326
  rl.registerAction("comment.update", {
1327
+ access: "write",
1256
1328
  description: "Edit the content of an existing comment. Caller must be the author or have edit rights.",
1257
- inputSchema: {
1258
- fileId: { type: "string", required: true },
1259
- commentId: { type: "string", required: true },
1260
- content: { type: "string", required: true },
1261
- },
1329
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: NonEmptyString }, { additionalProperties: false }),
1262
1330
  async execute(input, ctx) {
1263
1331
  const p = (input ?? {});
1264
1332
  return driveRequest(ctx, "PATCH", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`, { content: p.content }, { fields: COMMENT_FIELDS });
1265
1333
  },
1266
1334
  });
1267
1335
  rl.registerAction("comment.delete", {
1336
+ access: "write",
1268
1337
  description: "Soft-delete a comment.",
1269
- inputSchema: {
1270
- fileId: { type: "string", required: true },
1271
- commentId: { type: "string", required: true },
1272
- },
1338
+ inputSchema: t.Object({ fileId: Id, commentId: Id }, { additionalProperties: false }),
1273
1339
  async execute(input, ctx) {
1274
1340
  const p = (input ?? {});
1275
1341
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/comments/${p.commentId}`);
@@ -1277,41 +1343,32 @@ export default function googleDrive(rl) {
1277
1343
  },
1278
1344
  });
1279
1345
  rl.registerAction("comment.resolve", {
1346
+ access: "write",
1280
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.",
1281
- inputSchema: {
1282
- fileId: { type: "string", required: true },
1283
- commentId: { type: "string", required: true },
1284
- content: {
1285
- type: "string",
1286
- required: false,
1287
- description: "Optional reply body. Defaults to 'Resolved.'",
1288
- },
1289
- },
1348
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1290
1349
  async execute(input, ctx) {
1291
1350
  const p = (input ?? {});
1292
1351
  return driveRequest(ctx, "POST", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies`, { action: "resolve", content: p.content ?? "Resolved." }, { fields: REPLY_FIELDS });
1293
1352
  },
1294
1353
  });
1295
1354
  rl.registerAction("comment.reopen", {
1355
+ access: "write",
1296
1356
  description: "Re-open a previously resolved comment by posting a reopen reply.",
1297
- inputSchema: {
1298
- fileId: { type: "string", required: true },
1299
- commentId: { type: "string", required: true },
1300
- content: { type: "string", required: false },
1301
- },
1357
+ inputSchema: t.Object({ fileId: Id, commentId: Id, content: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1302
1358
  async execute(input, ctx) {
1303
1359
  const p = (input ?? {});
1304
1360
  return driveRequest(ctx, "POST", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies`, { action: "reopen", content: p.content ?? "Reopened." }, { fields: REPLY_FIELDS });
1305
1361
  },
1306
1362
  });
1307
1363
  rl.registerAction("reply.list", {
1364
+ access: "read",
1308
1365
  description: "List replies on a specific comment.",
1309
- inputSchema: {
1310
- fileId: { type: "string", required: true },
1311
- commentId: { type: "string", required: true },
1312
- includeDeleted: { type: "boolean", required: false },
1313
- pageSize: { type: "number", required: false },
1314
- },
1366
+ inputSchema: t.Object({
1367
+ fileId: Id,
1368
+ commentId: Id,
1369
+ includeDeleted: t.Optional(t.Boolean()),
1370
+ pageSize: t.Optional(PageSize100),
1371
+ }, { additionalProperties: false }),
1315
1372
  async execute(input, ctx) {
1316
1373
  const p = (input ?? {});
1317
1374
  const out = [];
@@ -1333,17 +1390,14 @@ export default function googleDrive(rl) {
1333
1390
  },
1334
1391
  });
1335
1392
  rl.registerAction("reply.create", {
1393
+ access: "write",
1336
1394
  description: "Post a reply to a comment. Pass action: 'resolve' | 'reopen' to also flip the comment state.",
1337
- inputSchema: {
1338
- fileId: { type: "string", required: true },
1339
- commentId: { type: "string", required: true },
1340
- content: { type: "string", required: true },
1341
- action: {
1342
- type: "string",
1343
- required: false,
1344
- description: "'resolve' | 'reopen'. Omit for a plain reply.",
1345
- },
1346
- },
1395
+ inputSchema: t.Object({
1396
+ fileId: Id,
1397
+ commentId: Id,
1398
+ content: NonEmptyString,
1399
+ action: t.Optional(stringEnum(["resolve", "reopen"])),
1400
+ }, { additionalProperties: false }),
1347
1401
  async execute(input, ctx) {
1348
1402
  const p = (input ?? {});
1349
1403
  const body = { content: p.content };
@@ -1353,25 +1407,18 @@ export default function googleDrive(rl) {
1353
1407
  },
1354
1408
  });
1355
1409
  rl.registerAction("reply.update", {
1410
+ access: "write",
1356
1411
  description: "Edit the content of a reply.",
1357
- inputSchema: {
1358
- fileId: { type: "string", required: true },
1359
- commentId: { type: "string", required: true },
1360
- replyId: { type: "string", required: true },
1361
- content: { type: "string", required: true },
1362
- },
1412
+ inputSchema: t.Object({ fileId: Id, commentId: Id, replyId: Id, content: NonEmptyString }, { additionalProperties: false }),
1363
1413
  async execute(input, ctx) {
1364
1414
  const p = (input ?? {});
1365
1415
  return driveRequest(ctx, "PATCH", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies/${p.replyId}`, { content: p.content }, { fields: REPLY_FIELDS });
1366
1416
  },
1367
1417
  });
1368
1418
  rl.registerAction("reply.delete", {
1419
+ access: "write",
1369
1420
  description: "Soft-delete a reply.",
1370
- inputSchema: {
1371
- fileId: { type: "string", required: true },
1372
- commentId: { type: "string", required: true },
1373
- replyId: { type: "string", required: true },
1374
- },
1421
+ inputSchema: t.Object({ fileId: Id, commentId: Id, replyId: Id }, { additionalProperties: false }),
1375
1422
  async execute(input, ctx) {
1376
1423
  const p = (input ?? {});
1377
1424
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/comments/${p.commentId}/replies/${p.replyId}`);
@@ -1397,11 +1444,9 @@ export default function googleDrive(rl) {
1397
1444
  "published,publishAuto,publishedOutsideDomain,publishedLink";
1398
1445
  const REVISION_LIST_FIELDS = `kind,nextPageToken,revisions(${REVISION_FIELDS})`;
1399
1446
  rl.registerAction("revision.list", {
1447
+ access: "read",
1400
1448
  description: "List every revision Drive retains for a file, oldest first. For Office files the per-revision bytes are what `revision.download` returns.",
1401
- inputSchema: {
1402
- fileId: { type: "string", required: true },
1403
- pageSize: { type: "number", required: false, description: "Default 200; Drive caps at 1000." },
1404
- },
1449
+ inputSchema: t.Object({ fileId: Id, pageSize: t.Optional(PageSize1000) }, { additionalProperties: false }),
1405
1450
  async execute(input, ctx) {
1406
1451
  const p = (input ?? {});
1407
1452
  const fileId = p.fileId;
@@ -1423,27 +1468,18 @@ export default function googleDrive(rl) {
1423
1468
  },
1424
1469
  });
1425
1470
  rl.registerAction("revision.get", {
1471
+ access: "read",
1426
1472
  description: "Fetch metadata for a single revision.",
1427
- inputSchema: {
1428
- fileId: { type: "string", required: true },
1429
- revisionId: { type: "string", required: true },
1430
- },
1473
+ inputSchema: t.Object({ fileId: Id, revisionId: Id }, { additionalProperties: false }),
1431
1474
  async execute(input, ctx) {
1432
1475
  const p = (input ?? {});
1433
1476
  return driveRequest(ctx, "GET", `/drive/v3/files/${p.fileId}/revisions/${p.revisionId}`, undefined, { fields: REVISION_FIELDS });
1434
1477
  },
1435
1478
  });
1436
1479
  rl.registerAction("revision.download", {
1480
+ access: "write",
1437
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.",
1438
- inputSchema: {
1439
- fileId: { type: "string", required: true },
1440
- revisionId: { type: "string", required: true },
1441
- savePath: {
1442
- type: "string",
1443
- required: false,
1444
- description: "Filesystem path to write the bytes to. If omitted, returns base64.",
1445
- },
1446
- },
1482
+ inputSchema: t.Object({ fileId: Id, revisionId: Id, savePath: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1447
1483
  async execute(input, ctx) {
1448
1484
  const p = (input ?? {});
1449
1485
  const fileId = p.fileId;
@@ -1473,15 +1509,24 @@ export default function googleDrive(rl) {
1473
1509
  },
1474
1510
  });
1475
1511
  rl.registerAction("revision.update", {
1512
+ access: "write",
1476
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.",
1477
- inputSchema: {
1478
- fileId: { type: "string", required: true },
1479
- revisionId: { type: "string", required: true },
1480
- keepForever: { type: "boolean", required: false },
1481
- published: { type: "boolean", required: false },
1482
- publishAuto: { type: "boolean", required: false },
1483
- publishedOutsideDomain: { type: "boolean", required: false },
1484
- },
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
+ }),
1485
1530
  async execute(input, ctx) {
1486
1531
  const p = (input ?? {});
1487
1532
  const body = {};
@@ -1501,11 +1546,9 @@ export default function googleDrive(rl) {
1501
1546
  },
1502
1547
  });
1503
1548
  rl.registerAction("revision.delete", {
1549
+ access: "write",
1504
1550
  description: "Permanently delete a revision (head revision cannot be deleted).",
1505
- inputSchema: {
1506
- fileId: { type: "string", required: true },
1507
- revisionId: { type: "string", required: true },
1508
- },
1551
+ inputSchema: t.Object({ fileId: Id, revisionId: Id }, { additionalProperties: false }),
1509
1552
  async execute(input, ctx) {
1510
1553
  const p = (input ?? {});
1511
1554
  await driveRequest(ctx, "DELETE", `/drive/v3/files/${p.fileId}/revisions/${p.revisionId}`);
@@ -1513,16 +1556,9 @@ export default function googleDrive(rl) {
1513
1556
  },
1514
1557
  });
1515
1558
  rl.registerAction("revision.restore", {
1559
+ access: "write",
1516
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.",
1517
- inputSchema: {
1518
- fileId: { type: "string", required: true },
1519
- revisionId: { type: "string", required: true, description: "Revision to restore as head." },
1520
- mimeType: {
1521
- type: "string",
1522
- required: false,
1523
- description: "Override mime type for the re-upload. Defaults to the revision's mime type.",
1524
- },
1525
- },
1561
+ inputSchema: t.Object({ fileId: Id, revisionId: Id, mimeType: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1526
1562
  async execute(input, ctx) {
1527
1563
  const p = (input ?? {});
1528
1564
  const fileId = p.fileId;
@@ -1565,11 +1601,12 @@ export default function googleDrive(rl) {
1565
1601
  // per file. With them, a sensor can wake on any file change in the user's
1566
1602
  // corpus or in a specific shared drive.
1567
1603
  rl.registerAction("changes.getStartPageToken", {
1604
+ access: "read",
1568
1605
  description: "Get the current Drive change-feed start page token. Use as the seed `pageToken` for the first `changes.list` call.",
1569
- inputSchema: {
1570
- driveId: { type: "string", required: false, description: "Shared-drive id; omit for the user's My Drive corpus." },
1571
- supportsAllDrives: { type: "boolean", required: false, default: true },
1572
- },
1606
+ inputSchema: t.Object({
1607
+ driveId: t.Optional(Id),
1608
+ supportsAllDrives: t.Optional(t.Boolean()),
1609
+ }, { additionalProperties: false }),
1573
1610
  async execute(input, ctx) {
1574
1611
  const p = (input ?? {});
1575
1612
  return driveRequest(ctx, "GET", `/drive/v3/changes/startPageToken`, undefined, {
@@ -1579,18 +1616,19 @@ export default function googleDrive(rl) {
1579
1616
  },
1580
1617
  });
1581
1618
  rl.registerAction("changes.list", {
1619
+ access: "read",
1582
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.",
1583
- inputSchema: {
1584
- pageToken: { type: "string", required: true, description: "Token from `changes.getStartPageToken` or a prior `nextPageToken`." },
1585
- driveId: { type: "string", required: false },
1586
- spaces: { type: "string", required: false, description: "drive | photos | appDataFolder. Default drive." },
1587
- includeRemoved: { type: "boolean", required: false },
1588
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1589
- supportsAllDrives: { type: "boolean", required: false, default: true },
1590
- restrictToMyDrive: { type: "boolean", required: false },
1591
- pageSize: { type: "number", required: false },
1592
- fields: { type: "string", required: false },
1593
- },
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 }),
1594
1632
  async execute(input, ctx) {
1595
1633
  const p = (input ?? {});
1596
1634
  return driveRequest(ctx, "GET", `/drive/v3/changes`, undefined, {
@@ -1608,17 +1646,18 @@ export default function googleDrive(rl) {
1608
1646
  },
1609
1647
  });
1610
1648
  rl.registerAction("changes.watch", {
1649
+ access: "write",
1611
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.",
1612
- inputSchema: {
1613
- pageToken: { type: "string", required: true },
1614
- address: { type: "string", required: true, description: "HTTPS URL Drive will POST notifications to." },
1615
- channelId: { type: "string", required: false, description: "Caller-chosen UUID. Auto-generated when omitted." },
1616
- token: { type: "string", required: false, description: "Optional opaque token Drive echoes on each delivery." },
1617
- expiration: { type: "number", required: false, description: "Unix ms at which Drive should expire the channel. Defaults ~1 hour." },
1618
- driveId: { type: "string", required: false },
1619
- supportsAllDrives: { type: "boolean", required: false, default: true },
1620
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1621
- },
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 }),
1622
1661
  async execute(input, ctx) {
1623
1662
  const p = (input ?? {});
1624
1663
  const body = {
@@ -1639,11 +1678,9 @@ export default function googleDrive(rl) {
1639
1678
  },
1640
1679
  });
1641
1680
  rl.registerAction("changes.stop", {
1681
+ access: "write",
1642
1682
  description: "Stop a previously-subscribed change channel. Pass the same `channelId` and `resourceId` returned by `changes.watch`.",
1643
- inputSchema: {
1644
- channelId: { type: "string", required: true },
1645
- resourceId: { type: "string", required: true },
1646
- },
1683
+ inputSchema: t.Object({ channelId: Id, resourceId: Id }, { additionalProperties: false }),
1647
1684
  async execute(input, ctx) {
1648
1685
  const p = (input ?? {});
1649
1686
  await driveRequest(ctx, "POST", `/drive/v3/channels/stop`, { id: p.channelId, resourceId: p.resourceId });
@@ -1657,17 +1694,28 @@ export default function googleDrive(rl) {
1657
1694
  // commenter to writer (or expire a permission) without re-sharing, you
1658
1695
  // need the PATCH endpoint.
1659
1696
  rl.registerAction("permission.update", {
1697
+ access: "write",
1660
1698
  description: "Patch an existing file/folder permission. Use to change the role on an existing share, set an expiration time, or transfer ownership.",
1661
- inputSchema: {
1662
- fileId: { type: "string", required: true },
1663
- permissionId: { type: "string", required: true },
1664
- role: { type: "string", required: false, description: "owner | organizer | fileOrganizer | writer | commenter | reader" },
1665
- expirationTime: { type: "string", required: false, description: "RFC 3339; only valid for writer/commenter/reader on My Drive files." },
1666
- transferOwnership: { type: "boolean", required: false },
1667
- removeExpiration: { type: "boolean", required: false },
1668
- useDomainAdminAccess: { type: "boolean", required: false },
1669
- supportsAllDrives: { type: "boolean", required: false, default: true },
1670
- },
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
+ }),
1671
1719
  async execute(input, ctx) {
1672
1720
  const p = (input ?? {});
1673
1721
  const body = {};
@@ -1695,11 +1743,9 @@ export default function googleDrive(rl) {
1695
1743
  // owner can resolve (accept and grant, or deny). These actions let the
1696
1744
  // agent triage and resolve those requests programmatically.
1697
1745
  rl.registerAction("accessProposal.list", {
1746
+ access: "read",
1698
1747
  description: "List access proposals (Request-access entries) on a file.",
1699
- inputSchema: {
1700
- fileId: { type: "string", required: true },
1701
- pageSize: { type: "number", required: false },
1702
- },
1748
+ inputSchema: t.Object({ fileId: Id, pageSize: t.Optional(PageSize100) }, { additionalProperties: false }),
1703
1749
  async execute(input, ctx) {
1704
1750
  const p = (input ?? {});
1705
1751
  const out = [];
@@ -1714,15 +1760,25 @@ export default function googleDrive(rl) {
1714
1760
  },
1715
1761
  });
1716
1762
  rl.registerAction("accessProposal.resolve", {
1763
+ access: "write",
1717
1764
  description: "Resolve an access proposal. `action` is one of 'ACCEPT' or 'DENY'. When accepting, pass `role` (default 'reader') to grant.",
1718
- inputSchema: {
1719
- fileId: { type: "string", required: true },
1720
- proposalId: { type: "string", required: true },
1721
- action: { type: "string", required: true, description: "'ACCEPT' | 'DENY'" },
1722
- role: { type: "string", required: false, description: "Role to grant on ACCEPT. Default 'reader'." },
1723
- view: { type: "string", required: false, description: "Optional Drive scope view (e.g. 'published')." },
1724
- sendNotification: { type: "boolean", required: false },
1725
- },
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
+ }),
1726
1782
  async execute(input, ctx) {
1727
1783
  const p = (input ?? {});
1728
1784
  const body = { action: p.action };
@@ -1738,10 +1794,9 @@ export default function googleDrive(rl) {
1738
1794
  });
1739
1795
  // ─── About ───────────────────────────────────────────────────────
1740
1796
  rl.registerAction("about.get", {
1797
+ access: "read",
1741
1798
  description: "Current user info: storage quota, export formats, max upload size, importable mime types. Useful for healthchecks and conversion planning.",
1742
- inputSchema: {
1743
- fields: { type: "string", required: false },
1744
- },
1799
+ inputSchema: t.Object({ fields: t.Optional(NonEmptyString) }, { additionalProperties: false }),
1745
1800
  async execute(input, ctx) {
1746
1801
  const p = (input ?? {});
1747
1802
  return driveRequest(ctx, "GET", `/drive/v3/about`, undefined, {
@@ -1752,16 +1807,13 @@ export default function googleDrive(rl) {
1752
1807
  });
1753
1808
  // ─── File export (ergonomic wrapper) ────────────────────────────
1754
1809
  rl.registerAction("file.export", {
1810
+ access: "write",
1755
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.",
1756
- inputSchema: {
1757
- fileId: { type: "string", required: true },
1758
- mimeType: {
1759
- type: "string",
1760
- required: true,
1761
- description: "Target export mime, e.g. application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf, text/plain, text/csv.",
1762
- },
1763
- savePath: { type: "string", required: false },
1764
- },
1812
+ inputSchema: t.Object({
1813
+ fileId: Id,
1814
+ mimeType: NonEmptyString,
1815
+ savePath: t.Optional(NonEmptyString),
1816
+ }, { additionalProperties: false }),
1765
1817
  async execute(input, ctx) {
1766
1818
  const p = (input ?? {});
1767
1819
  const token = await accessToken(ctx);
@@ -1782,20 +1834,21 @@ export default function googleDrive(rl) {
1782
1834
  });
1783
1835
  // ─── Raw file.list ──────────────────────────────────────────────
1784
1836
  rl.registerAction("file.list", {
1837
+ access: "read",
1785
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.",
1786
- inputSchema: {
1787
- q: { type: "string", required: false },
1788
- corpora: { type: "string", required: false, description: "user | drive | allDrives" },
1789
- driveId: { type: "string", required: false },
1790
- spaces: { type: "string", required: false },
1791
- orderBy: { type: "string", required: false },
1792
- pageSize: { type: "number", required: false },
1793
- pageToken: { type: "string", required: false },
1794
- includeItemsFromAllDrives: { type: "boolean", required: false, default: true },
1795
- supportsAllDrives: { type: "boolean", required: false, default: true },
1796
- fields: { type: "string", required: false },
1797
- returnAll: { type: "boolean", required: false, description: "If true, follows pageToken until exhausted and returns the concatenated file list." },
1798
- },
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 }),
1799
1852
  async execute(input, ctx) {
1800
1853
  const p = (input ?? {});
1801
1854
  const baseQs = {