@bubblelab/bubble-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +202 -0
- package/dist/bubble-bundle.d.ts +2021 -0
- package/dist/bubble-factory.d.ts +161 -0
- package/dist/bubble-factory.d.ts.map +1 -0
- package/dist/bubble-factory.js +426 -0
- package/dist/bubble-factory.js.map +1 -0
- package/dist/bubble-flow/bubble-flow-class.d.ts +19 -0
- package/dist/bubble-flow/bubble-flow-class.d.ts.map +1 -0
- package/dist/bubble-flow/bubble-flow-class.js +23 -0
- package/dist/bubble-flow/bubble-flow-class.js.map +1 -0
- package/dist/bubble-flow/sample/data-analyst-flow.d.ts +15 -0
- package/dist/bubble-flow/sample/data-analyst-flow.d.ts.map +1 -0
- package/dist/bubble-flow/sample/data-analyst-flow.js +63 -0
- package/dist/bubble-flow/sample/data-analyst-flow.js.map +1 -0
- package/dist/bubble-flow/sample/error-ts.d.ts +23 -0
- package/dist/bubble-flow/sample/error-ts.d.ts.map +1 -0
- package/dist/bubble-flow/sample/error-ts.js +31 -0
- package/dist/bubble-flow/sample/error-ts.js.map +1 -0
- package/dist/bubble-flow/sample/sanitytest.d.ts +10 -0
- package/dist/bubble-flow/sample/sanitytest.d.ts.map +1 -0
- package/dist/bubble-flow/sample/sanitytest.js +13 -0
- package/dist/bubble-flow/sample/sanitytest.js.map +1 -0
- package/dist/bubble-flow/sample/simple-webhook-2.d.ts +19 -0
- package/dist/bubble-flow/sample/simple-webhook-2.d.ts.map +1 -0
- package/dist/bubble-flow/sample/simple-webhook-2.js +23 -0
- package/dist/bubble-flow/sample/simple-webhook-2.js.map +1 -0
- package/dist/bubble-flow/sample/simple-webhook.d.ts +10 -0
- package/dist/bubble-flow/sample/simple-webhook.d.ts.map +1 -0
- package/dist/bubble-flow/sample/simple-webhook.js +18 -0
- package/dist/bubble-flow/sample/simple-webhook.js.map +1 -0
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts +29 -0
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts.map +1 -0
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.js +150 -0
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.js.map +1 -0
- package/dist/bubble-flow/sample/slack-v0.1.d.ts +10 -0
- package/dist/bubble-flow/sample/slack-v0.1.d.ts.map +1 -0
- package/dist/bubble-flow/sample/slack-v0.1.js +59 -0
- package/dist/bubble-flow/sample/slack-v0.1.js.map +1 -0
- package/dist/bubble-flow/sample/slackagenttest.d.ts +10 -0
- package/dist/bubble-flow/sample/slackagenttest.d.ts.map +1 -0
- package/dist/bubble-flow/sample/slackagenttest.js +59 -0
- package/dist/bubble-flow/sample/slackagenttest.js.map +1 -0
- package/dist/bubble-trigger/index.d.ts +2 -0
- package/dist/bubble-trigger/index.d.ts.map +1 -0
- package/dist/bubble-trigger/index.js +2 -0
- package/dist/bubble-trigger/index.js.map +1 -0
- package/dist/bubble-trigger/types.d.ts +87 -0
- package/dist/bubble-trigger/types.d.ts.map +1 -0
- package/dist/bubble-trigger/types.js +14 -0
- package/dist/bubble-trigger/types.js.map +1 -0
- package/dist/bubbles/service-bubble/ai-agent.d.ts +428 -0
- package/dist/bubbles/service-bubble/ai-agent.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/ai-agent.js +881 -0
- package/dist/bubbles/service-bubble/ai-agent.js.map +1 -0
- package/dist/bubbles/service-bubble/gmail.d.ts +3073 -0
- package/dist/bubbles/service-bubble/gmail.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/gmail.js +908 -0
- package/dist/bubbles/service-bubble/gmail.js.map +1 -0
- package/dist/bubbles/service-bubble/google-calendar.d.ts +3377 -0
- package/dist/bubbles/service-bubble/google-calendar.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-calendar.js +527 -0
- package/dist/bubbles/service-bubble/google-calendar.js.map +1 -0
- package/dist/bubbles/service-bubble/google-drive.d.ts +1152 -0
- package/dist/bubbles/service-bubble/google-drive.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-drive.js +943 -0
- package/dist/bubbles/service-bubble/google-drive.js.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets.d.ts +1811 -0
- package/dist/bubbles/service-bubble/google-sheets.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets.js +904 -0
- package/dist/bubbles/service-bubble/google-sheets.js.map +1 -0
- package/dist/bubbles/service-bubble/hello-world.d.ts +74 -0
- package/dist/bubbles/service-bubble/hello-world.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/hello-world.js +67 -0
- package/dist/bubbles/service-bubble/hello-world.js.map +1 -0
- package/dist/bubbles/service-bubble/http.d.ts +134 -0
- package/dist/bubbles/service-bubble/http.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/http.js +184 -0
- package/dist/bubbles/service-bubble/http.js.map +1 -0
- package/dist/bubbles/service-bubble/postgresql.d.ts +180 -0
- package/dist/bubbles/service-bubble/postgresql.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/postgresql.js +448 -0
- package/dist/bubbles/service-bubble/postgresql.js.map +1 -0
- package/dist/bubbles/service-bubble/resend.d.ts +301 -0
- package/dist/bubbles/service-bubble/resend.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/resend.js +253 -0
- package/dist/bubbles/service-bubble/resend.js.map +1 -0
- package/dist/bubbles/service-bubble/slack.d.ts +5869 -0
- package/dist/bubbles/service-bubble/slack.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/slack.js +1536 -0
- package/dist/bubbles/service-bubble/slack.js.map +1 -0
- package/dist/bubbles/service-bubble/storage.d.ts +571 -0
- package/dist/bubbles/service-bubble/storage.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/storage.js +504 -0
- package/dist/bubbles/service-bubble/storage.js.map +1 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +308 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js +285 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +416 -0
- package/dist/bubbles/tool-bubble/chart-js-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/chart-js-tool.js +570 -0
- package/dist/bubbles/tool-bubble/chart-js-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +99 -0
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js +645 -0
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/list-bubbles-tool.d.ts +112 -0
- package/dist/bubbles/tool-bubble/list-bubbles-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/list-bubbles-tool.js +82 -0
- package/dist/bubbles/tool-bubble/list-bubbles-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +413 -0
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.js +327 -0
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +122 -0
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/research-agent-tool.js +343 -0
- package/dist/bubbles/tool-bubble/research-agent-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +131 -0
- package/dist/bubbles/tool-bubble/sql-query-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/sql-query-tool.js +147 -0
- package/dist/bubbles/tool-bubble/sql-query-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/tool-template.d.ts +257 -0
- package/dist/bubbles/tool-bubble/tool-template.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/tool-template.js +238 -0
- package/dist/bubbles/tool-bubble/tool-template.js.map +1 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor-example.d.ts +8 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor-example.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor-example.js +65 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor-example.js.map +1 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor.tool.d.ts +125 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor.tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor.tool.js +169 -0
- package/dist/bubbles/tool-bubble/virtual-file-editor.tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +218 -0
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/web-crawl-tool.js +255 -0
- package/dist/bubbles/tool-bubble/web-crawl-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +134 -0
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/web-extract-tool.js +175 -0
- package/dist/bubbles/tool-bubble/web-extract-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +228 -0
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/web-scrape-tool.js +214 -0
- package/dist/bubbles/tool-bubble/web-scrape-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts +134 -0
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/web-search-tool.js +155 -0
- package/dist/bubbles/tool-bubble/web-search-tool.js.map +1 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts +114 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js +777 -0
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/bubblscript-generateor.workflow.d.ts +97 -0
- package/dist/bubbles/workflow-bubble/bubblscript-generateor.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/bubblscript-generateor.workflow.js +327 -0
- package/dist/bubbles/workflow-bubble/bubblscript-generateor.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts +303 -0
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.js +297 -0
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/file-editor-agent.workflow.d.ts +157 -0
- package/dist/bubbles/workflow-bubble/file-editor-agent.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/file-editor-agent.workflow.js +310 -0
- package/dist/bubbles/workflow-bubble/file-editor-agent.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +543 -0
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/generate-document.workflow.js +628 -0
- package/dist/bubbles/workflow-bubble/generate-document.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts +679 -0
- package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/parse-document.workflow.js +604 -0
- package/dist/bubbles/workflow-bubble/parse-document.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +1011 -0
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js +841 -0
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts +883 -0
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js +781 -0
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +300 -0
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js +508 -0
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +731 -0
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js +690 -0
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts +401 -0
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js +382 -0
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js.map +1 -0
- package/dist/bubbles/workflow-bubble/workflow-template.d.ts +144 -0
- package/dist/bubbles/workflow-bubble/workflow-template.d.ts.map +1 -0
- package/dist/bubbles/workflow-bubble/workflow-template.js +124 -0
- package/dist/bubbles/workflow-bubble/workflow-template.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/BubbleLogger.d.ts +146 -0
- package/dist/logging/BubbleLogger.d.ts.map +1 -0
- package/dist/logging/BubbleLogger.js +472 -0
- package/dist/logging/BubbleLogger.js.map +1 -0
- package/dist/logging/StreamingBubbleLogger.d.ts +85 -0
- package/dist/logging/StreamingBubbleLogger.d.ts.map +1 -0
- package/dist/logging/StreamingBubbleLogger.js +340 -0
- package/dist/logging/StreamingBubbleLogger.js.map +1 -0
- package/dist/types/ai-models.d.ts +4 -0
- package/dist/types/ai-models.d.ts.map +1 -0
- package/dist/types/ai-models.js +14 -0
- package/dist/types/ai-models.js.map +1 -0
- package/dist/types/available-tools.d.ts +4 -0
- package/dist/types/available-tools.d.ts.map +1 -0
- package/dist/types/available-tools.js +19 -0
- package/dist/types/available-tools.js.map +1 -0
- package/dist/types/base-bubble-class.d.ts +47 -0
- package/dist/types/base-bubble-class.d.ts.map +1 -0
- package/dist/types/base-bubble-class.js +212 -0
- package/dist/types/base-bubble-class.js.map +1 -0
- package/dist/types/bubble-errors.d.ts +44 -0
- package/dist/types/bubble-errors.d.ts.map +1 -0
- package/dist/types/bubble-errors.js +51 -0
- package/dist/types/bubble-errors.js.map +1 -0
- package/dist/types/bubble.d.ts +73 -0
- package/dist/types/bubble.d.ts.map +1 -0
- package/dist/types/bubble.js +2 -0
- package/dist/types/bubble.js.map +1 -0
- package/dist/types/credentials.d.ts +6 -0
- package/dist/types/credentials.d.ts.map +1 -0
- package/dist/types/credentials.js +6 -0
- package/dist/types/credentials.js.map +1 -0
- package/dist/types/service-bubble-class.d.ts +31 -0
- package/dist/types/service-bubble-class.d.ts.map +1 -0
- package/dist/types/service-bubble-class.js +36 -0
- package/dist/types/service-bubble-class.js.map +1 -0
- package/dist/types/streaming-events.d.ts +18 -0
- package/dist/types/streaming-events.d.ts.map +1 -0
- package/dist/types/streaming-events.js +5 -0
- package/dist/types/streaming-events.js.map +1 -0
- package/dist/types/tool-bubble-class.d.ts +19 -0
- package/dist/types/tool-bubble-class.d.ts.map +1 -0
- package/dist/types/tool-bubble-class.js +48 -0
- package/dist/types/tool-bubble-class.js.map +1 -0
- package/dist/types/workflow-bubble-class.d.ts +25 -0
- package/dist/types/workflow-bubble-class.d.ts.map +1 -0
- package/dist/types/workflow-bubble-class.js +30 -0
- package/dist/types/workflow-bubble-class.js.map +1 -0
- package/dist/utils/bubbleflow-parser.d.ts +32 -0
- package/dist/utils/bubbleflow-parser.d.ts.map +1 -0
- package/dist/utils/bubbleflow-parser.js +332 -0
- package/dist/utils/bubbleflow-parser.js.map +1 -0
- package/dist/utils/bubbleflow-validation.d.ts +9 -0
- package/dist/utils/bubbleflow-validation.d.ts.map +1 -0
- package/dist/utils/bubbleflow-validation.js +116 -0
- package/dist/utils/bubbleflow-validation.js.map +1 -0
- package/dist/utils/json-parsing.d.ts +20 -0
- package/dist/utils/json-parsing.d.ts.map +1 -0
- package/dist/utils/json-parsing.js +394 -0
- package/dist/utils/json-parsing.js.map +1 -0
- package/dist/utils/mock-data-generator.d.ts +43 -0
- package/dist/utils/mock-data-generator.d.ts.map +1 -0
- package/dist/utils/mock-data-generator.js +312 -0
- package/dist/utils/mock-data-generator.js.map +1 -0
- package/dist/utils/param-helper.d.ts +2 -0
- package/dist/utils/param-helper.d.ts.map +1 -0
- package/dist/utils/param-helper.js +5 -0
- package/dist/utils/param-helper.js.map +1 -0
- package/dist/utils/source-bubble-parser.d.ts +31 -0
- package/dist/utils/source-bubble-parser.d.ts.map +1 -0
- package/dist/utils/source-bubble-parser.js +231 -0
- package/dist/utils/source-bubble-parser.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ServiceBubble } from '../../types/service-bubble-class.js';
|
|
3
|
+
import { CredentialType } from '@bubblelab/shared-schemas';
|
|
4
|
+
// Define file metadata schema
|
|
5
|
+
const DriveFileSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
id: z.string().describe('Unique file identifier'),
|
|
8
|
+
name: z.string().describe('Name of the file'),
|
|
9
|
+
mimeType: z.string().describe('MIME type of the file'),
|
|
10
|
+
size: z.string().optional().describe('Size of the file in bytes'),
|
|
11
|
+
createdTime: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Creation time in RFC 3339 format'),
|
|
15
|
+
modifiedTime: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Last modified time in RFC 3339 format'),
|
|
19
|
+
webViewLink: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Link to view the file in Google Drive'),
|
|
23
|
+
webContentLink: z.string().optional().describe('Link to download the file'),
|
|
24
|
+
parents: z.array(z.string()).optional().describe('Parent folder IDs'),
|
|
25
|
+
shared: z.boolean().optional().describe('Whether the file is shared'),
|
|
26
|
+
owners: z
|
|
27
|
+
.array(z.object({
|
|
28
|
+
displayName: z.string().optional().describe('Owner display name'),
|
|
29
|
+
emailAddress: z.string().optional().describe('Owner email address'),
|
|
30
|
+
}))
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('File owners'),
|
|
33
|
+
})
|
|
34
|
+
.describe('Google Drive file metadata');
|
|
35
|
+
// Define folder creation schema
|
|
36
|
+
const DriveFolderSchema = z
|
|
37
|
+
.object({
|
|
38
|
+
id: z.string().describe('Unique folder identifier'),
|
|
39
|
+
name: z.string().describe('Name of the folder'),
|
|
40
|
+
webViewLink: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Link to view the folder in Google Drive'),
|
|
44
|
+
parents: z.array(z.string()).optional().describe('Parent folder IDs'),
|
|
45
|
+
})
|
|
46
|
+
.describe('Google Drive folder metadata');
|
|
47
|
+
// Define the parameters schema for Google Drive operations
|
|
48
|
+
const GoogleDriveParamsSchema = z.discriminatedUnion('operation', [
|
|
49
|
+
// Upload file operation
|
|
50
|
+
z.object({
|
|
51
|
+
operation: z
|
|
52
|
+
.literal('upload_file')
|
|
53
|
+
.describe('Upload a file to Google Drive'),
|
|
54
|
+
name: z
|
|
55
|
+
.string()
|
|
56
|
+
.min(1, 'File name is required')
|
|
57
|
+
.describe('Name for the uploaded file'),
|
|
58
|
+
content: z
|
|
59
|
+
.string()
|
|
60
|
+
.describe('File content as base64 encoded string or plain text'),
|
|
61
|
+
mimeType: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('MIME type of the file (auto-detected if not provided)'),
|
|
65
|
+
parent_folder_id: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('ID of the parent folder (uploads to root if not provided)'),
|
|
69
|
+
convert_to_google_docs: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.optional()
|
|
72
|
+
.default(false)
|
|
73
|
+
.describe('Convert uploaded file to Google Docs format if possible'),
|
|
74
|
+
credentials: z
|
|
75
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
78
|
+
}),
|
|
79
|
+
// Download file operation
|
|
80
|
+
z.object({
|
|
81
|
+
operation: z
|
|
82
|
+
.literal('download_file')
|
|
83
|
+
.describe('Download a file from Google Drive'),
|
|
84
|
+
file_id: z
|
|
85
|
+
.string()
|
|
86
|
+
.min(1, 'File ID is required')
|
|
87
|
+
.describe('Google Drive file ID to download'),
|
|
88
|
+
export_format: z
|
|
89
|
+
.string()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe('Export format for Google Workspace files (e.g., "application/pdf", "text/plain")'),
|
|
92
|
+
credentials: z
|
|
93
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
94
|
+
.optional()
|
|
95
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
96
|
+
}),
|
|
97
|
+
// List files operation
|
|
98
|
+
z.object({
|
|
99
|
+
operation: z
|
|
100
|
+
.literal('list_files')
|
|
101
|
+
.describe('List files and folders in Google Drive'),
|
|
102
|
+
folder_id: z
|
|
103
|
+
.string()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe('ID of folder to list files from (lists from root if not provided)'),
|
|
106
|
+
query: z
|
|
107
|
+
.string()
|
|
108
|
+
.optional()
|
|
109
|
+
.describe('Search query to filter files (e.g., "name contains \'report\'"'),
|
|
110
|
+
max_results: z
|
|
111
|
+
.number()
|
|
112
|
+
.min(1)
|
|
113
|
+
.max(1000)
|
|
114
|
+
.optional()
|
|
115
|
+
.default(100)
|
|
116
|
+
.describe('Maximum number of files to return'),
|
|
117
|
+
include_folders: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.optional()
|
|
120
|
+
.default(true)
|
|
121
|
+
.describe('Include folders in the results'),
|
|
122
|
+
order_by: z
|
|
123
|
+
.string()
|
|
124
|
+
.optional()
|
|
125
|
+
.default('modifiedTime desc')
|
|
126
|
+
.describe('Order results by field (e.g., "name", "modifiedTime desc")'),
|
|
127
|
+
credentials: z
|
|
128
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
129
|
+
.optional()
|
|
130
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
131
|
+
}),
|
|
132
|
+
// Create folder operation
|
|
133
|
+
z.object({
|
|
134
|
+
operation: z
|
|
135
|
+
.literal('create_folder')
|
|
136
|
+
.describe('Create a new folder in Google Drive'),
|
|
137
|
+
name: z
|
|
138
|
+
.string()
|
|
139
|
+
.min(1, 'Folder name is required')
|
|
140
|
+
.describe('Name of the folder to create'),
|
|
141
|
+
parent_folder_id: z
|
|
142
|
+
.string()
|
|
143
|
+
.optional()
|
|
144
|
+
.describe('ID of the parent folder (creates in root if not provided)'),
|
|
145
|
+
credentials: z
|
|
146
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
147
|
+
.optional()
|
|
148
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
149
|
+
}),
|
|
150
|
+
// Delete file operation
|
|
151
|
+
z.object({
|
|
152
|
+
operation: z
|
|
153
|
+
.literal('delete_file')
|
|
154
|
+
.describe('Delete a file or folder from Google Drive'),
|
|
155
|
+
file_id: z
|
|
156
|
+
.string()
|
|
157
|
+
.min(1, 'File ID is required')
|
|
158
|
+
.describe('Google Drive file or folder ID to delete'),
|
|
159
|
+
permanent: z
|
|
160
|
+
.boolean()
|
|
161
|
+
.optional()
|
|
162
|
+
.default(false)
|
|
163
|
+
.describe('Permanently delete (true) or move to trash (false)'),
|
|
164
|
+
credentials: z
|
|
165
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
166
|
+
.optional()
|
|
167
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
168
|
+
}),
|
|
169
|
+
// Get file info operation
|
|
170
|
+
z.object({
|
|
171
|
+
operation: z
|
|
172
|
+
.literal('get_file_info')
|
|
173
|
+
.describe('Get detailed information about a file or folder'),
|
|
174
|
+
file_id: z
|
|
175
|
+
.string()
|
|
176
|
+
.min(1, 'File ID is required')
|
|
177
|
+
.describe('Google Drive file or folder ID to get info for'),
|
|
178
|
+
include_permissions: z
|
|
179
|
+
.boolean()
|
|
180
|
+
.optional()
|
|
181
|
+
.default(false)
|
|
182
|
+
.describe('Include file permissions in the response'),
|
|
183
|
+
credentials: z
|
|
184
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
185
|
+
.optional()
|
|
186
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
187
|
+
}),
|
|
188
|
+
// Share file operation
|
|
189
|
+
z.object({
|
|
190
|
+
operation: z
|
|
191
|
+
.literal('share_file')
|
|
192
|
+
.describe('Share a file or folder with specific users or make it public'),
|
|
193
|
+
file_id: z
|
|
194
|
+
.string()
|
|
195
|
+
.min(1, 'File ID is required')
|
|
196
|
+
.describe('Google Drive file or folder ID to share'),
|
|
197
|
+
email_address: z
|
|
198
|
+
.string()
|
|
199
|
+
.email()
|
|
200
|
+
.optional()
|
|
201
|
+
.describe('Email address to share with (for specific user sharing)'),
|
|
202
|
+
role: z
|
|
203
|
+
.enum(['reader', 'writer', 'commenter', 'owner'])
|
|
204
|
+
.optional()
|
|
205
|
+
.default('reader')
|
|
206
|
+
.describe('Permission role to grant'),
|
|
207
|
+
type: z
|
|
208
|
+
.enum(['user', 'group', 'domain', 'anyone'])
|
|
209
|
+
.optional()
|
|
210
|
+
.default('user')
|
|
211
|
+
.describe('Type of permission to create'),
|
|
212
|
+
send_notification: z
|
|
213
|
+
.boolean()
|
|
214
|
+
.optional()
|
|
215
|
+
.default(true)
|
|
216
|
+
.describe('Send notification email to the user'),
|
|
217
|
+
credentials: z
|
|
218
|
+
.record(z.nativeEnum(CredentialType), z.string())
|
|
219
|
+
.optional()
|
|
220
|
+
.describe('Object mapping credential types to values (injected at runtime)'),
|
|
221
|
+
}),
|
|
222
|
+
]);
|
|
223
|
+
// Define result schemas for different operations
|
|
224
|
+
const GoogleDriveResultSchema = z.discriminatedUnion('operation', [
|
|
225
|
+
z.object({
|
|
226
|
+
operation: z
|
|
227
|
+
.literal('upload_file')
|
|
228
|
+
.describe('Upload a file to Google Drive'),
|
|
229
|
+
success: z.boolean().describe('Whether the file was uploaded successfully'),
|
|
230
|
+
file: DriveFileSchema.optional().describe('Uploaded file metadata'),
|
|
231
|
+
error: z.string().describe('Error message if operation failed'),
|
|
232
|
+
}),
|
|
233
|
+
z.object({
|
|
234
|
+
operation: z
|
|
235
|
+
.literal('download_file')
|
|
236
|
+
.describe('Download a file from Google Drive'),
|
|
237
|
+
success: z
|
|
238
|
+
.boolean()
|
|
239
|
+
.describe('Whether the file was downloaded successfully'),
|
|
240
|
+
content: z
|
|
241
|
+
.string()
|
|
242
|
+
.optional()
|
|
243
|
+
.describe('File content as base64 encoded string'),
|
|
244
|
+
filename: z.string().optional().describe('Original filename'),
|
|
245
|
+
mimeType: z
|
|
246
|
+
.string()
|
|
247
|
+
.optional()
|
|
248
|
+
.describe('MIME type of the downloaded file'),
|
|
249
|
+
error: z.string().describe('Error message if operation failed'),
|
|
250
|
+
}),
|
|
251
|
+
z.object({
|
|
252
|
+
operation: z
|
|
253
|
+
.literal('list_files')
|
|
254
|
+
.describe('List files and folders in Google Drive'),
|
|
255
|
+
success: z
|
|
256
|
+
.boolean()
|
|
257
|
+
.describe('Whether the file list was retrieved successfully'),
|
|
258
|
+
files: z
|
|
259
|
+
.array(DriveFileSchema)
|
|
260
|
+
.optional()
|
|
261
|
+
.describe('List of files and folders'),
|
|
262
|
+
total_count: z.number().optional().describe('Total number of files found'),
|
|
263
|
+
next_page_token: z
|
|
264
|
+
.string()
|
|
265
|
+
.optional()
|
|
266
|
+
.describe('Token for fetching next page of results'),
|
|
267
|
+
error: z.string().describe('Error message if operation failed'),
|
|
268
|
+
}),
|
|
269
|
+
z.object({
|
|
270
|
+
operation: z
|
|
271
|
+
.literal('create_folder')
|
|
272
|
+
.describe('Create a new folder in Google Drive'),
|
|
273
|
+
success: z
|
|
274
|
+
.boolean()
|
|
275
|
+
.describe('Whether the folder was created successfully'),
|
|
276
|
+
folder: DriveFolderSchema.optional().describe('Created folder metadata'),
|
|
277
|
+
error: z.string().describe('Error message if operation failed'),
|
|
278
|
+
}),
|
|
279
|
+
z.object({
|
|
280
|
+
operation: z
|
|
281
|
+
.literal('delete_file')
|
|
282
|
+
.describe('Delete a file or folder from Google Drive'),
|
|
283
|
+
success: z.boolean().describe('Whether the file was deleted successfully'),
|
|
284
|
+
deleted_file_id: z.string().optional().describe('ID of the deleted file'),
|
|
285
|
+
error: z.string().describe('Error message if operation failed'),
|
|
286
|
+
}),
|
|
287
|
+
z.object({
|
|
288
|
+
operation: z
|
|
289
|
+
.literal('get_file_info')
|
|
290
|
+
.describe('Get detailed information about a file or folder'),
|
|
291
|
+
success: z
|
|
292
|
+
.boolean()
|
|
293
|
+
.describe('Whether the file information was retrieved successfully'),
|
|
294
|
+
file: DriveFileSchema.optional().describe('File metadata and information'),
|
|
295
|
+
permissions: z
|
|
296
|
+
.array(z.object({
|
|
297
|
+
id: z.string(),
|
|
298
|
+
type: z.string(),
|
|
299
|
+
role: z.string(),
|
|
300
|
+
emailAddress: z
|
|
301
|
+
.string()
|
|
302
|
+
.optional()
|
|
303
|
+
.describe('Permission holder email address'),
|
|
304
|
+
displayName: z
|
|
305
|
+
.string()
|
|
306
|
+
.optional()
|
|
307
|
+
.describe('Permission holder display name'),
|
|
308
|
+
}))
|
|
309
|
+
.optional()
|
|
310
|
+
.describe('File permissions (if requested)'),
|
|
311
|
+
error: z.string().describe('Error message if operation failed'),
|
|
312
|
+
}),
|
|
313
|
+
z.object({
|
|
314
|
+
operation: z
|
|
315
|
+
.literal('share_file')
|
|
316
|
+
.describe('Share a file or folder with specific users or make it public'),
|
|
317
|
+
success: z.boolean().describe('Whether the file was shared successfully'),
|
|
318
|
+
permission_id: z
|
|
319
|
+
.string()
|
|
320
|
+
.optional()
|
|
321
|
+
.describe('ID of the created permission'),
|
|
322
|
+
share_link: z.string().optional().describe('Shareable link to the file'),
|
|
323
|
+
error: z.string().describe('Error message if operation failed'),
|
|
324
|
+
}),
|
|
325
|
+
]);
|
|
326
|
+
export class GoogleDriveBubble extends ServiceBubble {
|
|
327
|
+
static type = 'service';
|
|
328
|
+
static service = 'google-drive';
|
|
329
|
+
static authType = 'oauth';
|
|
330
|
+
static bubbleName = 'google-drive';
|
|
331
|
+
static schema = GoogleDriveParamsSchema;
|
|
332
|
+
static resultSchema = GoogleDriveResultSchema;
|
|
333
|
+
static shortDescription = 'Google Drive integration for file management';
|
|
334
|
+
static longDescription = `
|
|
335
|
+
Google Drive service integration for comprehensive file and folder management.
|
|
336
|
+
Use cases:
|
|
337
|
+
- Upload files and documents to Google Drive
|
|
338
|
+
- Download files with format conversion support
|
|
339
|
+
- List and search files with advanced filtering
|
|
340
|
+
- Create and organize folders
|
|
341
|
+
- Share files and manage permissions
|
|
342
|
+
- Get detailed file metadata and information
|
|
343
|
+
|
|
344
|
+
Security Features:
|
|
345
|
+
- OAuth 2.0 authentication with Google
|
|
346
|
+
- Scoped access permissions
|
|
347
|
+
- Secure file handling and validation
|
|
348
|
+
- User-controlled sharing and permissions
|
|
349
|
+
`;
|
|
350
|
+
static alias = 'gdrive';
|
|
351
|
+
constructor(params = {
|
|
352
|
+
operation: 'list_files',
|
|
353
|
+
max_results: 10,
|
|
354
|
+
}, context) {
|
|
355
|
+
super(params, context);
|
|
356
|
+
}
|
|
357
|
+
async testCredential() {
|
|
358
|
+
const credential = this.chooseCredential();
|
|
359
|
+
if (!credential) {
|
|
360
|
+
throw new Error('Google Drive credentials are required');
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
// Test the credentials by making a simple API call
|
|
364
|
+
const response = await fetch('https://www.googleapis.com/drive/v3/about?fields=user', {
|
|
365
|
+
headers: {
|
|
366
|
+
Authorization: `Bearer ${credential}`,
|
|
367
|
+
'Content-Type': 'application/json',
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
return response.ok;
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async makeGoogleApiRequest(endpoint, method = 'GET', body, headers = {}, responseType = 'auto') {
|
|
377
|
+
const url = endpoint.startsWith('https://')
|
|
378
|
+
? endpoint
|
|
379
|
+
: `https://www.googleapis.com/drive/v3${endpoint}`;
|
|
380
|
+
const requestHeaders = {
|
|
381
|
+
Authorization: `Bearer ${this.chooseCredential()}`,
|
|
382
|
+
'Content-Type': 'application/json',
|
|
383
|
+
...headers,
|
|
384
|
+
};
|
|
385
|
+
const requestInit = {
|
|
386
|
+
method,
|
|
387
|
+
headers: requestHeaders,
|
|
388
|
+
};
|
|
389
|
+
if (body && method !== 'GET') {
|
|
390
|
+
if (body instanceof FormData) {
|
|
391
|
+
// Remove Content-Type for FormData (browser will set it with boundary)
|
|
392
|
+
const { 'Content-Type': _, ...headersWithoutContentType } = requestHeaders;
|
|
393
|
+
requestInit.headers = headersWithoutContentType;
|
|
394
|
+
requestInit.body = body;
|
|
395
|
+
}
|
|
396
|
+
else if (body instanceof Buffer) {
|
|
397
|
+
// Handle Buffer content (for multipart uploads)
|
|
398
|
+
requestInit.body = body;
|
|
399
|
+
}
|
|
400
|
+
else if (typeof body === 'string') {
|
|
401
|
+
// Handle string content directly
|
|
402
|
+
requestInit.body = body;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
// Handle JSON objects
|
|
406
|
+
requestInit.body = JSON.stringify(body);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
console.log('📤 Sending request...');
|
|
410
|
+
const response = await fetch(url, requestInit);
|
|
411
|
+
console.log('📥 Response received:', {
|
|
412
|
+
status: response.status,
|
|
413
|
+
statusText: response.statusText,
|
|
414
|
+
ok: response.ok,
|
|
415
|
+
contentType: response.headers.get('content-type'),
|
|
416
|
+
});
|
|
417
|
+
if (!response.ok) {
|
|
418
|
+
const errorText = await response.text();
|
|
419
|
+
console.error('❌ API Error Response:', {
|
|
420
|
+
status: response.status,
|
|
421
|
+
statusText: response.statusText,
|
|
422
|
+
errorText,
|
|
423
|
+
});
|
|
424
|
+
throw new Error(`Google Drive API error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
425
|
+
}
|
|
426
|
+
// Handle empty responses
|
|
427
|
+
const contentType = response.headers.get('content-type') || '';
|
|
428
|
+
let responseData;
|
|
429
|
+
if (responseType === 'arrayBuffer') {
|
|
430
|
+
const ab = await response.arrayBuffer();
|
|
431
|
+
console.log('✅ API Response data: arrayBuffer bytes', ab.byteLength);
|
|
432
|
+
responseData = ab;
|
|
433
|
+
}
|
|
434
|
+
else if (responseType === 'json' ||
|
|
435
|
+
(responseType === 'auto' && contentType.includes('application/json'))) {
|
|
436
|
+
responseData = await response.json();
|
|
437
|
+
console.log('✅ API Response data:', JSON.stringify(responseData, null, 2));
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
responseData = await response.text();
|
|
441
|
+
console.log('✅ API Response data:', `text: ${String(responseData).substring(0, 200)}...`);
|
|
442
|
+
}
|
|
443
|
+
return responseData;
|
|
444
|
+
}
|
|
445
|
+
async performAction(context) {
|
|
446
|
+
void context;
|
|
447
|
+
const { operation } = this.params;
|
|
448
|
+
try {
|
|
449
|
+
const result = await (async () => {
|
|
450
|
+
switch (operation) {
|
|
451
|
+
case 'upload_file':
|
|
452
|
+
return await this.uploadFile(this.params);
|
|
453
|
+
case 'download_file':
|
|
454
|
+
return await this.downloadFile(this.params);
|
|
455
|
+
case 'list_files':
|
|
456
|
+
return await this.listFiles(this.params);
|
|
457
|
+
case 'create_folder':
|
|
458
|
+
return await this.createFolder(this.params);
|
|
459
|
+
case 'delete_file':
|
|
460
|
+
return await this.deleteFile(this.params);
|
|
461
|
+
case 'get_file_info':
|
|
462
|
+
return await this.getFileInfo(this.params);
|
|
463
|
+
case 'share_file':
|
|
464
|
+
return await this.shareFile(this.params);
|
|
465
|
+
default:
|
|
466
|
+
throw new Error(`Unsupported operation: ${operation}`);
|
|
467
|
+
}
|
|
468
|
+
})();
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
return {
|
|
473
|
+
operation,
|
|
474
|
+
success: false,
|
|
475
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async uploadFile(params) {
|
|
480
|
+
try {
|
|
481
|
+
console.log('🔍 Starting Google Drive upload process...');
|
|
482
|
+
const { name, content, mimeType, parent_folder_id, convert_to_google_docs, } = params;
|
|
483
|
+
console.log('📝 Upload parameters:', {
|
|
484
|
+
name,
|
|
485
|
+
contentLength: content?.length,
|
|
486
|
+
mimeType,
|
|
487
|
+
parent_folder_id,
|
|
488
|
+
convert_to_google_docs,
|
|
489
|
+
});
|
|
490
|
+
// Validate required parameters
|
|
491
|
+
if (!name || name.trim().length === 0) {
|
|
492
|
+
throw new Error('File name is required and cannot be empty');
|
|
493
|
+
}
|
|
494
|
+
if (!content || content.length === 0) {
|
|
495
|
+
throw new Error('File content is required and cannot be empty');
|
|
496
|
+
}
|
|
497
|
+
// Prepare file metadata
|
|
498
|
+
const fileMetadata = {
|
|
499
|
+
name,
|
|
500
|
+
};
|
|
501
|
+
if (parent_folder_id) {
|
|
502
|
+
fileMetadata.parents = [parent_folder_id];
|
|
503
|
+
}
|
|
504
|
+
// Handle Google Workspace conversion
|
|
505
|
+
if (convert_to_google_docs && mimeType) {
|
|
506
|
+
if (mimeType.includes('text/')) {
|
|
507
|
+
fileMetadata.mimeType = 'application/vnd.google-apps.document';
|
|
508
|
+
}
|
|
509
|
+
else if (mimeType.includes('spreadsheet') ||
|
|
510
|
+
mimeType.includes('csv')) {
|
|
511
|
+
fileMetadata.mimeType = 'application/vnd.google-apps.spreadsheet';
|
|
512
|
+
}
|
|
513
|
+
else if (mimeType.includes('presentation')) {
|
|
514
|
+
fileMetadata.mimeType = 'application/vnd.google-apps.presentation';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Determine content type and prepare file data
|
|
518
|
+
let fileData;
|
|
519
|
+
let actualMimeType = mimeType;
|
|
520
|
+
// Check if content is base64 encoded
|
|
521
|
+
const isBase64 = this.isBase64(content);
|
|
522
|
+
console.log('🔍 Content analysis:', {
|
|
523
|
+
isBase64,
|
|
524
|
+
contentPreview: content.substring(0, 100) + (content.length > 100 ? '...' : ''),
|
|
525
|
+
providedMimeType: mimeType,
|
|
526
|
+
});
|
|
527
|
+
if (isBase64) {
|
|
528
|
+
// Extract actual base64 content (might be wrapped in JSON)
|
|
529
|
+
const extractedBase64 = this.extractBase64Content(content);
|
|
530
|
+
console.log('🎯 Extracted base64 info:', {
|
|
531
|
+
originalLength: content.length,
|
|
532
|
+
extractedLength: extractedBase64.length,
|
|
533
|
+
isExtracted: extractedBase64 !== content,
|
|
534
|
+
});
|
|
535
|
+
// Decode base64 content
|
|
536
|
+
fileData = Buffer.from(extractedBase64, 'base64');
|
|
537
|
+
console.log('✅ Decoded base64 content:', {
|
|
538
|
+
base64Length: extractedBase64.length,
|
|
539
|
+
decodedLength: fileData.length,
|
|
540
|
+
});
|
|
541
|
+
// Auto-detect MIME type if not provided
|
|
542
|
+
if (!actualMimeType) {
|
|
543
|
+
actualMimeType = this.detectMimeTypeFromBase64(extractedBase64);
|
|
544
|
+
console.log('🎯 Auto-detected MIME type:', actualMimeType);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
// Treat as plain text
|
|
549
|
+
fileData = Buffer.from(content, 'utf-8');
|
|
550
|
+
console.log('📝 Processing as plain text:', {
|
|
551
|
+
contentLength: content.length,
|
|
552
|
+
bufferLength: fileData.length,
|
|
553
|
+
});
|
|
554
|
+
// Default to text/plain if no MIME type provided for plain text
|
|
555
|
+
if (!actualMimeType) {
|
|
556
|
+
actualMimeType = 'text/plain';
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
console.log('🎯 Final MIME type:', actualMimeType);
|
|
560
|
+
// Create multipart form data for upload
|
|
561
|
+
const boundary = `----formdata-boundary-${Date.now()}`;
|
|
562
|
+
const delimiter = `\r\n--${boundary}\r\n`;
|
|
563
|
+
const closeDelimiter = `\r\n--${boundary}--`;
|
|
564
|
+
console.log('🔧 Building multipart request:', {
|
|
565
|
+
boundary,
|
|
566
|
+
fileMetadata,
|
|
567
|
+
actualMimeType,
|
|
568
|
+
fileDataLength: fileData.length,
|
|
569
|
+
});
|
|
570
|
+
// Build the multipart body
|
|
571
|
+
let body = delimiter;
|
|
572
|
+
body += 'Content-Type: application/json\r\n\r\n';
|
|
573
|
+
body += JSON.stringify(fileMetadata) + delimiter;
|
|
574
|
+
body += `Content-Type: ${actualMimeType}\r\n\r\n`;
|
|
575
|
+
// Convert to Buffer and combine with file data
|
|
576
|
+
const bodyBuffer = Buffer.from(body, 'utf8');
|
|
577
|
+
const closeBuffer = Buffer.from(closeDelimiter, 'utf8');
|
|
578
|
+
const fullBody = Buffer.concat([bodyBuffer, fileData, closeBuffer]);
|
|
579
|
+
const uploadUrl = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,mimeType,size,createdTime,modifiedTime,webViewLink,webContentLink,parents';
|
|
580
|
+
console.log('🚀 Making upload request to:', uploadUrl);
|
|
581
|
+
// Make the upload request
|
|
582
|
+
const response = await this.makeGoogleApiRequest(uploadUrl, 'POST', fullBody, {
|
|
583
|
+
'Content-Type': `multipart/related; boundary=${boundary}`,
|
|
584
|
+
});
|
|
585
|
+
console.log('✅ Upload successful:', response);
|
|
586
|
+
return {
|
|
587
|
+
operation: 'upload_file',
|
|
588
|
+
success: true,
|
|
589
|
+
file: response,
|
|
590
|
+
error: '',
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
// Enhanced error handling for upload failures
|
|
595
|
+
console.error('❌ Upload failed:', error);
|
|
596
|
+
let errorMessage = 'Unknown error occurred during file upload';
|
|
597
|
+
if (error instanceof Error) {
|
|
598
|
+
console.error('📝 Error details:', {
|
|
599
|
+
message: error.message,
|
|
600
|
+
stack: error.stack,
|
|
601
|
+
name: error.name,
|
|
602
|
+
});
|
|
603
|
+
errorMessage = error.message;
|
|
604
|
+
// Provide more specific error messages for common issues
|
|
605
|
+
if (errorMessage.includes('401')) {
|
|
606
|
+
errorMessage =
|
|
607
|
+
'Authentication failed. Please check your Google Drive credentials.';
|
|
608
|
+
}
|
|
609
|
+
else if (errorMessage.includes('403')) {
|
|
610
|
+
errorMessage =
|
|
611
|
+
'Permission denied. Please ensure you have write access to Google Drive.';
|
|
612
|
+
}
|
|
613
|
+
else if (errorMessage.includes('404')) {
|
|
614
|
+
errorMessage =
|
|
615
|
+
'Parent folder not found. Please check the parent_folder_id.';
|
|
616
|
+
}
|
|
617
|
+
else if (errorMessage.includes('413')) {
|
|
618
|
+
errorMessage = 'File too large. Please reduce the file size.';
|
|
619
|
+
}
|
|
620
|
+
else if (errorMessage.includes('quotaExceeded')) {
|
|
621
|
+
errorMessage =
|
|
622
|
+
'Google Drive storage quota exceeded. Please free up space.';
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
console.error('💥 Final error message:', errorMessage);
|
|
626
|
+
return {
|
|
627
|
+
operation: 'upload_file',
|
|
628
|
+
success: false,
|
|
629
|
+
file: undefined,
|
|
630
|
+
error: errorMessage,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async downloadFile(params) {
|
|
635
|
+
const { file_id, export_format } = params;
|
|
636
|
+
// First get file metadata to determine if it's a Google Workspace file
|
|
637
|
+
const fileInfo = await this.makeGoogleApiRequest(`/files/${file_id}?fields=name,mimeType`);
|
|
638
|
+
let content;
|
|
639
|
+
let actualMimeType;
|
|
640
|
+
// Check if it's a Google Workspace file that needs export
|
|
641
|
+
if (fileInfo.mimeType?.startsWith('application/vnd.google-apps.')) {
|
|
642
|
+
if (!export_format) {
|
|
643
|
+
throw new Error('Export format is required for Google Workspace files');
|
|
644
|
+
}
|
|
645
|
+
const exportResponse = await this.makeGoogleApiRequest(`/files/${file_id}/export?mimeType=${encodeURIComponent(export_format)}`, 'GET', undefined, {}, 'arrayBuffer');
|
|
646
|
+
content = Buffer.from(exportResponse).toString('base64');
|
|
647
|
+
actualMimeType = export_format;
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
// Regular file download
|
|
651
|
+
const downloadResponse = await this.makeGoogleApiRequest(`/files/${file_id}?alt=media`, 'GET', undefined, {}, 'arrayBuffer');
|
|
652
|
+
content = Buffer.from(downloadResponse).toString('base64');
|
|
653
|
+
actualMimeType = fileInfo.mimeType;
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
operation: 'download_file',
|
|
657
|
+
success: true,
|
|
658
|
+
content,
|
|
659
|
+
filename: fileInfo.name,
|
|
660
|
+
mimeType: actualMimeType,
|
|
661
|
+
error: '',
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
async listFiles(params) {
|
|
665
|
+
const { folder_id, query, max_results, include_folders, order_by } = params;
|
|
666
|
+
let searchQuery = '';
|
|
667
|
+
// Build search query
|
|
668
|
+
if (folder_id) {
|
|
669
|
+
searchQuery += `'${folder_id}' in parents`;
|
|
670
|
+
}
|
|
671
|
+
if (!include_folders) {
|
|
672
|
+
searchQuery +=
|
|
673
|
+
(searchQuery ? ' and ' : '') +
|
|
674
|
+
"mimeType != 'application/vnd.google-apps.folder'";
|
|
675
|
+
}
|
|
676
|
+
if (query) {
|
|
677
|
+
searchQuery += (searchQuery ? ' and ' : '') + query;
|
|
678
|
+
}
|
|
679
|
+
// Add trashed filter
|
|
680
|
+
searchQuery += (searchQuery ? ' and ' : '') + 'trashed = false';
|
|
681
|
+
// Build query parameters
|
|
682
|
+
const queryParams = new URLSearchParams({
|
|
683
|
+
pageSize: max_results.toString(),
|
|
684
|
+
orderBy: order_by,
|
|
685
|
+
fields: 'nextPageToken,files(id,name,mimeType,size,createdTime,modifiedTime,webViewLink,webContentLink,parents,shared,owners)',
|
|
686
|
+
});
|
|
687
|
+
if (searchQuery) {
|
|
688
|
+
queryParams.set('q', searchQuery);
|
|
689
|
+
}
|
|
690
|
+
const response = await this.makeGoogleApiRequest(`/files?${queryParams.toString()}`);
|
|
691
|
+
return {
|
|
692
|
+
operation: 'list_files',
|
|
693
|
+
success: true,
|
|
694
|
+
files: response.files || [],
|
|
695
|
+
total_count: response.files?.length || 0,
|
|
696
|
+
next_page_token: response.nextPageToken,
|
|
697
|
+
error: '',
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
async createFolder(params) {
|
|
701
|
+
const { name, parent_folder_id } = params;
|
|
702
|
+
const fileMetadata = {
|
|
703
|
+
name,
|
|
704
|
+
mimeType: 'application/vnd.google-apps.folder',
|
|
705
|
+
};
|
|
706
|
+
if (parent_folder_id) {
|
|
707
|
+
fileMetadata.parents = [parent_folder_id];
|
|
708
|
+
}
|
|
709
|
+
const response = await this.makeGoogleApiRequest('/files?fields=id,name,webViewLink,parents', 'POST', fileMetadata);
|
|
710
|
+
return {
|
|
711
|
+
operation: 'create_folder',
|
|
712
|
+
success: true,
|
|
713
|
+
folder: response,
|
|
714
|
+
error: '',
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
async deleteFile(params) {
|
|
718
|
+
const { file_id, permanent } = params;
|
|
719
|
+
if (permanent) {
|
|
720
|
+
// Permanently delete the file
|
|
721
|
+
await this.makeGoogleApiRequest(`/files/${file_id}`, 'DELETE');
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
// Move to trash
|
|
725
|
+
await this.makeGoogleApiRequest(`/files/${file_id}`, 'PATCH', {
|
|
726
|
+
trashed: true,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
return {
|
|
730
|
+
operation: 'delete_file',
|
|
731
|
+
success: true,
|
|
732
|
+
deleted_file_id: file_id,
|
|
733
|
+
error: '',
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async getFileInfo(params) {
|
|
737
|
+
const { file_id, include_permissions } = params;
|
|
738
|
+
const fields = 'id,name,mimeType,size,createdTime,modifiedTime,webViewLink,webContentLink,parents,shared,owners';
|
|
739
|
+
const response = await this.makeGoogleApiRequest(`/files/${file_id}?fields=${fields}`);
|
|
740
|
+
let permissions;
|
|
741
|
+
if (include_permissions) {
|
|
742
|
+
const permissionsResponse = await this.makeGoogleApiRequest(`/files/${file_id}/permissions?fields=permissions(id,type,role,emailAddress,displayName)`);
|
|
743
|
+
permissions = permissionsResponse.permissions;
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
operation: 'get_file_info',
|
|
747
|
+
success: true,
|
|
748
|
+
file: response,
|
|
749
|
+
permissions,
|
|
750
|
+
error: '',
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
async shareFile(params) {
|
|
754
|
+
const { file_id, email_address, role, type, send_notification } = params;
|
|
755
|
+
const permission = {
|
|
756
|
+
role,
|
|
757
|
+
type,
|
|
758
|
+
};
|
|
759
|
+
if (email_address && (type === 'user' || type === 'group')) {
|
|
760
|
+
permission.emailAddress = email_address;
|
|
761
|
+
}
|
|
762
|
+
const queryParams = new URLSearchParams({
|
|
763
|
+
fields: 'id',
|
|
764
|
+
});
|
|
765
|
+
if (send_notification !== undefined) {
|
|
766
|
+
queryParams.set('sendNotificationEmail', send_notification.toString());
|
|
767
|
+
}
|
|
768
|
+
const response = await this.makeGoogleApiRequest(`/files/${file_id}/permissions?${queryParams.toString()}`, 'POST', permission);
|
|
769
|
+
// Get the file's web view link for sharing
|
|
770
|
+
const fileResponse = await this.makeGoogleApiRequest(`/files/${file_id}?fields=webViewLink`);
|
|
771
|
+
return {
|
|
772
|
+
operation: 'share_file',
|
|
773
|
+
success: true,
|
|
774
|
+
permission_id: response.id,
|
|
775
|
+
share_link: fileResponse.webViewLink,
|
|
776
|
+
error: '',
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
isBase64(str) {
|
|
780
|
+
try {
|
|
781
|
+
console.log('🔍 Analyzing content for base64...');
|
|
782
|
+
console.log('📏 Content length:', str.length);
|
|
783
|
+
console.log('🔤 Content start:', str.substring(0, 200));
|
|
784
|
+
// First check if it's a direct base64 string
|
|
785
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
786
|
+
if (base64Regex.test(str)) {
|
|
787
|
+
console.log('✅ Direct base64 detected');
|
|
788
|
+
// Try to decode and re-encode to verify
|
|
789
|
+
const decoded = Buffer.from(str, 'base64').toString('base64');
|
|
790
|
+
return decoded === str;
|
|
791
|
+
}
|
|
792
|
+
// Check if it's JSON containing base64 data
|
|
793
|
+
if (str.trim().startsWith('[') || str.trim().startsWith('{')) {
|
|
794
|
+
console.log('🔍 JSON structure detected, looking for base64 inside...');
|
|
795
|
+
try {
|
|
796
|
+
const parsed = JSON.parse(str);
|
|
797
|
+
const hasBase64Data = this.findBase64InObject(parsed);
|
|
798
|
+
console.log('📊 Base64 found in JSON:', hasBase64Data);
|
|
799
|
+
return hasBase64Data;
|
|
800
|
+
}
|
|
801
|
+
catch (jsonError) {
|
|
802
|
+
console.log('❌ Invalid JSON structure');
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// Check if it looks like base64 data (longer strings that might be base64)
|
|
806
|
+
if (str.length > 100 && /^[A-Za-z0-9+/]/.test(str) && str.includes('=')) {
|
|
807
|
+
console.log('🤔 Possible base64 string detected');
|
|
808
|
+
const cleanStr = str.replace(/\s/g, ''); // Remove whitespace
|
|
809
|
+
if (base64Regex.test(cleanStr)) {
|
|
810
|
+
console.log('✅ Cleaned base64 validated');
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
console.log('❌ No base64 content detected');
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
console.error('⚠️ Error in base64 detection:', error);
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
extractBase64Content(content) {
|
|
823
|
+
try {
|
|
824
|
+
console.log('🔍 Extracting base64 content...');
|
|
825
|
+
// If content is direct base64, return as-is
|
|
826
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
827
|
+
if (base64Regex.test(content)) {
|
|
828
|
+
console.log('✅ Content is direct base64');
|
|
829
|
+
return content;
|
|
830
|
+
}
|
|
831
|
+
// Try to parse as JSON and extract base64 data
|
|
832
|
+
if (content.trim().startsWith('[') || content.trim().startsWith('{')) {
|
|
833
|
+
try {
|
|
834
|
+
const parsed = JSON.parse(content);
|
|
835
|
+
const extractedBase64 = this.findAndExtractBase64(parsed);
|
|
836
|
+
if (extractedBase64) {
|
|
837
|
+
console.log('✅ Extracted base64 from JSON structure');
|
|
838
|
+
return extractedBase64;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch (parseError) {
|
|
842
|
+
console.log('❌ Failed to parse JSON for base64 extraction');
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
// If no extraction possible, return original content
|
|
846
|
+
console.log('⚠️ No base64 extraction possible, returning original');
|
|
847
|
+
return content;
|
|
848
|
+
}
|
|
849
|
+
catch (error) {
|
|
850
|
+
console.error('⚠️ Error extracting base64 content:', error);
|
|
851
|
+
return content;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
findAndExtractBase64(obj) {
|
|
855
|
+
if (typeof obj === 'string') {
|
|
856
|
+
// Check if this string looks like base64
|
|
857
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
858
|
+
if (obj.length > 100 && base64Regex.test(obj)) {
|
|
859
|
+
return obj;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
else if (Array.isArray(obj)) {
|
|
863
|
+
for (const item of obj) {
|
|
864
|
+
const result = this.findAndExtractBase64(item);
|
|
865
|
+
if (result)
|
|
866
|
+
return result;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else if (typeof obj === 'object' && obj !== null) {
|
|
870
|
+
// Look for common base64 data fields
|
|
871
|
+
const base64Fields = ['data', 'content', 'image', 'file'];
|
|
872
|
+
for (const field of base64Fields) {
|
|
873
|
+
if (obj[field] && typeof obj[field] === 'string') {
|
|
874
|
+
const result = this.findAndExtractBase64(obj[field]);
|
|
875
|
+
if (result)
|
|
876
|
+
return result;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
// Check all other values
|
|
880
|
+
for (const value of Object.values(obj)) {
|
|
881
|
+
const result = this.findAndExtractBase64(value);
|
|
882
|
+
if (result)
|
|
883
|
+
return result;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
findBase64InObject(obj) {
|
|
889
|
+
if (typeof obj === 'string') {
|
|
890
|
+
// Check if this string looks like base64
|
|
891
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
892
|
+
if (obj.length > 100 && base64Regex.test(obj)) {
|
|
893
|
+
return true;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
else if (Array.isArray(obj)) {
|
|
897
|
+
return obj.some((item) => this.findBase64InObject(item));
|
|
898
|
+
}
|
|
899
|
+
else if (typeof obj === 'object' && obj !== null) {
|
|
900
|
+
return Object.values(obj).some((value) => this.findBase64InObject(value));
|
|
901
|
+
}
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
detectMimeTypeFromBase64(base64Content) {
|
|
905
|
+
try {
|
|
906
|
+
// Decode the first few bytes to check for file signatures
|
|
907
|
+
const buffer = Buffer.from(base64Content.substring(0, 100), 'base64');
|
|
908
|
+
const header = buffer.toString('hex').toUpperCase();
|
|
909
|
+
// Common file type signatures
|
|
910
|
+
if (header.startsWith('FFD8FF'))
|
|
911
|
+
return 'image/jpeg';
|
|
912
|
+
if (header.startsWith('89504E47'))
|
|
913
|
+
return 'image/png';
|
|
914
|
+
if (header.startsWith('47494638'))
|
|
915
|
+
return 'image/gif';
|
|
916
|
+
if (header.startsWith('25504446'))
|
|
917
|
+
return 'application/pdf';
|
|
918
|
+
if (header.startsWith('504B0304') ||
|
|
919
|
+
header.startsWith('504B0506') ||
|
|
920
|
+
header.startsWith('504B0708')) {
|
|
921
|
+
// ZIP-based formats
|
|
922
|
+
return 'application/zip';
|
|
923
|
+
}
|
|
924
|
+
if (header.startsWith('D0CF11E0A1B11AE1'))
|
|
925
|
+
return 'application/vnd.ms-office';
|
|
926
|
+
// Default to binary if no specific type detected
|
|
927
|
+
return 'application/octet-stream';
|
|
928
|
+
}
|
|
929
|
+
catch {
|
|
930
|
+
// If detection fails, default to binary
|
|
931
|
+
return 'application/octet-stream';
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
chooseCredential() {
|
|
935
|
+
const { credentials } = this.params;
|
|
936
|
+
if (!credentials || typeof credentials !== 'object') {
|
|
937
|
+
throw new Error('No Google Drive credentials provided');
|
|
938
|
+
}
|
|
939
|
+
// Google Drive bubble uses GOOGLE_DRIVE_CRED credentials
|
|
940
|
+
return credentials[CredentialType.GOOGLE_DRIVE_CRED];
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
//# sourceMappingURL=google-drive.js.map
|