@hasna/connectors 0.5.0 → 0.5.2

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 (223) hide show
  1. package/bin/index.js +147 -6
  2. package/bin/mcp.js +92 -1
  3. package/bin/serve.js +91 -0
  4. package/connectors/connect-ably/.env.example +11 -0
  5. package/connectors/connect-ably/CLAUDE.md +111 -0
  6. package/connectors/connect-ably/README.md +193 -0
  7. package/connectors/connect-ably/package.json +54 -0
  8. package/connectors/connect-ably/scripts/release.ts +179 -0
  9. package/connectors/connect-ably/src/api/channels.ts +33 -0
  10. package/connectors/connect-ably/src/api/client.ts +203 -0
  11. package/connectors/connect-ably/src/api/index.ts +59 -0
  12. package/connectors/connect-ably/src/api/messages.ts +48 -0
  13. package/connectors/connect-ably/src/api/presence.ts +39 -0
  14. package/connectors/connect-ably/src/api/stats.ts +29 -0
  15. package/connectors/connect-ably/src/cli/index.ts +397 -0
  16. package/connectors/connect-ably/src/index.ts +102 -0
  17. package/connectors/connect-ably/src/types/index.ts +294 -0
  18. package/connectors/connect-ably/src/utils/auth.ts +274 -0
  19. package/connectors/connect-ably/src/utils/bulk.ts +212 -0
  20. package/connectors/connect-ably/src/utils/config.ts +323 -0
  21. package/connectors/connect-ably/src/utils/output.ts +175 -0
  22. package/connectors/connect-ably/src/utils/settings.ts +114 -0
  23. package/connectors/connect-ably/src/utils/storage.ts +198 -0
  24. package/connectors/connect-ably/tsconfig.json +16 -0
  25. package/connectors/connect-box/.env.example +11 -0
  26. package/connectors/connect-box/CLAUDE.md +272 -0
  27. package/connectors/connect-box/README.md +193 -0
  28. package/connectors/connect-box/package.json +51 -0
  29. package/connectors/connect-box/scripts/release.ts +179 -0
  30. package/connectors/connect-box/src/api/client.ts +213 -0
  31. package/connectors/connect-box/src/api/example.ts +48 -0
  32. package/connectors/connect-box/src/api/index.ts +51 -0
  33. package/connectors/connect-box/src/cli/index.ts +254 -0
  34. package/connectors/connect-box/src/index.ts +103 -0
  35. package/connectors/connect-box/src/types/index.ts +237 -0
  36. package/connectors/connect-box/src/utils/auth.ts +274 -0
  37. package/connectors/connect-box/src/utils/bulk.ts +212 -0
  38. package/connectors/connect-box/src/utils/config.ts +326 -0
  39. package/connectors/connect-box/src/utils/output.ts +175 -0
  40. package/connectors/connect-box/src/utils/settings.ts +114 -0
  41. package/connectors/connect-box/src/utils/storage.ts +198 -0
  42. package/connectors/connect-box/tsconfig.json +16 -0
  43. package/connectors/connect-clearbit/.env.example +11 -0
  44. package/connectors/connect-clearbit/CLAUDE.md +272 -0
  45. package/connectors/connect-clearbit/README.md +193 -0
  46. package/connectors/connect-clearbit/package.json +51 -0
  47. package/connectors/connect-clearbit/scripts/release.ts +179 -0
  48. package/connectors/connect-clearbit/src/api/client.ts +213 -0
  49. package/connectors/connect-clearbit/src/api/example.ts +48 -0
  50. package/connectors/connect-clearbit/src/api/index.ts +51 -0
  51. package/connectors/connect-clearbit/src/cli/index.ts +254 -0
  52. package/connectors/connect-clearbit/src/index.ts +103 -0
  53. package/connectors/connect-clearbit/src/types/index.ts +237 -0
  54. package/connectors/connect-clearbit/src/utils/auth.ts +274 -0
  55. package/connectors/connect-clearbit/src/utils/bulk.ts +212 -0
  56. package/connectors/connect-clearbit/src/utils/config.ts +326 -0
  57. package/connectors/connect-clearbit/src/utils/output.ts +175 -0
  58. package/connectors/connect-clearbit/src/utils/settings.ts +114 -0
  59. package/connectors/connect-clearbit/src/utils/storage.ts +198 -0
  60. package/connectors/connect-clearbit/tsconfig.json +16 -0
  61. package/connectors/connect-coda/.env.example +11 -0
  62. package/connectors/connect-coda/CLAUDE.md +272 -0
  63. package/connectors/connect-coda/README.md +193 -0
  64. package/connectors/connect-coda/package.json +51 -0
  65. package/connectors/connect-coda/scripts/release.ts +179 -0
  66. package/connectors/connect-coda/src/api/client.ts +213 -0
  67. package/connectors/connect-coda/src/api/example.ts +48 -0
  68. package/connectors/connect-coda/src/api/index.ts +51 -0
  69. package/connectors/connect-coda/src/cli/index.ts +254 -0
  70. package/connectors/connect-coda/src/index.ts +103 -0
  71. package/connectors/connect-coda/src/types/index.ts +237 -0
  72. package/connectors/connect-coda/src/utils/auth.ts +274 -0
  73. package/connectors/connect-coda/src/utils/bulk.ts +212 -0
  74. package/connectors/connect-coda/src/utils/config.ts +326 -0
  75. package/connectors/connect-coda/src/utils/output.ts +175 -0
  76. package/connectors/connect-coda/src/utils/settings.ts +114 -0
  77. package/connectors/connect-coda/src/utils/storage.ts +198 -0
  78. package/connectors/connect-coda/tsconfig.json +16 -0
  79. package/connectors/connect-dropbox/.env.example +11 -0
  80. package/connectors/connect-dropbox/CLAUDE.md +119 -0
  81. package/connectors/connect-dropbox/README.md +193 -0
  82. package/connectors/connect-dropbox/package.json +51 -0
  83. package/connectors/connect-dropbox/src/api/client.ts +222 -0
  84. package/connectors/connect-dropbox/src/api/index.ts +395 -0
  85. package/connectors/connect-dropbox/src/cli/index.ts +627 -0
  86. package/connectors/connect-dropbox/src/index.ts +20 -0
  87. package/connectors/connect-dropbox/src/types/index.ts +516 -0
  88. package/connectors/connect-dropbox/src/utils/config.ts +197 -0
  89. package/connectors/connect-dropbox/tsconfig.json +16 -0
  90. package/connectors/connect-linode/.env.example +11 -0
  91. package/connectors/connect-linode/CLAUDE.md +272 -0
  92. package/connectors/connect-linode/README.md +193 -0
  93. package/connectors/connect-linode/package.json +51 -0
  94. package/connectors/connect-linode/scripts/release.ts +179 -0
  95. package/connectors/connect-linode/src/api/client.ts +213 -0
  96. package/connectors/connect-linode/src/api/example.ts +48 -0
  97. package/connectors/connect-linode/src/api/index.ts +51 -0
  98. package/connectors/connect-linode/src/cli/index.ts +254 -0
  99. package/connectors/connect-linode/src/index.ts +103 -0
  100. package/connectors/connect-linode/src/types/index.ts +237 -0
  101. package/connectors/connect-linode/src/utils/auth.ts +274 -0
  102. package/connectors/connect-linode/src/utils/bulk.ts +212 -0
  103. package/connectors/connect-linode/src/utils/config.ts +326 -0
  104. package/connectors/connect-linode/src/utils/output.ts +175 -0
  105. package/connectors/connect-linode/src/utils/settings.ts +114 -0
  106. package/connectors/connect-linode/src/utils/storage.ts +198 -0
  107. package/connectors/connect-linode/tsconfig.json +16 -0
  108. package/connectors/connect-mailgun/.env.example +11 -0
  109. package/connectors/connect-mailgun/CLAUDE.md +272 -0
  110. package/connectors/connect-mailgun/README.md +193 -0
  111. package/connectors/connect-mailgun/package.json +51 -0
  112. package/connectors/connect-mailgun/scripts/release.ts +179 -0
  113. package/connectors/connect-mailgun/src/api/client.ts +213 -0
  114. package/connectors/connect-mailgun/src/api/example.ts +48 -0
  115. package/connectors/connect-mailgun/src/api/index.ts +51 -0
  116. package/connectors/connect-mailgun/src/cli/index.ts +254 -0
  117. package/connectors/connect-mailgun/src/index.ts +103 -0
  118. package/connectors/connect-mailgun/src/types/index.ts +237 -0
  119. package/connectors/connect-mailgun/src/utils/auth.ts +274 -0
  120. package/connectors/connect-mailgun/src/utils/bulk.ts +212 -0
  121. package/connectors/connect-mailgun/src/utils/config.ts +326 -0
  122. package/connectors/connect-mailgun/src/utils/output.ts +175 -0
  123. package/connectors/connect-mailgun/src/utils/settings.ts +114 -0
  124. package/connectors/connect-mailgun/src/utils/storage.ts +198 -0
  125. package/connectors/connect-mailgun/tsconfig.json +16 -0
  126. package/connectors/connect-messagebird/.env.example +11 -0
  127. package/connectors/connect-messagebird/CLAUDE.md +272 -0
  128. package/connectors/connect-messagebird/README.md +193 -0
  129. package/connectors/connect-messagebird/package.json +51 -0
  130. package/connectors/connect-messagebird/scripts/release.ts +179 -0
  131. package/connectors/connect-messagebird/src/api/client.ts +213 -0
  132. package/connectors/connect-messagebird/src/api/example.ts +48 -0
  133. package/connectors/connect-messagebird/src/api/index.ts +51 -0
  134. package/connectors/connect-messagebird/src/cli/index.ts +254 -0
  135. package/connectors/connect-messagebird/src/index.ts +103 -0
  136. package/connectors/connect-messagebird/src/types/index.ts +237 -0
  137. package/connectors/connect-messagebird/src/utils/auth.ts +274 -0
  138. package/connectors/connect-messagebird/src/utils/bulk.ts +212 -0
  139. package/connectors/connect-messagebird/src/utils/config.ts +326 -0
  140. package/connectors/connect-messagebird/src/utils/output.ts +175 -0
  141. package/connectors/connect-messagebird/src/utils/settings.ts +114 -0
  142. package/connectors/connect-messagebird/src/utils/storage.ts +198 -0
  143. package/connectors/connect-messagebird/tsconfig.json +16 -0
  144. package/connectors/connect-miro/.env.example +11 -0
  145. package/connectors/connect-miro/CLAUDE.md +272 -0
  146. package/connectors/connect-miro/README.md +193 -0
  147. package/connectors/connect-miro/package.json +51 -0
  148. package/connectors/connect-miro/scripts/release.ts +179 -0
  149. package/connectors/connect-miro/src/api/client.ts +213 -0
  150. package/connectors/connect-miro/src/api/example.ts +48 -0
  151. package/connectors/connect-miro/src/api/index.ts +51 -0
  152. package/connectors/connect-miro/src/cli/index.ts +254 -0
  153. package/connectors/connect-miro/src/index.ts +103 -0
  154. package/connectors/connect-miro/src/types/index.ts +237 -0
  155. package/connectors/connect-miro/src/utils/auth.ts +274 -0
  156. package/connectors/connect-miro/src/utils/bulk.ts +212 -0
  157. package/connectors/connect-miro/src/utils/config.ts +326 -0
  158. package/connectors/connect-miro/src/utils/output.ts +175 -0
  159. package/connectors/connect-miro/src/utils/settings.ts +114 -0
  160. package/connectors/connect-miro/src/utils/storage.ts +198 -0
  161. package/connectors/connect-miro/tsconfig.json +16 -0
  162. package/connectors/connect-monday/.env.example +11 -0
  163. package/connectors/connect-monday/CLAUDE.md +128 -0
  164. package/connectors/connect-monday/README.md +193 -0
  165. package/connectors/connect-monday/package.json +52 -0
  166. package/connectors/connect-monday/src/api/client.ts +59 -0
  167. package/connectors/connect-monday/src/api/index.ts +539 -0
  168. package/connectors/connect-monday/src/cli/index.ts +479 -0
  169. package/connectors/connect-monday/src/index.ts +19 -0
  170. package/connectors/connect-monday/src/types/index.ts +274 -0
  171. package/connectors/connect-monday/src/utils/config.ts +197 -0
  172. package/connectors/connect-monday/src/utils/output.ts +119 -0
  173. package/connectors/connect-monday/tsconfig.json +16 -0
  174. package/connectors/connect-pipedrive/.env.example +11 -0
  175. package/connectors/connect-pipedrive/CLAUDE.md +128 -0
  176. package/connectors/connect-pipedrive/README.md +193 -0
  177. package/connectors/connect-pipedrive/package.json +52 -0
  178. package/connectors/connect-pipedrive/src/api/client.ts +121 -0
  179. package/connectors/connect-pipedrive/src/api/index.ts +306 -0
  180. package/connectors/connect-pipedrive/src/cli/index.ts +824 -0
  181. package/connectors/connect-pipedrive/src/index.ts +19 -0
  182. package/connectors/connect-pipedrive/src/types/index.ts +335 -0
  183. package/connectors/connect-pipedrive/src/utils/config.ts +171 -0
  184. package/connectors/connect-pipedrive/src/utils/output.ts +119 -0
  185. package/connectors/connect-pipedrive/tsconfig.json +16 -0
  186. package/connectors/connect-pusher/.env.example +11 -0
  187. package/connectors/connect-pusher/CLAUDE.md +272 -0
  188. package/connectors/connect-pusher/README.md +193 -0
  189. package/connectors/connect-pusher/package.json +51 -0
  190. package/connectors/connect-pusher/scripts/release.ts +179 -0
  191. package/connectors/connect-pusher/src/api/client.ts +213 -0
  192. package/connectors/connect-pusher/src/api/example.ts +48 -0
  193. package/connectors/connect-pusher/src/api/index.ts +51 -0
  194. package/connectors/connect-pusher/src/cli/index.ts +254 -0
  195. package/connectors/connect-pusher/src/index.ts +103 -0
  196. package/connectors/connect-pusher/src/types/index.ts +237 -0
  197. package/connectors/connect-pusher/src/utils/auth.ts +274 -0
  198. package/connectors/connect-pusher/src/utils/bulk.ts +212 -0
  199. package/connectors/connect-pusher/src/utils/config.ts +326 -0
  200. package/connectors/connect-pusher/src/utils/output.ts +175 -0
  201. package/connectors/connect-pusher/src/utils/settings.ts +114 -0
  202. package/connectors/connect-pusher/src/utils/storage.ts +198 -0
  203. package/connectors/connect-pusher/tsconfig.json +16 -0
  204. package/connectors/connect-vonage/.env.example +11 -0
  205. package/connectors/connect-vonage/CLAUDE.md +272 -0
  206. package/connectors/connect-vonage/README.md +193 -0
  207. package/connectors/connect-vonage/package.json +51 -0
  208. package/connectors/connect-vonage/scripts/release.ts +179 -0
  209. package/connectors/connect-vonage/src/api/client.ts +213 -0
  210. package/connectors/connect-vonage/src/api/example.ts +48 -0
  211. package/connectors/connect-vonage/src/api/index.ts +51 -0
  212. package/connectors/connect-vonage/src/cli/index.ts +254 -0
  213. package/connectors/connect-vonage/src/index.ts +103 -0
  214. package/connectors/connect-vonage/src/types/index.ts +237 -0
  215. package/connectors/connect-vonage/src/utils/auth.ts +274 -0
  216. package/connectors/connect-vonage/src/utils/bulk.ts +212 -0
  217. package/connectors/connect-vonage/src/utils/config.ts +326 -0
  218. package/connectors/connect-vonage/src/utils/output.ts +175 -0
  219. package/connectors/connect-vonage/src/utils/settings.ts +114 -0
  220. package/connectors/connect-vonage/src/utils/storage.ts +198 -0
  221. package/connectors/connect-vonage/tsconfig.json +16 -0
  222. package/dist/index.js +91 -0
  223. package/package.json +1 -1
@@ -0,0 +1,627 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { Dropbox } from '../api';
5
+ import {
6
+ getAccessToken,
7
+ setAccessToken,
8
+ clearConfig,
9
+ getCurrentProfile,
10
+ setCurrentProfile,
11
+ listProfiles,
12
+ createProfile,
13
+ deleteProfile,
14
+ setProfileOverride,
15
+ getConfigDir,
16
+ } from '../utils/config';
17
+ import type { Metadata, FileMetadata, FolderMetadata } from '../types';
18
+
19
+ const program = new Command();
20
+
21
+ // Helper to get authenticated client
22
+ function getClient(): Dropbox {
23
+ const accessToken = getAccessToken();
24
+ if (!accessToken) {
25
+ console.error(chalk.red('Error: Not authenticated. Run "connect-dropbox auth set <token>" first or set DROPBOX_ACCESS_TOKEN.'));
26
+ process.exit(1);
27
+ }
28
+ return new Dropbox({ accessToken });
29
+ }
30
+
31
+ // Format file size
32
+ function formatSize(bytes: number): string {
33
+ if (bytes < 1024) return `${bytes} B`;
34
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
35
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
36
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
37
+ }
38
+
39
+ // Format metadata for display
40
+ function formatMetadata(item: Metadata): string {
41
+ if (item['.tag'] === 'file') {
42
+ const file = item as FileMetadata;
43
+ return `${chalk.white(file.name)} ${chalk.gray(`(${formatSize(file.size)})`)}`;
44
+ } else if (item['.tag'] === 'folder') {
45
+ return chalk.blue(item.name + '/');
46
+ } else {
47
+ return chalk.gray(`[deleted] ${item.name}`);
48
+ }
49
+ }
50
+
51
+ program
52
+ .name('connect-dropbox')
53
+ .description('Dropbox connector - Manage files, folders, sharing, and team administration')
54
+ .version('0.0.1')
55
+ .option('--profile <name>', 'Use a specific profile')
56
+ .hook('preAction', (thisCommand) => {
57
+ const opts = thisCommand.opts();
58
+ if (opts.profile) {
59
+ setProfileOverride(opts.profile);
60
+ }
61
+ });
62
+
63
+ // ============================================
64
+ // Auth Commands
65
+ // ============================================
66
+
67
+ const authCmd = program.command('auth').description('Authentication management');
68
+
69
+ authCmd
70
+ .command('set')
71
+ .description('Set access token for the current profile')
72
+ .argument('<token>', 'Dropbox access token')
73
+ .action((token: string) => {
74
+ setAccessToken(token);
75
+ console.log(chalk.green(`Access token saved to profile "${getCurrentProfile()}"`));
76
+ });
77
+
78
+ authCmd
79
+ .command('status')
80
+ .description('Check authentication status')
81
+ .action(async () => {
82
+ const token = getAccessToken();
83
+ if (!token) {
84
+ console.log(chalk.yellow('Not authenticated'));
85
+ console.log(chalk.gray('Run "connect-dropbox auth set <token>" to authenticate'));
86
+ return;
87
+ }
88
+
89
+ try {
90
+ const client = getClient();
91
+ const account = await client.getCurrentAccount();
92
+ console.log(chalk.green('Authenticated'));
93
+ console.log(` Profile: ${chalk.cyan(getCurrentProfile())}`);
94
+ console.log(` User: ${chalk.white(account.name.display_name)}`);
95
+ console.log(` Email: ${chalk.white(account.email)}`);
96
+ console.log(` Account Type: ${chalk.white(account.account_type['.tag'])}`);
97
+ } catch (error) {
98
+ console.log(chalk.red('Authentication failed'));
99
+ console.error(chalk.gray(error instanceof Error ? error.message : String(error)));
100
+ }
101
+ });
102
+
103
+ authCmd
104
+ .command('clear')
105
+ .description('Clear stored credentials')
106
+ .action(() => {
107
+ clearConfig();
108
+ console.log(chalk.green('Credentials cleared'));
109
+ });
110
+
111
+ // ============================================
112
+ // Profile Commands
113
+ // ============================================
114
+
115
+ const profileCmd = program.command('profile').description('Profile management');
116
+
117
+ profileCmd
118
+ .command('list')
119
+ .description('List all profiles')
120
+ .action(() => {
121
+ const profiles = listProfiles();
122
+ const current = getCurrentProfile();
123
+
124
+ if (profiles.length === 0) {
125
+ console.log(chalk.gray('No profiles configured'));
126
+ return;
127
+ }
128
+
129
+ console.log(chalk.bold('Profiles:'));
130
+ for (const profile of profiles) {
131
+ const marker = profile === current ? chalk.green(' (active)') : '';
132
+ console.log(` ${profile}${marker}`);
133
+ }
134
+ });
135
+
136
+ profileCmd
137
+ .command('use')
138
+ .description('Switch to a profile')
139
+ .argument('<name>', 'Profile name')
140
+ .action((name: string) => {
141
+ try {
142
+ setCurrentProfile(name);
143
+ console.log(chalk.green(`Switched to profile "${name}"`));
144
+ } catch (error) {
145
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ profileCmd
151
+ .command('create')
152
+ .description('Create a new profile')
153
+ .argument('<name>', 'Profile name')
154
+ .action((name: string) => {
155
+ try {
156
+ if (createProfile(name)) {
157
+ console.log(chalk.green(`Profile "${name}" created`));
158
+ } else {
159
+ console.log(chalk.yellow(`Profile "${name}" already exists`));
160
+ }
161
+ } catch (error) {
162
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
163
+ process.exit(1);
164
+ }
165
+ });
166
+
167
+ profileCmd
168
+ .command('delete')
169
+ .description('Delete a profile')
170
+ .argument('<name>', 'Profile name')
171
+ .action((name: string) => {
172
+ if (deleteProfile(name)) {
173
+ console.log(chalk.green(`Profile "${name}" deleted`));
174
+ } else {
175
+ console.log(chalk.yellow(`Cannot delete profile "${name}" (doesn't exist or is default)`));
176
+ }
177
+ });
178
+
179
+ profileCmd
180
+ .command('show')
181
+ .description('Show current profile')
182
+ .action(() => {
183
+ console.log(`Current profile: ${chalk.cyan(getCurrentProfile())}`);
184
+ console.log(`Config directory: ${chalk.gray(getConfigDir())}`);
185
+ });
186
+
187
+ // ============================================
188
+ // Files Commands
189
+ // ============================================
190
+
191
+ const filesCmd = program.command('files').description('File operations');
192
+
193
+ filesCmd
194
+ .command('list')
195
+ .description('List folder contents')
196
+ .argument('[path]', 'Folder path', '')
197
+ .option('-r, --recursive', 'List recursively')
198
+ .option('-l, --long', 'Show detailed information')
199
+ .option('--limit <n>', 'Limit number of results', '100')
200
+ .action(async (path: string, options) => {
201
+ try {
202
+ const client = getClient();
203
+ const result = await client.listFolder(path, {
204
+ recursive: options.recursive,
205
+ limit: parseInt(options.limit),
206
+ });
207
+
208
+ if (result.entries.length === 0) {
209
+ console.log(chalk.gray('Empty folder'));
210
+ return;
211
+ }
212
+
213
+ for (const entry of result.entries) {
214
+ if (options.long) {
215
+ const type = entry['.tag'] === 'folder' ? 'd' : '-';
216
+ const size = entry['.tag'] === 'file' ? formatSize((entry as FileMetadata).size) : '-';
217
+ const modified = entry['.tag'] === 'file' ? (entry as FileMetadata).server_modified : '';
218
+ console.log(`${type} ${size.padStart(10)} ${modified.padStart(20)} ${formatMetadata(entry)}`);
219
+ } else {
220
+ console.log(formatMetadata(entry));
221
+ }
222
+ }
223
+
224
+ if (result.has_more) {
225
+ console.log(chalk.gray(`\n... and more (use --limit to see more)`));
226
+ }
227
+ } catch (error) {
228
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
229
+ process.exit(1);
230
+ }
231
+ });
232
+
233
+ filesCmd
234
+ .command('info')
235
+ .description('Get file or folder metadata')
236
+ .argument('<path>', 'File or folder path')
237
+ .action(async (path: string) => {
238
+ try {
239
+ const client = getClient();
240
+ const metadata = await client.getMetadata(path, {
241
+ include_media_info: true,
242
+ include_has_explicit_shared_members: true,
243
+ });
244
+
245
+ console.log(chalk.bold('Metadata:'));
246
+ console.log(` Type: ${chalk.cyan(metadata['.tag'])}`);
247
+ console.log(` Name: ${metadata.name}`);
248
+ console.log(` Path: ${metadata.path_display || metadata.path_lower}`);
249
+ console.log(` ID: ${chalk.gray(metadata.id)}`);
250
+
251
+ if (metadata['.tag'] === 'file') {
252
+ const file = metadata as FileMetadata;
253
+ console.log(` Size: ${formatSize(file.size)}`);
254
+ console.log(` Modified: ${file.server_modified}`);
255
+ console.log(` Rev: ${chalk.gray(file.rev)}`);
256
+ if (file.content_hash) {
257
+ console.log(` Content Hash: ${chalk.gray(file.content_hash)}`);
258
+ }
259
+ }
260
+ } catch (error) {
261
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
262
+ process.exit(1);
263
+ }
264
+ });
265
+
266
+ filesCmd
267
+ .command('search')
268
+ .description('Search for files and folders')
269
+ .argument('<query>', 'Search query')
270
+ .option('-p, --path <path>', 'Search within path')
271
+ .option('--filename-only', 'Search only filenames')
272
+ .option('--limit <n>', 'Maximum results', '20')
273
+ .action(async (query: string, options) => {
274
+ try {
275
+ const client = getClient();
276
+ const result = await client.search(query, {
277
+ path: options.path,
278
+ filename_only: options.filenameOnly,
279
+ max_results: parseInt(options.limit),
280
+ });
281
+
282
+ if (result.matches.length === 0) {
283
+ console.log(chalk.gray('No matches found'));
284
+ return;
285
+ }
286
+
287
+ console.log(chalk.bold(`Found ${result.matches.length} matches:`));
288
+ for (const match of result.matches) {
289
+ const metadata = match.metadata.metadata;
290
+ console.log(` ${formatMetadata(metadata)}`);
291
+ console.log(chalk.gray(` ${metadata.path_display || metadata.path_lower}`));
292
+ }
293
+
294
+ if (result.has_more) {
295
+ console.log(chalk.gray(`\n... and more results`));
296
+ }
297
+ } catch (error) {
298
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
299
+ process.exit(1);
300
+ }
301
+ });
302
+
303
+ filesCmd
304
+ .command('mkdir')
305
+ .description('Create a folder')
306
+ .argument('<path>', 'Folder path')
307
+ .option('--autorename', 'Autorename if exists')
308
+ .action(async (path: string, options) => {
309
+ try {
310
+ const client = getClient();
311
+ const folder = await client.createFolder(path, options.autorename);
312
+ console.log(chalk.green(`Created folder: ${folder.path_display || folder.name}`));
313
+ } catch (error) {
314
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
315
+ process.exit(1);
316
+ }
317
+ });
318
+
319
+ filesCmd
320
+ .command('delete')
321
+ .description('Delete a file or folder')
322
+ .argument('<path>', 'File or folder path')
323
+ .option('--permanent', 'Permanently delete (cannot be recovered)')
324
+ .action(async (path: string, options) => {
325
+ try {
326
+ const client = getClient();
327
+ if (options.permanent) {
328
+ await client.permanentlyDelete(path);
329
+ console.log(chalk.green(`Permanently deleted: ${path}`));
330
+ } else {
331
+ const result = await client.delete(path);
332
+ console.log(chalk.green(`Deleted: ${result.metadata.path_display || result.metadata.name}`));
333
+ }
334
+ } catch (error) {
335
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
336
+ process.exit(1);
337
+ }
338
+ });
339
+
340
+ filesCmd
341
+ .command('copy')
342
+ .description('Copy a file or folder')
343
+ .argument('<from>', 'Source path')
344
+ .argument('<to>', 'Destination path')
345
+ .option('--autorename', 'Autorename if destination exists')
346
+ .action(async (from: string, to: string, options) => {
347
+ try {
348
+ const client = getClient();
349
+ const result = await client.copy(from, to, { autorename: options.autorename });
350
+ console.log(chalk.green(`Copied to: ${result.metadata.path_display || result.metadata.name}`));
351
+ } catch (error) {
352
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
353
+ process.exit(1);
354
+ }
355
+ });
356
+
357
+ filesCmd
358
+ .command('move')
359
+ .description('Move a file or folder')
360
+ .argument('<from>', 'Source path')
361
+ .argument('<to>', 'Destination path')
362
+ .option('--autorename', 'Autorename if destination exists')
363
+ .action(async (from: string, to: string, options) => {
364
+ try {
365
+ const client = getClient();
366
+ const result = await client.move(from, to, { autorename: options.autorename });
367
+ console.log(chalk.green(`Moved to: ${result.metadata.path_display || result.metadata.name}`));
368
+ } catch (error) {
369
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
370
+ process.exit(1);
371
+ }
372
+ });
373
+
374
+ filesCmd
375
+ .command('upload')
376
+ .description('Upload a file')
377
+ .argument('<local>', 'Local file path')
378
+ .argument('<remote>', 'Remote Dropbox path')
379
+ .option('--overwrite', 'Overwrite if exists')
380
+ .option('--autorename', 'Autorename if exists')
381
+ .action(async (local: string, remote: string, options) => {
382
+ try {
383
+ const content = await Bun.file(local).arrayBuffer();
384
+ const client = getClient();
385
+
386
+ let mode: 'add' | 'overwrite' = 'add';
387
+ if (options.overwrite) mode = 'overwrite';
388
+
389
+ const result = await client.upload(remote, new Uint8Array(content), {
390
+ mode,
391
+ autorename: options.autorename,
392
+ });
393
+ console.log(chalk.green(`Uploaded: ${result.path_display || result.name}`));
394
+ console.log(` Size: ${formatSize(result.size)}`);
395
+ } catch (error) {
396
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
397
+ process.exit(1);
398
+ }
399
+ });
400
+
401
+ filesCmd
402
+ .command('download')
403
+ .description('Download a file')
404
+ .argument('<remote>', 'Remote Dropbox path')
405
+ .argument('[local]', 'Local file path (defaults to filename)')
406
+ .action(async (remote: string, local?: string) => {
407
+ try {
408
+ const client = getClient();
409
+ const result = await client.download(remote);
410
+
411
+ const localPath = local || result.metadata.name;
412
+ await Bun.write(localPath, result.content);
413
+
414
+ console.log(chalk.green(`Downloaded: ${localPath}`));
415
+ console.log(` Size: ${formatSize(result.metadata.size)}`);
416
+ } catch (error) {
417
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
418
+ process.exit(1);
419
+ }
420
+ });
421
+
422
+ filesCmd
423
+ .command('link')
424
+ .description('Get a temporary download link')
425
+ .argument('<path>', 'File path')
426
+ .action(async (path: string) => {
427
+ try {
428
+ const client = getClient();
429
+ const result = await client.getTemporaryLink(path);
430
+ console.log(chalk.bold('Temporary Link:'));
431
+ console.log(result.link);
432
+ console.log(chalk.gray('\nThis link expires after 4 hours.'));
433
+ } catch (error) {
434
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
435
+ process.exit(1);
436
+ }
437
+ });
438
+
439
+ // ============================================
440
+ // Sharing Commands
441
+ // ============================================
442
+
443
+ const shareCmd = program.command('share').description('Sharing operations');
444
+
445
+ shareCmd
446
+ .command('create')
447
+ .description('Create a shared link')
448
+ .argument('<path>', 'File or folder path')
449
+ .action(async (path: string) => {
450
+ try {
451
+ const client = getClient();
452
+ const link = await client.createSharedLink(path);
453
+ console.log(chalk.green('Shared link created:'));
454
+ console.log(link.url);
455
+ } catch (error) {
456
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
457
+ process.exit(1);
458
+ }
459
+ });
460
+
461
+ shareCmd
462
+ .command('list')
463
+ .description('List shared links')
464
+ .option('-p, --path <path>', 'List links for specific path')
465
+ .action(async (options) => {
466
+ try {
467
+ const client = getClient();
468
+ const result = await client.listSharedLinks({ path: options.path });
469
+
470
+ if (result.links.length === 0) {
471
+ console.log(chalk.gray('No shared links'));
472
+ return;
473
+ }
474
+
475
+ console.log(chalk.bold(`Shared Links (${result.links.length}):`));
476
+ for (const link of result.links) {
477
+ console.log(`\n ${chalk.white(link.name)}`);
478
+ console.log(` URL: ${chalk.cyan(link.url)}`);
479
+ console.log(` Path: ${chalk.gray(link.path_lower || '-')}`);
480
+ console.log(` Visibility: ${link.link_permissions.resolved_visibility['.tag']}`);
481
+ if (link.expires) {
482
+ console.log(` Expires: ${link.expires}`);
483
+ }
484
+ }
485
+ } catch (error) {
486
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
487
+ process.exit(1);
488
+ }
489
+ });
490
+
491
+ shareCmd
492
+ .command('revoke')
493
+ .description('Revoke a shared link')
494
+ .argument('<url>', 'Shared link URL')
495
+ .action(async (url: string) => {
496
+ try {
497
+ const client = getClient();
498
+ await client.revokeSharedLink(url);
499
+ console.log(chalk.green('Shared link revoked'));
500
+ } catch (error) {
501
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
502
+ process.exit(1);
503
+ }
504
+ });
505
+
506
+ shareCmd
507
+ .command('folders')
508
+ .description('List shared folders')
509
+ .action(async () => {
510
+ try {
511
+ const client = getClient();
512
+ const result = await client.listSharedFolders();
513
+
514
+ if (result.entries.length === 0) {
515
+ console.log(chalk.gray('No shared folders'));
516
+ return;
517
+ }
518
+
519
+ console.log(chalk.bold(`Shared Folders (${result.entries.length}):`));
520
+ for (const folder of result.entries) {
521
+ console.log(`\n ${chalk.blue(folder.name + '/')}`);
522
+ console.log(` ID: ${chalk.gray(folder.shared_folder_id)}`);
523
+ console.log(` Access: ${folder.access_type['.tag']}`);
524
+ console.log(` Team Folder: ${folder.is_team_folder ? 'Yes' : 'No'}`);
525
+ if (folder.path_lower) {
526
+ console.log(` Path: ${folder.path_lower}`);
527
+ }
528
+ }
529
+ } catch (error) {
530
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
531
+ process.exit(1);
532
+ }
533
+ });
534
+
535
+ shareCmd
536
+ .command('folder-members')
537
+ .description('List members of a shared folder')
538
+ .argument('<folder-id>', 'Shared folder ID')
539
+ .action(async (folderId: string) => {
540
+ try {
541
+ const client = getClient();
542
+ const result = await client.listSharedFolderMembers(folderId);
543
+
544
+ console.log(chalk.bold('Members:'));
545
+ for (const member of result.users) {
546
+ console.log(`\n ${chalk.white(member.user.display_name)}`);
547
+ console.log(` Email: ${member.user.email}`);
548
+ console.log(` Access: ${member.access_type['.tag']}`);
549
+ console.log(` Same Team: ${member.user.same_team ? 'Yes' : 'No'}`);
550
+ }
551
+
552
+ if (result.groups && result.groups.length > 0) {
553
+ console.log(chalk.bold('\nGroups:'));
554
+ for (const group of result.groups) {
555
+ console.log(`\n ${chalk.white(group.group.group_name)}`);
556
+ console.log(` Access: ${group.access_type['.tag']}`);
557
+ }
558
+ }
559
+ } catch (error) {
560
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
561
+ process.exit(1);
562
+ }
563
+ });
564
+
565
+ // ============================================
566
+ // Account Commands
567
+ // ============================================
568
+
569
+ const accountCmd = program.command('account').description('Account information');
570
+
571
+ accountCmd
572
+ .command('info')
573
+ .description('Get account information')
574
+ .action(async () => {
575
+ try {
576
+ const client = getClient();
577
+ const account = await client.getCurrentAccount();
578
+
579
+ console.log(chalk.bold('Account Information:'));
580
+ console.log(` Name: ${account.name.display_name}`);
581
+ console.log(` Email: ${account.email} ${account.email_verified ? chalk.green('(verified)') : chalk.yellow('(unverified)')}`);
582
+ console.log(` Account Type: ${account.account_type['.tag']}`);
583
+ console.log(` Locale: ${account.locale}`);
584
+ console.log(` Referral Link: ${account.referral_link}`);
585
+
586
+ if (account.team) {
587
+ console.log(`\n Team: ${account.team.name}`);
588
+ console.log(` Team ID: ${chalk.gray(account.team.id)}`);
589
+ }
590
+
591
+ console.log(`\n Account ID: ${chalk.gray(account.account_id)}`);
592
+ console.log(` Root Namespace ID: ${chalk.gray(account.root_info.root_namespace_id)}`);
593
+ } catch (error) {
594
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
595
+ process.exit(1);
596
+ }
597
+ });
598
+
599
+ accountCmd
600
+ .command('usage')
601
+ .description('Get space usage')
602
+ .action(async () => {
603
+ try {
604
+ const client = getClient();
605
+ const usage = await client.getSpaceUsage();
606
+
607
+ const usedPercent = (usage.used / usage.allocation.allocated * 100).toFixed(1);
608
+
609
+ console.log(chalk.bold('Space Usage:'));
610
+ console.log(` Used: ${formatSize(usage.used)}`);
611
+ console.log(` Allocated: ${formatSize(usage.allocation.allocated)}`);
612
+ console.log(` Usage: ${usedPercent}%`);
613
+ console.log(` Type: ${usage.allocation['.tag']}`);
614
+
615
+ // Progress bar
616
+ const barWidth = 30;
617
+ const filled = Math.round(parseFloat(usedPercent) / 100 * barWidth);
618
+ const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(barWidth - filled));
619
+ console.log(`\n [${bar}] ${usedPercent}%`);
620
+ } catch (error) {
621
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
622
+ process.exit(1);
623
+ }
624
+ });
625
+
626
+ // Parse and execute
627
+ program.parse();
@@ -0,0 +1,20 @@
1
+ // Dropbox Connector
2
+ // A TypeScript wrapper for the Dropbox API
3
+
4
+ export { Dropbox } from './api';
5
+ export { DropboxClient } from './api/client';
6
+ export * from './types';
7
+
8
+ // Export config utilities
9
+ export {
10
+ getAccessToken,
11
+ setAccessToken,
12
+ getCurrentProfile,
13
+ setCurrentProfile,
14
+ listProfiles,
15
+ createProfile,
16
+ deleteProfile,
17
+ loadProfile,
18
+ saveProfile,
19
+ clearConfig,
20
+ } from './utils/config';