@divyacon/piece-dropbox 0.0.0-stage → 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.
Files changed (83) hide show
  1. package/LICENSE.MIT-AP +24 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +65 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/actions/copy-file.d.ts +7 -0
  7. package/dist/lib/actions/copy-file.d.ts.map +1 -0
  8. package/dist/lib/actions/copy-file.js +65 -0
  9. package/dist/lib/actions/copy-file.js.map +1 -0
  10. package/dist/lib/actions/copy-folder.d.ts +7 -0
  11. package/dist/lib/actions/copy-folder.d.ts.map +1 -0
  12. package/dist/lib/actions/copy-folder.js +65 -0
  13. package/dist/lib/actions/copy-folder.js.map +1 -0
  14. package/dist/lib/actions/create-new-folder.d.ts +5 -0
  15. package/dist/lib/actions/create-new-folder.d.ts.map +1 -0
  16. package/dist/lib/actions/create-new-folder.js +54 -0
  17. package/dist/lib/actions/create-new-folder.js.map +1 -0
  18. package/dist/lib/actions/create-new-text-file.d.ts +8 -0
  19. package/dist/lib/actions/create-new-text-file.d.ts.map +1 -0
  20. package/dist/lib/actions/create-new-text-file.js +76 -0
  21. package/dist/lib/actions/create-new-text-file.js.map +1 -0
  22. package/dist/lib/actions/delete-file.d.ts +4 -0
  23. package/dist/lib/actions/delete-file.d.ts.map +1 -0
  24. package/dist/lib/actions/delete-file.js +45 -0
  25. package/dist/lib/actions/delete-file.js.map +1 -0
  26. package/dist/lib/actions/delete-folder.d.ts +4 -0
  27. package/dist/lib/actions/delete-folder.d.ts.map +1 -0
  28. package/dist/lib/actions/delete-folder.js +45 -0
  29. package/dist/lib/actions/delete-folder.js.map +1 -0
  30. package/dist/lib/actions/download-file.d.ts +4 -0
  31. package/dist/lib/actions/download-file.d.ts.map +1 -0
  32. package/dist/lib/actions/download-file.js +53 -0
  33. package/dist/lib/actions/download-file.js.map +1 -0
  34. package/dist/lib/actions/get-file-link.d.ts +4 -0
  35. package/dist/lib/actions/get-file-link.d.ts.map +1 -0
  36. package/dist/lib/actions/get-file-link.js +45 -0
  37. package/dist/lib/actions/get-file-link.js.map +1 -0
  38. package/dist/lib/actions/list-a-folder.d.ts +6 -0
  39. package/dist/lib/actions/list-a-folder.d.ts.map +1 -0
  40. package/dist/lib/actions/list-a-folder.js +58 -0
  41. package/dist/lib/actions/list-a-folder.js.map +1 -0
  42. package/dist/lib/actions/move-file.d.ts +7 -0
  43. package/dist/lib/actions/move-file.d.ts.map +1 -0
  44. package/dist/lib/actions/move-file.js +65 -0
  45. package/dist/lib/actions/move-file.js.map +1 -0
  46. package/dist/lib/actions/move-folder.d.ts +7 -0
  47. package/dist/lib/actions/move-folder.d.ts.map +1 -0
  48. package/dist/lib/actions/move-folder.js +65 -0
  49. package/dist/lib/actions/move-folder.js.map +1 -0
  50. package/dist/lib/actions/search.d.ts +12 -0
  51. package/dist/lib/actions/search.d.ts.map +1 -0
  52. package/dist/lib/actions/search.js +114 -0
  53. package/dist/lib/actions/search.js.map +1 -0
  54. package/dist/lib/actions/upload-file.d.ts +8 -0
  55. package/dist/lib/actions/upload-file.d.ts.map +1 -0
  56. package/dist/lib/actions/upload-file.js +75 -0
  57. package/dist/lib/actions/upload-file.js.map +1 -0
  58. package/dist/lib/auth.d.ts +2 -0
  59. package/dist/lib/auth.d.ts.map +1 -0
  60. package/dist/lib/auth.js +21 -0
  61. package/dist/lib/auth.js.map +1 -0
  62. package/dist/lib/triggers/new-folder.d.ts +15 -0
  63. package/dist/lib/triggers/new-folder.d.ts.map +1 -0
  64. package/dist/lib/triggers/new-folder.js +114 -0
  65. package/dist/lib/triggers/new-folder.js.map +1 -0
  66. package/package.json +43 -3
  67. package/src/index.ts +59 -0
  68. package/src/lib/actions/copy-file.ts +66 -0
  69. package/src/lib/actions/copy-folder.ts +66 -0
  70. package/src/lib/actions/create-new-folder.ts +54 -0
  71. package/src/lib/actions/create-new-text-file.ts +78 -0
  72. package/src/lib/actions/delete-file.ts +44 -0
  73. package/src/lib/actions/delete-folder.ts +43 -0
  74. package/src/lib/actions/download-file.ts +51 -0
  75. package/src/lib/actions/get-file-link.ts +43 -0
  76. package/src/lib/actions/list-a-folder.ts +59 -0
  77. package/src/lib/actions/move-file.ts +65 -0
  78. package/src/lib/actions/move-folder.ts +67 -0
  79. package/src/lib/actions/search.ts +117 -0
  80. package/src/lib/actions/upload-file.ts +79 -0
  81. package/src/lib/auth.ts +16 -0
  82. package/src/lib/triggers/new-folder.ts +167 -0
  83. package/README.md +0 -3
@@ -0,0 +1,43 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxGetFileLink = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'get_dropbox_file_link',
12
+ description: 'Get a temporary file link',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Returns a temporary, directly downloadable URL for the file at the given Dropbox path. Use when an agent needs a shareable or fetchable link to file contents rather than downloading the bytes into the flow. Read-only lookup; safe to repeat, though the returned URL is short-lived.', idempotent: true },
15
+ displayName: 'Get temporary file link',
16
+ props: {
17
+ path: Property.ShortText({
18
+ displayName: 'Path',
19
+ description: 'The path of the file (e.g. /folder1/file.txt)',
20
+ required: true,
21
+ }),
22
+ },
23
+ async run(context) {
24
+ const params = {
25
+ path: context.propsValue.path,
26
+ };
27
+
28
+ const result = await httpClient.sendRequest({
29
+ method: HttpMethod.POST,
30
+ url: `https://api.dropboxapi.com/2/files/get_temporary_link`,
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ body: { path: context.propsValue.path },
35
+ authentication: {
36
+ type: AuthenticationType.BEARER_TOKEN,
37
+ token: context.auth.access_token,
38
+ },
39
+ });
40
+
41
+ return result.body;
42
+ },
43
+ });
@@ -0,0 +1,59 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxListAFolder = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'list_dropbox_folder',
12
+ description: 'List the contents of a folder',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Lists the files and subfolders within the given Dropbox folder path (use an empty string for the root), optionally recursing into all subfolders. Use to enumerate folder contents and discover item paths/IDs. Read-only; safe to repeat and returns the same listing for unchanged folders.', idempotent: true },
15
+ displayName: 'List a folder',
16
+ props: {
17
+ path: Property.ShortText({
18
+ displayName: 'Path',
19
+ description:
20
+ 'The path of the folder to be listed (e.g. /folder1). Use an empty string for the root folder.',
21
+ required: true,
22
+ }),
23
+ recursive: Property.Checkbox({
24
+ displayName: 'Recursive',
25
+ description:
26
+ 'If set to true, the list folder operation will be applied recursively to all subfolders and the response will contain contents of all subfolders.',
27
+ defaultValue: false,
28
+ required: false,
29
+ }),
30
+ limit: Property.Number({
31
+ displayName: 'Limit',
32
+ description:
33
+ 'The maximum number of results to return (between 1 and 2000). Default is 2000 if not specified.',
34
+ required: false,
35
+ }),
36
+ },
37
+ async run(context) {
38
+ const params = {
39
+ path: context.propsValue.path,
40
+ recursive: context.propsValue.recursive,
41
+ limit: context.propsValue.limit || 2000,
42
+ };
43
+
44
+ const result = await httpClient.sendRequest({
45
+ method: HttpMethod.POST,
46
+ url: `https://api.dropboxapi.com/2/files/list_folder`,
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ body: params,
51
+ authentication: {
52
+ type: AuthenticationType.BEARER_TOKEN,
53
+ token: context.auth.access_token,
54
+ },
55
+ });
56
+
57
+ return result.body;
58
+ },
59
+ });
@@ -0,0 +1,65 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxMoveFile = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'move_dropbox_file',
12
+ description: 'Move a file',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Moves (or renames) the file at the source path to a new destination path within Dropbox; optionally autorenames on conflict. Use to relocate or rename a single file. Not idempotent: after a successful move the source no longer exists, so repeating the call fails or, with autorename, can produce a differently named copy.', idempotent: false },
15
+ displayName: 'Move file',
16
+ props: {
17
+ from_path: Property.ShortText({
18
+ displayName: 'From Path',
19
+ description: 'The current path of the file (e.g. /folder1/oldfile.txt)',
20
+ required: true,
21
+ }),
22
+ to_path: Property.ShortText({
23
+ displayName: 'To Path',
24
+ description: 'The new path for the file (e.g. /folder2/newfile.txt)',
25
+ required: true,
26
+ }),
27
+ autorename: Property.Checkbox({
28
+ displayName: 'Auto Rename',
29
+ description:
30
+ "If there's a conflict, have the Dropbox server try to autorename the file to avoid conflict.",
31
+ defaultValue: false,
32
+ required: false,
33
+ }),
34
+ allow_ownership_transfer: Property.Checkbox({
35
+ displayName: 'Allow Ownership Transfer',
36
+ description:
37
+ 'Allows moves by owner even if it would result in an ownership transfer.',
38
+ defaultValue: false,
39
+ required: false,
40
+ }),
41
+ },
42
+ async run(context) {
43
+ const params = {
44
+ from_path: context.propsValue.from_path,
45
+ to_path: context.propsValue.to_path,
46
+ autorename: context.propsValue.autorename,
47
+ allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
48
+ };
49
+
50
+ const result = await httpClient.sendRequest({
51
+ method: HttpMethod.POST,
52
+ url: `https://api.dropboxapi.com/2/files/move_v2`,
53
+ headers: {
54
+ 'Content-Type': 'application/json',
55
+ },
56
+ body: params,
57
+ authentication: {
58
+ type: AuthenticationType.BEARER_TOKEN,
59
+ token: context.auth.access_token,
60
+ },
61
+ });
62
+
63
+ return result.body;
64
+ },
65
+ });
@@ -0,0 +1,67 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxMoveFolder = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'move_dropbox_folder',
12
+ description: 'Move a folder',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Moves (or renames) the folder at the source path, including its contents, to a new destination path within Dropbox; optionally autorenames on conflict. Use to relocate or rename an entire directory. Not idempotent: after a successful move the source no longer exists, so repeating the call fails or, with autorename, produces a differently named folder.', idempotent: false },
15
+ displayName: 'Move folder',
16
+ props: {
17
+ from_path: Property.ShortText({
18
+ displayName: 'From Path',
19
+ description:
20
+ 'The current path of the folder (e.g. /folder1/sourceFolder)',
21
+ required: true,
22
+ }),
23
+ to_path: Property.ShortText({
24
+ displayName: 'To Path',
25
+ description:
26
+ 'The new path for the folder (e.g. /folder2/destinationFolder)',
27
+ required: true,
28
+ }),
29
+ autorename: Property.Checkbox({
30
+ displayName: 'Auto Rename',
31
+ description:
32
+ "If there's a conflict, have the Dropbox server try to autorename the folder to avoid conflict.",
33
+ defaultValue: false,
34
+ required: false,
35
+ }),
36
+ allow_ownership_transfer: Property.Checkbox({
37
+ displayName: 'Allow Ownership Transfer',
38
+ description:
39
+ 'Allows moves by owner even if it would result in an ownership transfer.',
40
+ defaultValue: false,
41
+ required: false,
42
+ }),
43
+ },
44
+ async run(context) {
45
+ const params = {
46
+ from_path: context.propsValue.from_path,
47
+ to_path: context.propsValue.to_path,
48
+ autorename: context.propsValue.autorename,
49
+ allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
50
+ };
51
+
52
+ const result = await httpClient.sendRequest({
53
+ method: HttpMethod.POST,
54
+ url: `https://api.dropboxapi.com/2/files/move_v2`,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ },
58
+ body: params,
59
+ authentication: {
60
+ type: AuthenticationType.BEARER_TOKEN,
61
+ token: context.auth.access_token,
62
+ },
63
+ });
64
+
65
+ return result.body;
66
+ },
67
+ });
@@ -0,0 +1,117 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxSearch = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'search_dropbox',
12
+ description: 'Search for files and folders',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Searches a Dropbox account for files and folders whose name or content matches a query string (minimum 3 characters), optionally scoped to a path and filtered by file status, extensions, categories, or account. Use to locate items and obtain their paths/IDs before acting on them. Read-only; repeating the same search is safe and returns the same matches.', idempotent: true },
15
+ displayName: 'Search',
16
+ props: {
17
+ query: Property.ShortText({
18
+ displayName: 'Query',
19
+ description: 'The search string. Must be at least 3 characters.',
20
+ required: true,
21
+ }),
22
+ path: Property.ShortText({
23
+ displayName: 'Path',
24
+ description:
25
+ 'The path to search in. If not specified, the search is performed from the root.',
26
+ required: false,
27
+ }),
28
+ max_results: Property.Number({
29
+ displayName: 'Max Results',
30
+ description:
31
+ 'The maximum number of search results to return (up to 1000). Default is 100 if not specified.',
32
+ required: false,
33
+ }),
34
+ order_by: Property.StaticDropdown({
35
+ displayName: 'Order By',
36
+ description: 'Specified property of the order of search results.',
37
+ options: {
38
+ options: [
39
+ { label: 'Relevance', value: 'relevance' },
40
+ { label: 'Modified Time', value: 'modified_time' },
41
+ ],
42
+ },
43
+ defaultValue: 'relevance',
44
+ required: false,
45
+ }),
46
+ file_status: Property.StaticDropdown({
47
+ displayName: 'File Status',
48
+ description: 'Restricts search to the given file status.',
49
+ options: {
50
+ options: [
51
+ { label: 'Active', value: 'active' },
52
+ { label: 'Deleted', value: 'deleted' },
53
+ ],
54
+ },
55
+ defaultValue: 'active',
56
+ required: false,
57
+ }),
58
+ filename_only: Property.Checkbox({
59
+ displayName: 'Filename Only',
60
+ description: 'Restricts search to only match on filenames.',
61
+ defaultValue: false,
62
+ required: false,
63
+ }),
64
+ file_extensions: Property.ShortText({
65
+ displayName: 'File Extensions',
66
+ description:
67
+ 'Restricts search to only the extensions specified (comma-separated).',
68
+ required: false,
69
+ }),
70
+ file_categories: Property.ShortText({
71
+ displayName: 'File Categories',
72
+ description:
73
+ 'Restricts search to only the file categories specified (comma-separated).',
74
+ required: false,
75
+ }),
76
+ account_id: Property.ShortText({
77
+ displayName: 'Account ID',
78
+ description: 'Restricts results to the given account id.',
79
+ required: false,
80
+ }),
81
+ },
82
+ async run(context) {
83
+ const options = {
84
+ path: context.propsValue.path || '',
85
+ max_results: context.propsValue.max_results || 100,
86
+ file_status: context.propsValue.file_status,
87
+ filename_only: context.propsValue.filename_only,
88
+ file_extensions: context.propsValue.file_extensions
89
+ ? context.propsValue.file_extensions.split(',')
90
+ : undefined,
91
+ file_categories: context.propsValue.file_categories
92
+ ? context.propsValue.file_categories.split(',')
93
+ : undefined,
94
+ account_id: context.propsValue.account_id,
95
+ };
96
+
97
+ const requestBody = {
98
+ query: context.propsValue.query,
99
+ options: options,
100
+ };
101
+
102
+ const result = await httpClient.sendRequest({
103
+ method: HttpMethod.POST,
104
+ url: `https://api.dropboxapi.com/2/files/search_v2`,
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ },
108
+ body: requestBody,
109
+ authentication: {
110
+ type: AuthenticationType.BEARER_TOKEN,
111
+ token: context.auth.access_token,
112
+ },
113
+ });
114
+
115
+ return result.body;
116
+ },
117
+ });
@@ -0,0 +1,79 @@
1
+ import { createAction, Property } from '@agnocon/pieces-framework';
2
+ import {
3
+ httpClient,
4
+ HttpMethod,
5
+ AuthenticationType,
6
+ } from '@agnocon/pieces-common';
7
+ import { dropboxAuth } from '../auth';
8
+
9
+ export const dropboxUploadFile = createAction({
10
+ auth: dropboxAuth,
11
+ name: 'upload_dropbox_file',
12
+ description: 'Upload a file',
13
+ audience: 'both',
14
+ aiMetadata: { description: 'Uploads a file (provided as a URL or base64 file object) to the given Dropbox path in add mode. Use to store binary or arbitrary file content; prefer the create-text-file action when the source is plain text. Not idempotent: each call uploads, so repeating it can create autorenamed duplicates rather than overwriting.', idempotent: false },
15
+ displayName: 'Upload file',
16
+ props: {
17
+ path: Property.ShortText({
18
+ displayName: 'Path',
19
+ description:
20
+ 'The path where the file should be saved (e.g. /folder1/file.txt)',
21
+ required: true,
22
+ }),
23
+ file: Property.File({
24
+ displayName: 'File',
25
+ description: 'The file URL or base64 to upload',
26
+ required: true,
27
+ }),
28
+ autorename: Property.Checkbox({
29
+ displayName: 'Auto Rename',
30
+ description:
31
+ "If there's a conflict, as determined by mode, have the Dropbox server try to autorename the file to avoid conflict.",
32
+ defaultValue: false,
33
+ required: false,
34
+ }),
35
+ mute: Property.Checkbox({
36
+ displayName: 'Mute',
37
+ description:
38
+ "Normally, users are made aware of any file modifications in their Dropbox account via notifications in the client software. If true, this tells the clients that this modification shouldn't result in a user notification.",
39
+ required: false,
40
+ }),
41
+ strict_conflict: Property.Checkbox({
42
+ displayName: 'Strict conflict',
43
+ description:
44
+ 'Be more strict about how each WriteMode detects conflict. For example, always return a conflict error when mode = WriteMode.update and the given "rev" doesn\'t match the existing file\'s "rev", even if the existing file has been deleted.',
45
+ required: false,
46
+ }),
47
+ },
48
+ async run(context) {
49
+ const fileData = context.propsValue.file;
50
+
51
+ const params = {
52
+ autorename: context.propsValue.autorename,
53
+ path: context.propsValue.path,
54
+ mode: 'add',
55
+ mute: context.propsValue.mute,
56
+ strict_conflict: context.propsValue.strict_conflict,
57
+ };
58
+
59
+ const fileBuffer = Buffer.from(fileData.base64, 'base64');
60
+ // For information about Dropbox JSON encoding, see https://www.dropbox.com/developers/reference/json-encoding
61
+ const dropboxApiArg = JSON.stringify(params).replace(/[\u007f-\uffff]/g, (c) => '\\u'+('000'+c.charCodeAt(0).toString(16)).slice(-4));
62
+
63
+ const result = await httpClient.sendRequest({
64
+ method: HttpMethod.POST,
65
+ url: `https://content.dropboxapi.com/2/files/upload`,
66
+ body: fileBuffer,
67
+ headers: {
68
+ 'Dropbox-API-Arg': dropboxApiArg,
69
+ 'Content-Type': 'application/octet-stream',
70
+ },
71
+ authentication: {
72
+ type: AuthenticationType.BEARER_TOKEN,
73
+ token: context.auth.access_token,
74
+ },
75
+ });
76
+
77
+ return result.body;
78
+ },
79
+ });
@@ -0,0 +1,16 @@
1
+ import { PieceAuth } from '@agnocon/pieces-framework';
2
+
3
+ export const dropboxAuth = PieceAuth.OAuth2({
4
+ description: '',
5
+ authUrl: 'https://www.dropbox.com/oauth2/authorize',
6
+ tokenUrl: 'https://api.dropboxapi.com/oauth2/token',
7
+ required: true,
8
+ // include token_access_type=offline as a parameter on Authorization URL in order to return a refresh_token
9
+ extra: { token_access_type: 'offline' },
10
+ scope: [
11
+ 'files.metadata.write',
12
+ 'files.metadata.read',
13
+ 'files.content.write',
14
+ 'files.content.read',
15
+ ],
16
+ });
@@ -0,0 +1,167 @@
1
+ import {
2
+ createTrigger,
3
+ Property,
4
+ TriggerStrategy,
5
+ } from '@agnocon/pieces-framework';
6
+
7
+ import {
8
+ AuthenticationType,
9
+ HttpMethod,
10
+ httpClient,
11
+ } from '@agnocon/pieces-common';
12
+ import { dropboxAuth } from '../auth';
13
+
14
+ type DropboxListFolderEntry = {
15
+ id: string;
16
+ name: string;
17
+ path_lower?: string;
18
+ path_display?: string;
19
+ ['.tag']: string;
20
+ };
21
+
22
+ type DropboxListFolderResponse = {
23
+ entries: DropboxListFolderEntry[];
24
+ cursor: string;
25
+ has_more: boolean;
26
+ };
27
+
28
+ // Returns the latest cursor for a folder without fetching any entries.
29
+ // Designed for apps that only need to know about new changes going forward.
30
+ const getLatestCursor = async (
31
+ accessToken: string,
32
+ path: string,
33
+ recursive: boolean,
34
+ ): Promise<string> => {
35
+ const res = await httpClient.sendRequest<{ cursor: string }>({
36
+ method: HttpMethod.POST,
37
+ url: 'https://api.dropboxapi.com/2/files/list_folder/get_latest_cursor',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: { path, recursive, include_deleted: false },
40
+ authentication: {
41
+ type: AuthenticationType.BEARER_TOKEN,
42
+ token: accessToken,
43
+ },
44
+ });
45
+ return res.body.cursor;
46
+ };
47
+
48
+ // Calls list_folder/continue with a stored cursor and returns all new folder entries,
49
+ // plus the updated cursor to store for the next poll.
50
+ const getChangedFolders = async (
51
+ accessToken: string,
52
+ cursor: string,
53
+ ): Promise<{ folders: DropboxListFolderEntry[]; newCursor: string }> => {
54
+ const folders: DropboxListFolderEntry[] = [];
55
+ let currentCursor = cursor;
56
+ let hasMore = true;
57
+
58
+ while (hasMore) {
59
+ const res = await httpClient.sendRequest<DropboxListFolderResponse>({
60
+ method: HttpMethod.POST,
61
+ url: 'https://api.dropboxapi.com/2/files/list_folder/continue',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: { cursor: currentCursor },
64
+ authentication: {
65
+ type: AuthenticationType.BEARER_TOKEN,
66
+ token: accessToken,
67
+ },
68
+ });
69
+
70
+ const newFolders = res.body.entries.filter((e) => e['.tag'] === 'folder');
71
+ folders.push(...newFolders);
72
+ currentCursor = res.body.cursor;
73
+ hasMore = res.body.has_more;
74
+ }
75
+
76
+ return { folders, newCursor: currentCursor };
77
+ };
78
+
79
+ // Fetches recent folder entries for test mode — returns up to 5.
80
+ const listRecentFolders = async (
81
+ accessToken: string,
82
+ path: string,
83
+ recursive: boolean,
84
+ ): Promise<DropboxListFolderEntry[]> => {
85
+ const res = await httpClient.sendRequest<DropboxListFolderResponse>({
86
+ method: HttpMethod.POST,
87
+ url: 'https://api.dropboxapi.com/2/files/list_folder',
88
+ headers: { 'Content-Type': 'application/json' },
89
+ body: { path, recursive, include_deleted: false, limit: 100 },
90
+ authentication: {
91
+ type: AuthenticationType.BEARER_TOKEN,
92
+ token: accessToken,
93
+ },
94
+ });
95
+
96
+ return res.body.entries.filter((e) => e['.tag'] === 'folder').slice(0, 5);
97
+ };
98
+
99
+ export const dropboxNewFolder = createTrigger({
100
+ auth: dropboxAuth,
101
+ name: 'new_folder',
102
+ displayName: 'New Folder',
103
+ description:
104
+ 'Triggers when a new folder is created inside a watched Dropbox folder.',
105
+ aiMetadata: {
106
+ description: 'Fires when a new folder appears inside the watched Dropbox folder path, detected by polling the folder for changes since the last cursor. Each fired event represents one newly created folder; enable recursive watching to also detect folders created within subfolders.',
107
+ },
108
+
109
+ type: TriggerStrategy.POLLING,
110
+
111
+ props: {
112
+ path: Property.ShortText({
113
+ displayName: 'Watched Folder Path',
114
+ description: 'Folder path to watch. Use empty string "" for root.',
115
+ required: true,
116
+ defaultValue: '',
117
+ }),
118
+
119
+ recursive: Property.Checkbox({
120
+ displayName: 'Recursive',
121
+ description: 'Watch subfolders recursively. May return many results.',
122
+ required: false,
123
+ defaultValue: false,
124
+ }),
125
+ },
126
+
127
+ sampleData: {
128
+ '.tag': 'folder',
129
+ name: 'Project Docs',
130
+ id: 'id:a4ayc_80_OEAAAAAAAAAXw',
131
+ path_display: '/Work/Project Docs',
132
+ path_lower: '/work/project docs',
133
+ },
134
+
135
+ onEnable: async (context) => {
136
+ const cursor = await getLatestCursor(
137
+ context.auth.access_token,
138
+ context.propsValue.path || '',
139
+ context.propsValue.recursive ?? false,
140
+ );
141
+ await context.store.put('cursor', cursor);
142
+ },
143
+
144
+ onDisable: async (context) => {
145
+ await context.store.delete('cursor');
146
+ },
147
+
148
+ run: async (context) => {
149
+ const cursor = await context.store.get<string>('cursor');
150
+ if (!cursor) return [];
151
+
152
+ const { folders, newCursor } = await getChangedFolders(
153
+ context.auth.access_token,
154
+ cursor,
155
+ );
156
+ await context.store.put('cursor', newCursor);
157
+ return folders;
158
+ },
159
+
160
+ test: async (context) => {
161
+ return listRecentFolders(
162
+ context.auth.access_token,
163
+ context.propsValue.path || '',
164
+ context.propsValue.recursive ?? false,
165
+ );
166
+ },
167
+ });
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.